본문 바로가기
CS/데이터베이스

[백엔드] DB 추가 정보

by Hoozy 2023. 4. 3.

이전 게시글 NoSQL(비관계형) DB

https://hoozy.tistory.com/entry/%EB%B0%B1%EC%97%94%EB%93%9C-NoSQL%EB%B9%84%EA%B4%80%EA%B3%84%ED%98%95-DB

 

[백엔드] NoSQL(비관계형) DB

이전 게시글 관계형 DB https://hoozy.tistory.com/entry/%EB%B0%B1%EC%97%94%EB%93%9C-%EA%B4%80%EA%B3%84%ED%98%95-DB 카테고리 : 데이터베이스 비관계형 데이터베이스란 관계형 데이터 모델(RDBMS)을 지양하며 대량의 분산

hoozy.tistory.com

카테고리 : 데이터베이스

ORM
  • 객체-관계 매핑의 약자로, 객체라는 개념을 구현한 클래스인 엔티티와 RDBMS에서 쓰이는 테이블을 자동으로 매핑하는 것을 말한다.
  • 원래 클래스와 테이블은 서로 기존부터 호환가능성을 두고 만들어진 것이 아니여서 불일치하는데, 이를 ORM을 통해 객체 간의 관계를 바탕으로 SQL문을 자동으로 생성하여 불일치를 해결한다. -> ORM을 이용하면 따로 SQL문을 짤 필요없이 간접적으로 데이터베이스를 조작할 수 있다.
  • 장점
    1. 완벽한 객체지향적인 코드
      • SQL문이 아닌 클래스의 메소드를 통해 데이터베이스를 조작할 수 있어서 개발자가 객체 모델만을 이용해서 프로그래밍 하는데 집중할 수 있다. 각종 객체에 대한 코드를 별도로 작성하여 코드의 가독성을 높일 수 있다. 객체지향적인 접근과 SQL의 절차적/순차적 접근이 혼재되어있던 기존 방식과 달리 오직 객체지향적 접근만 고려하면 되어서 생산성이 증가한다.
    2. 재사용, 유지보수, 리팩토링 용이성
      • ORM은 기존 객체와 독립적으로 작성되어 있고, 객체로 작성되었기 때문에 재활용할 수 있다. 또한, 매핑하는 정보가 명확하기 때문에 ERD를 보는 의존도를 낮출 수 있다.
    3. DBMS 종속성 하락
      • 객체 간의 관계를 바탕으로 SQL문을 자동으로 생성하고, 객체의 자료형 타입까지 사용할 수 있기 때문에 RDBMS의 데이터 구조와 객체지향 모델 사이의 간격을 좁힐 수 있다. 객체에만 집중할 수 있기 때문에 DBMS를 교체하는 큰 작업에도 리스크가 적고 드는 시간도 줄어든다. 예들 들어 자바에서 가공할 경우 equals, hashCode의 오버라이드 같은 자바의 기능을 이용할 수 있고, 간결하고 빠르게 가공할 수 있다.
  • 단점
    1. ORM이 모든 것을 해결할 수 없다.
      • ORM을 사용하는 것은 매우 편리하지만 그만큼 신중하게 설계해야한다. 프로젝트의 복잡성이 커질 수록 난이도도 올라가고 부족한 설계로 잘못 구현되었을 경우 속도 저하 및 일관성을 무너뜨리는 문제점이 생길 수 있다. 또한 일부 자주 사용되는 대형 SQL문은 속도를 위해 별도의 튜닝이 필요하기 때문에 결국 SQL문을 써야할 수도 있다.
    2. 객체-관계 간의 불일치
      • 다음과 같은 특성에서 객체-관계 간의 불일치가 생긴다.
    3. 세분성(Granularity)
      • 경우에 따라서 데이터베이스에 있는 테이블 수보다 더 많은 클래스를 가진 모델이 생길 수 있다.
    4. 상속성(Inheritance)
      • RDBMS는 객체지향 프로그래밍 언어의 특징인 상속 개념이 없다.
    5. 일치(Identity)
      • RDBMS는 기본키(primary key)를 이용하여 동일성을 정의한다. 그러나 자바는 객체 식별(a==b)과 객체 동일성(a.equals(b))을 모두 정의한다.
    6. 연관성(Associations)
      • 객체지향 언어는 방향성이 있는 객체의 참조(reference)를 사용하여 연관성을 나타내지만 RDBMS는 방향성이 없는 외래키를 이용해서 나타낸다.
    7. 탐색(Navigation)
      • 자바와 RDBMS에서 객체를 접근하는 방법이 근본적으로 다르다. 자바는 그래프형태로 하나의 연결에서 다른 연결로 이동하며 탐색한다. 그러나 RDBMS에서는 일반적으로 SQL문을 최소화하고 JOIN을 통해 여러 엔티티를 로드하고 원하는 대상 엔티티를 선택하는 방식으로 탐색한다.
  • 종류
    1. JPA / HIBERNATE
      • JPA는 자바의 ORM 기술 표준으로 인터페이스의 모음이다. 이러한 JPA 표준 ㅁ여세를 구현한 구현체가 HIBERNATE이다.
    2. Sequelize
      • Sequelize는 Postgres, MySQL, MariaDB, SQLite 등을 지원하는 Promise에 기반한 비동기로 동작하는 Node.js ORM이다.
    3. Django ORM
      • python기반 프레임워크인 Django에서 자체적으로 지원하는 ORM이다.
