DB Review (2)

이미 DDIA책, 수업 등의 경험으로 DB에 대한 지식을 어느정도 갖췄지만, 기본적인 거를 정리할 겸 블로그를 사용하고자 한다. 큰 컨셉들을 정리하고 생각해보는 시간을 갖출 것이다. 이 내용은 추후 더 정리하거나 추가될 수도 있다.

DBMS 구조


DBMS는 보통 디스크에 메모리를 저장하고 데이터베이스의 일부분을 메인 메모리에 유지한다. DBMS는 Query Processor와 Storage System으로 나눌 수 있으며, 두 개는 layered 구조를 가진다. DBMS는 데이터를 페이지 단위로 저장하여 해당 단위로 디스크에 읽거나 쓴다. 메인 메모리에 유지하는 페이지들을 관리하는 모듈을 페이지 버퍼라 한다. (해당 내용은 네이버 D2 블로그에서 참고했다)

Transaction

데이터베이스의 상태를 변경시키기 위해 수행하는 작업의 단위로서 (Select, Insert, Delete, Update) 작업의 완전성을 보장해줘야한다. 트렌젝션이 성공하면 시스템에 반영하고 (commit) 실패하면 변경사항이 반영되지 않는다 (rollback). 트랜잭션은 다음과 같은 특징을 가지고 있다 (ACID).

  • Atomicity
    트랜잭션이 DB에 모두 반영되거나 반영되지않거나 해야 한다.
  • Consistency
    트랜잭션 완료 이후에는 일관성 있는 데이터를 보장해줘야한다.
  • Isolation
    여러 개의 트랜잭션이 이뤄지는 경우 각각 독립적으로 일어나야한다.
  • Durability
    트랜잭션이 완료되면 영구적으로 반영되야 한다.

또한 트랜잭션은 다음과 같은 상태가 있다.

  • Active
    트랜잭션이 실행중이다.
  • Partially Committed
    트랜잭션의 commit명령이 도착하여 commit 이전 sql이 수행된 상태이다.
  • Committed
    트랜잭션 완료 상태이다.
  • Failed
    트랜잭션이 실패한 경우이다.
  • Aborted
    트랜잭션을 취소한 상태이다.

교착상태

두개 이상의 트랜잭션이 특정 테이블이나 행의 lock을 획득한채 다른 트랜잭션이 보유하고 있는 lock을 요구하면 시간이 지나도 바뀌지않으므로 deadlock이 발생하게 된다!

Transaction 1> create table B (i1 int not null primary key) engine = innodb;
Transaction 2> create table A (i1 int not null primary key) engine = innodb;

Transaction 1> start transaction; insert into B values(1);
Transaction 2> start transaction; insert into A values(1);

Transaction 1> insert into A values(1);
Transaction 2> insert into B values(1);
ERROR 1213 (40001): Deadlock found when trying to get lock; try restarting transaction

이러한 교착상태를 줄이기 위해 Transaction을 자주 커밋하고, Transaction을 작고 짧게 유지해야하며, SELECT ~ FOR UPDATE 구문을 사용하지 말아야하며 정해진 순서로 (동일한 테이블순으로) 트랜젝션들이 접근하게 해야한다.

Lock의 종류

Lock은 트랜잭션의 순차성을 보장해준다. 크게 Exclusive Lock (Write Lock)Shared Lock (Read Lock)으로 나눌 수 있다. X Lock은 Write할때 사용되며 insert, select for update, update 등 할때 쓰인다. S Lock은 Read할때 사용된다. S Lock은 동시에 사용될수 있으나 이 경우 W Lock은 사용되지 못하며, X Lock이 걸려있는 경우에는 두 Lock모두 걸지 못한다.

Isolation Level of Transaction

Isolation Level이 올라갈 수록 일관성은 보장하지만 성능은 하락한다.

  1. Read Uncommitted (Level 0)
    트랜잭션이 처리중이거나 커밋하지않은 데이터를 다른 트랜잭션이 읽는 것을 허용함. Dirty Read, Non-Repeatable Read, Phantom Read가 일어날 수 있음
  2. Read Committed (Level 1)
    트랜잭션이 커밋되어 확정된 데이터만 다른 트랜잭션이 읽도록 허용함. 즉, 커밋되지 않은 데이터에 대해선 실제 DB데이터가 아닌 UNDO 로그에서 이전 데이터를 가져오게 함. Dirty Read는 허용하지만, 나머지는 막지 못함. 일반적인 DBMS에서 사용됨.
  3. Repeatable Read (Level 2)
    트랜잭션에서 읽고 있는 데이터는 다른 트랜잭션에서 수정하거나 삭제할 수 없다 (삽입은 가능). Phantom Read가 일어날 수 있다. MySQL InnoDB가 이를 사용한다.
  4. Serializable Read (Level 3)
    완벽한 읽기 일관성을 보장함. 모든 이상현상을 방지할 수 있다. 단, 동시성이 떨어져 성능이 저하됨.

다음과 같은 이상현상을 알아두면 좋다.

  • Dirty Read
    다른 트랜잭션에 의해 수정됐지만 커밋되지 않은 데이터를 읽는 경우. 이럴 경우 원래 트랜잭션이 롤백하게 되면 잘못된 데이터를 읽게 된다.
  • Non-Repeatable Read
    한 트랜잭션에서 같은 키를 가진 row를 두번 읽는데 값이 달라지거나 삭제되는 경우를 말한다.
  • Phantom Read
    한 트랜잭션에서 같은 쿼리를 두번 수행했는데 첫 쿼리에 없던 유령 레코드가 두번째 쿼리에는 발생하는 경우를 말한다.

다음엔 인덱싱, NoSQL의 개념, 그리고 기타 개념을 다뤄보겠다.

Reference

https://d2.naver.com/helloworld/407507
https://github.com/JaeYeopHan/Interview_Question_for_Beginner/tree/master/Database#transaction