트랜잭션
  • 여러 개의 작업을 하나로 묶은 실행 유닛.
  • 즉, 데이터베이스의 상태를 변환시키는 기능을 수행하기 위한 하나 이상의 쿼리를 모아놓은 하나의 작업 단위이다.
  • 각 트랜잭션은 하나의 특정 작업으로 시자해서 묶여 있는 모든 작업들을 다 완료해야 정상적으로 종료한다.
  • 만약 하나의 트랜잭션에 속해있는 여러 작업 중에서 단 하나의 작업이라도 실패하면, 이 트랜잭션에 속한 모든 작업을 실패한 것으로 판단한다.
  • 작업이 하나라도 실패를 하게 되면 트랜잭션도 실패이고, 모든 작업이 성공적이면 트랜잭션 또한 성공이다.
  • 성공 또는 실패 라는 두 개의 결과만 존재하는 트랜잭션은, 미완료된 작업없이 모든 작업을 성공해야 한다.
  • ACID 라는 특성을 가지고 있다
    • ACID
      • 데이터베이스 내에서 일어나는 하나의 트랜잭션의 안전성을 보장하기 위해 필요한 성질이며 주식거래, 금융업에서 중점적으로 사용된다. -> 관계형 데이터베이스를 사용해서 데이터베이스와 상호작용하는 방식을 정확하게 규정할 수 있기 때문에, 데이터베이스에서 데이터를 처리할 때 발생할 수 있는 예외적인 상황을 줄이고, 데이터베이스의 무결성을 보호할 수 있다.
      1. Atomicity : 원자성
        • 한 트랜잭션의 연산들이 모두 성공하거나, 반대로 전부 실패되는 성질. 작업이 모두 반영되거나 모두 반영되지 않음으로서 결과를 예측할 수 있어야 한다.
        • 예를 들어 A 계좌에서 출금 후 B 게좌로 입금을 해야하는데 출금은 되었는데, 입금이 되지 않으면 전체 작업(트랜잭션)이 취소(롤백)되어야 하는 것이 원자성이다.
        1. Consistency : 일관성
          • 트랜잭션 이전과 이후, 데이터베이스의 상태는 이전과 같이 유효해야 한다. 즉, 트랜잭션이 일어난 이후에 데이터베이스는 데이터베이스의 제약이나 규칙을 만족해야 한다.
          • 예를 들어 모든 고객은 반드시 이름을 가지고 있어야 한다 라는 데이터베이스의 제약이 있다고 가정하자.
          • 만약 트랜잭션이 이름 없는 새로운 고객을 추가하는 쿼리거나, 기존 고객의 이름을 삭제하는 쿼리라고 가정하면, 둘 다 이름을 가지고 있어야 하는 제약을 위반했기 때문에 일관성이 없는 것이 된다.
      2. Isolation : 격리성, 고립성
        • 모든 트랜잭션은 다른 트랜잭션으로부터 독립되어야 한다. 즉, 동시에 여러 트랜잭션이 수행될 때 연속으로 실행된 것과 동일한 결과를 나타내야 한다.
        • 예를 들어 잔액이 1만원인 A 계좌에서 6천원씩 B,C 계좌에 입금한다고 가정하면, B계좌에 6천원을 이체 후 C 계좌에 이체할 때에는 잔액 부족으로 취소되어야 한다.
      3. Durability : 지속성
        • 하나의 트랜잭션이 성공적으로 수행된다면, 해당 트랜잭션에 대한 로그가 남아야하는 성질이다. 즉, 만약 런타임 오류나 시스템 오류가 발생하더라도 해당 기록은 영구적이어야 한다는 뜻이다.
        • 예를 들어 게좌이체를 성공적으로 실행한 뒤에, 해당 은행 데이터베이스에 오류가 발생해서 종료되더라도 계좌이체 내역은 기록으로 남아야 한다.
        • 만약 로그로 기록하기 전에 시스템 오류 등에 의해 종료된다면, 해당 이체내역은 실패로 돌아가고 각 계좌들은 계좌이체 이전 상태들로 돌아가게 된다.
N + 1 문제
정규화
  • 테이블 간에 중복된 데이터를 허용하지 않는 것. 중복된 데이터를 허용하지 않음으로서 무결성을 유지할 수 있으며, DB의 저장 용량 역시 줄일 수 있다.
  • 이러한 테이블을 분해하는 정규화 단계가 정의되어 있다. 아래에서 정규화 단계를 자세하게 알아보도록 하겠습니다.

제 1 정규화 (1NF)

  • 테이블의 컬럼이 원자값(하나의 값)을 갖도록 테이블을 분해하는 것
  • 예를 들어 위 테이블에서 추신수와 박세리는 취미를 2개 가지고 있어서 제 1 정규화를 만족하지 못해서 제 1 정규화하여 분해할 수 있다. 분해하면 아래 이미지처럼 된다.

제 2 정규화 (2NF)

  • 제 1 정규화를 진행한 테이블에 대해 완전 함수 종속을 만족하도록 테이블을 분해하는 것
    • 완전 함수 종속 : 기본키의 부분집합이 결정자가 되어선 안되는 것
  • 위의 이미지의 테이블에서 기본키는 학생번호, 강좌이름으로 복합키이다. 여기서 기본키의 부분집합인 강좌이름으로 강의실도 알 수 있기 때문에 제 2 정규화에 위반된다.
  • 그래서 제 2 정규화로인해 강좌이름과 강의실 테이블을 분해해야 한다. -> 강좌이름이 기본키고 강의실이 컬럼
  • 분해한 테이블은 아래의 이미지로 나타난다.

제 3 정규화 (3NF)

  • 제 2 정규화를 진행한 테이블에 대해 이행적 종속을 없애도록 테이블을 분해하는 것
    • 이행적 종속 : A -> B, B -> C 일때, A -> C 가 성립되는 것
  • 위 이미지의 테이블에서 학생번호로 강좌이름을, 강좌이름으로 수강료를 알 수 있기 때문에, 제 3 정규화로 테이블을 2개로 분해해야 한다.
  • 분해하면 아래 이미지와 같다.

BCNF 정규화

  • 제 3 정규화를 진행한 테이블에 대해 모든 결정자가 후보키가 되도록 테이블을 분해하는 것.
  • 위 이미지의 테이블에서 기본키는 학생번호, 특강이름인데 여기서 특강이름을 결정 짓는 것은 교수 인데, 여기서 교수는 결정자이지만 후보키가 아니어서 BCNF 정규화로 분해해야 한다.
  • 분해하면 아래와 같다.

제 4 정규화 (4NF)

  • BCNF 정규화를 진행한 테이블에 대해 다치 종속이 없어야 한다.
    • 다치 종속 : 아래와 같은 조건들을 만족할 때
      1. A -> B 일 때 하나의 A 값에 여러 개의 B 값이 존재하면 다치 종속성을 가진다고하고 A->>B 로 표시한다.
      2. 최소 3개의 컬럼이 존재한다.
      3. R(A,B,C) 가 있을 때 A와 B 사이에 다치 종속성이 있을 때 B와 C가 독립적이다.
  • 위 이미지의 테이블에서 학생 번호 101번 학생은 자바와 C++을 수강하며 노래와 게임을 취미로 가진다.
  • 이럴 경우 학생 번호 101번으로 조회하면 아래와 같이 중복이 발생하게 된다.
  • 과목과 취미는 관계가 없는 독립적인 관계인데 학생번호 컬럼에 종속되어서 중복이 발생하는 문제가 발생한다.
  • 제 4 정규화로 분해하면 아래와 같이 된다.

제 5 정규화 (5NF)

  • 제 4 정규화를 진행한 테이블에서 조인 종속이 없어야 하고, 조인 연산을 했을 때 손실이 없어야 한다.
    • 조인 종속 : 만약 하나의 릴레이션을 여러 개의 릴레이션으로 무손실 분해했다가 다시 결합할 수 있으면 조인 종속이다.
      • 예를 들어 A라는 릴레이션을 B와 C로 분해했다가 다시 조인했을 때 그대로 A가 된다면, A는 조인 종속성이 있다.
  • 제 5 정규화는 현실의 데이터베이스에서 사용하지 않기 때문에 자세하게 알 필요는 없다고 느낍니다.
인덱스
  • 데이터베이스의 테이블에 대한 검색 속도를 향상시켜주는 자료구조이다.
  • 특정 컬럼에 인덱스를 생성하면, 해당 컬럼의 데이터를 정렬한 후 별도의 메모리 공간에 데이터의 물리적 주소와 함께 저장된다. -> 컬럼의 값과 물리적 주소를 (key, value)의 한 쌍으로 저장한다.
  • 데이터 = 책의 내용, 인덱스 = 책의 목차, 물리적 주소 = 책의 페이지 번호 라고 비유할 수 있다.
  • 장점
    • 테이블을 검색하는 속도와 성능이 향상된다. 이에 따라 시스템의 전반적인 부하를 줄일 수 있다.
    • 데이터들이 정렬된 형태를 갖는다. 기존에는 where 문으로 특정 조건의 데이터를 찾기 위해서 테이블의 전체를 조건과 비교해야 하는 '풀 데이터 스캔' 작업이 필요했는데, 인덱스를 이용하면 데이터들이 정렬되어 있기 때문에 조건에 맞는 데이터를 빠르게 찾을 수 있다.
    • order by 문이나 min/max 문 같은 경우도 이미 정렬이 되어 있기 때문에 빠르게 수행할 수 있다.
  • 단점
    1. 인덱스를 관리하기 위한 추가 작업이 필요하다.
    2. 추가 저장 공간이 필요하다.
    3. 잘못 사용하는 경우 오히려 검색 성능 저하.
    • 항상 정렬된 상태로 유지해야 하기 때문에, 인덱스가 적용된 컬럼에 아래 작업을 수행하려면 추가 작업이 필요하다.
    • INSERT : 새로운 데이터에 대한 인덱스를 추가
    • DELETE : 삭제하는 데이터의 인덱스를 사용하지 않는다는 작업 수행
    • UPDATE : 기존의 인덱스를 사용하지 않음 처리, 갱신된 데이터에 대한 인덱스 추가.
    • 데이터의 수정이 잦은 경우 성능이 낮아진다. 이때 인덱스를 제거하는 것이 아닌 '사용하지 않음' 처리하고 남겨두기 때문에 자주 수정하면 인덱스가 과도하게 커지는 문제점이 발생한다. 별도의 메모리 공간에 저장되기 때문에 추가 저장 공간이 많이 필요하게 된다.
    • 또한 인덱스는 전체 데이터의 10% ~ 15% 이상의 데이터를 처리하거나 데이터의 형식에 따라 오히려 성능이 낮아질 수 있다.
    • 예를 들어, 나이나 성별과 같이 값의 범위가 적은 컬럼인 경우, 인덱스를 읽고 나서 다시 많은 데이터를 조회해야 하기 때문에 비효율적이다.

인덱스를 사용하면 좋은 경우

  • 데이터의 범위가 넓고, 중복이 적고, 조회가 많거나 정렬된 상태가 유용한 컬럼에 사용하는 것이 좋다.
  1. 규모가 큰 테이블
  2. INSERT, UPDATE, DELETE 작업이 자주 발생하지 않는 컬럼
  3. WHERE나 ORDER BY, JOIN 등이 자주 사용되는 컬럼
  4. 데이터의 중복도가 낮은 컬럼

인덱스의 자료구조

  • 인덱스는 여러 자료구조를 이용해서 구현할 수 있는데, 대표적으로 해시 테이블과 B+Tree가 있다

1. 해시 테이블

  • (key, value)를 한 쌍으로 데이터를 저장하는 구조이다. key 값을 이용해 대응되는 value 값을 구하는 방식이며, 해시 충돌이라는 변수가 존재하지만 평균적으로 O(1)의 매우 빠른 시간만에 원하는 데이터를 탐색할 수 있는 구조이다.
  • 인덱스는 (key,value) = (컬럼의 값, 데이터의 위치) 로 구현하는데 해시 테이블은 실제로 인덱스에서 잘 사용되지 않는다.
  • 이유는 해시 테이블은 등호(=) 연산에 최적화되어 있기 때문이다.
  • 데이터베이스에선 부등호 연산이 자주 사용되는데, 해시 테이블 내의 데이터들은 정렬되어 있지 않으므로 특정 기준보다 크거나 작은 값을 빠른 시간 내에 찾을 수가 없다.

2. B+Tree

  • 기존의 B-Tree는 어느 한 데이터의 검색은 효율적이지만, 모든 데이터를 한 번 순회하는 데에는 트리의 모든 노드를 방문해야 하므로 비효율적이다. 이러한 단점을 개선시킨 자료구조이다.
B-Tree 예시
  • 오직 leaf node에만 데이터를 저장하고, leaf node가 아닌 node에서는 자식 포인터만 저장한다. 그리고 leaf node 끼리는 Linked List로 연결되어있다.
  • 반드시 leaf node에만 데이터가 저장되기 때문에 중간 node에서 key를 올바르게 찾아가기 위해 key가 중복될 수 있다.
B+Tree 예시
  • 장점
    1. leaf node를 제외하고 데이터를 저장하지 않기 때문에 메모리를 더 확보할 수 있다. 따라서 하나의 node에 더 많은 포인터를 가질 수 있기 때문에 트리의 높이가 더 낮아지므로 검색 속도를 높일 수 있다.
    2. full scan을 하는 경우 B+Tree는 leaf node에만 데이터가 저장되어 있고, leaf node끼리 linked list로 연결되어 있어서 선형 시간이 소모된다. B-Tree는 모든 node를 확인해야한다.
    • B+Tree를 사용하는 이유
      • 인덱스 컬럼은 부등호를 이용한 순차 검색 연산이 자주 발생할 수 있기 때문에 linked list를 이용하면 순차 검색을 효율적으로 할 수 있게 된다.
  • B+Tree 의 검색 과정은 B-Tree와 동일하지만, 삽입과 삭제 과정은 약간의 차이가 있다 아래에서 알아보자.
  1. 삽입
  • (1) key의 수가 최대보다 적은 leaf node에 삽입하는 경우
    • 해당 node의 가장 앞이 아닌 곳에 삽입하는 경우는 단순히 삽입해 주면 된다.
    • 하지만, leaf node의 가장 앞에 삽입되는 경우는 해당 node를 가리키는 부모 node의 포인터의 오른쪽에 위치한 key를 K로 바꿔준다. 그리고 leaf node끼리 linked list로 이어줘야 하므로 삽입된 key에 linked list로 연결한다.
  • (2) key의 수가 최대인 leaf node에 삽입하는 경우
    • key의 수가 최대이므로 삽입하는 경우 분할을 해주어야 한다. 만약 중간 node에서 분할이 일어나는 경우는 B-Tree와 동일하게 해준다.
    • leaf node에서 분할이 일어나는 경우는 중간 key를 부모 node로 올려주는데 이때 오른쪽 node에 중간 key를 붙여 분할한다. 그리고 분할된 두 node를 linked list로 연결해준다.
  1. 삭제
  • (1) 삭제할 key가 leaf node의 가장 앞에 있지 않은 경우
    • B-Tree와 동일한 방법으로 삭제된다.
  • (2) 삭제할 key가 leaf node의 가장 앞에 위치한 경우
    • leaf node가 아닌 node에 key가 중복해서 존재한다. 따라서 해당 key를 노드보다 오른쪽에 있으면서 가장 작은 값으로 바꿔주어야 한다.

다음 게시글 API

https://hoozy.tistory.com/entry/%EB%B0%B1%EC%97%94%EB%93%9C-API

 

[백엔드] API

이전 게시글 DB 추가 정보 https://hoozy.tistory.com/entry/%EB%B0%B1%EC%97%94%EB%93%9C-DB-%EC%B6%94%EA%B0%80-%EC%A0%95%EB%B3%B4 API Application Programming Interface의 약자로, 여러 프로그램들과 데이터베이스, 그리고 기능들의

hoozy.tistory.com

참고 자료

https://geonlee.tistory.com/207
https://hanamon.kr/%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4-%ED%8A%B8%EB%9E%9C%EC%9E%AD%EC%85%98%EC%9D%98-acid-%EC%84%B1%EC%A7%88/
https://mangkyu.tistory.com/28
https://code-lab1.tistory.com/270
https://rebro.kr/167

'CS > 데이터베이스' 카테고리의 다른 글

[백엔드] NoSQL(비관계형) DB  (0) 2023.04.03
[백엔드] 관계형 DB  (0) 2023.04.02

댓글