회사에서 DB 지식을 좀 쌓기 위해서 그림으로 공부하는 오라클 구조라는 책을 읽는 중이다. 아직 12장 중 4장까지만 읽었고 정독보다는 속독을 하는 중인데, 읽다보니 새삼 내가 DB에 대해서 제대로 아는 게 없다는 사실을 많이 느꼈다. 그래서 포스팅을 통해 현재까지의 내가 뭘 모르고 뭘 아는지를 정리해보려고 한다.
✅ 내용 정리
우리가 많이 들어본 Oracle, MySQL, MariaDB, PostgreSQL과 같은 데이터베이스들은 모두 DBMS(database management system)의 여러 종류들이다. DBMS는 데이터를 저장하고 꺼내올 수 있는 디스크나 메모리같은 저장 장치만을 의미하는 것이 아니라, 넓은 의미로는 DB의 4원칙이라 불리는 ACID를 보장할 수 있도록 관리해주는 시스템이라고 이해했다.
내가 이해한 ACID는 다음과 같다.
- Atomicity: commit은 원자적이다. 하나의 commit은 전체가 모두 반영되거나 전체가 모두 반영되지 않는다.
- Consistency: DB의 데이터는 일관성 있게 관리되어야 한다.
- Isolation: 트랜잭션이 다른 트랜잭션의 간섭을 받지 않고 독립적으로 실행되는 것을 보장한다. 여기서 '독립적'이라는 말은 해석의 여지가 있다. 이는 DB의 isolation level에 따라서 다르게 해석될 수 있다.
- Durability: commit한 데이터는 어떤 일이 있어도 반드시 DB에 기록되어야 한다.
Durability와 Atomicity를 동시에 보장하는 일은 어렵다고 한다. 특히 Durability를 준수하려면 commit한 데이터는 어떤 일이 있어도 반드시 DB에 기록되어야 한다는 말은 commit 후 DB에 저장하지 않은 상태에서 DB failure가 발생하는 경우를 대비해야 한다.
그러기 위해서 매번 commit 할 때마다 DB에 저장을 하면 되지 않나? 라고 할 수 있지만, 그럴 경우 commit이 빈번하게 되면 DB I/O가 빈번하게 발생하여 성능 저하로 이어진다. 즉 이 ACID를 만족시키면서 동시에 높은 성능을 제공하는 것이 어렵다고 할 수 있다. DBMS는 이 Durability를 지키기 위해서 DB에 데이터를 저장하는 것과 별도로 로그를 기록하는 저장소를 따로 두고 있다고 한다.
Isolation의 경우, DB를 동시에 사용하고 있는 여러 사용자가 있고 그들이 동시에 데이터를 조회하거나 commit을 날릴 경우 어떻게 대응할지에 따라 4단계로 나뉜다. 아래로 갈수록 트랜잭션(transaction)들은 서로 강하게 분리되지만, 그만큼 처리에서 시간이 오래 걸릴 가능성은 높아진다.
- Read Uncommited: 어떤 트랜잭션의 변경 내용이 commit과 rollback과 상관없이 다른 트랜잭션에서 보여진다. 예를 들면 A라는 트랜잭션에서 CUD(create/update/delete)하고 commit하지 않은 내용을 다른 트랜잭션에서 조회할 수 있다. 이 경우 Dirty Read 문제가 발생할 수 있다.
- Read Committed: 한 트랜잭션이 commit한 내용만 다른 트랜잭션에서 볼 수 있다. 한 트랜잭션에서 commit하지 않고 CUD한 내용들은 Undo 영역에 저장된다고 한다. 그러나 이 경우에도 다른 트랜잭션에서 CUD를 하고 commit을 해 버리면 그 다음부터는 다른 트랜잭션에서 변경된 내역을 바로 볼 수 있기 때문에, Non-Repeatable Read 문제가 발생할 수 있다.
- Repeatable Read: 한 트랜잭션이 시작되기 전에 commit된 내용에 대해서만 볼 수 있다. 즉 트랜잭션 여러 개가 진행 중인 상황에서 한 트랜잭션이 commit을 해도 다른 트랜잭션들은 자신이 시작된 시점에 commit 되어있던 내용에 대해서만 볼 수 있다. 이 경우 Non-Repeatable Read는 발생하지 않으나, Insert 문에 대해서는 이야기가 다르다. 다른 트랜잭션에서 Insert 쿼리가 실행되었을 경우 Phantom Read 문제가 생길 수 있다.
- Serializable: 하나의 트랜잭션이 실행 중일 때 다른 트랜잭션은 실행되지 않는다. 트랜잭션 간의 실행을 완벽히 분리할 수 있고 Dirty Read, Non-Repeatable Read, Phantom Read 등이 발생하지 않는다는 장점이 있지만, 트랜잭션을 순차적으로 실행하면서 여러 트랜잭션의 처리 시간이 길어지는 단점도 있다.
그리고 Serializable 단계를 제외한 나머지 세 단계에서 나타나는 현상들은 다음과 같다.
- Dirty Read: 한 트랜잭션에서 commit 없이 변경한 내용을 다른 트랜잭션에서 읽어들일 수 있다. 만약 이 데이터가 정상적으로 commit되지 않고 rollback 되어도 이미 그 데이터를 읽어들인 다른 트랜잭션에서는 이를 알 길이 없다. 즉 트랜잭션의 입장에서는 지금 자신이 읽어들인 데이터가 정상적으로 commit된 것인지 아닌지도 알 수 없다.
- Non-Repeatable Read: 한 트랜잭션에서 데이터 변경 없이 여러 번 조회 쿼리를 날렸을 때 그 결과가 바뀔 수 있다.
- Phantom Read: 한 트랜잭션에서 데이터 변경 없이 여러 번 조회 쿼리를 날렸을 때, 기존에 있던 데이터가 변경되지는 않지만 새로운 데이터가 생성된 결과가 나올 수 있다.
표로 나타내면 다음과 같다.
Dirty Read | Non-Repeatable Read | Phantom Read | |
Read Uncommitted | Yes | Yes | Yes |
Read Committed | No | Yes | Yes |
Repeatable Read | No | No | Yes |
Serializable | No | No | No |
그리고 SQL을 파싱(parsing)할 때도 DBMS가 관여하는 부분이 있다. SQL은 어떤 데이터를 읽을지(what)를 기술하는 언어이지 어떻게(how) 읽을지를 알려주지 않는다. SELECT FROM, WHERE, GROUP BY, ORDER BY 등으로 어떤 데이터를 읽어올지만 기술한다. 그렇다면 그 데이터를 '어떻게' 읽을지는 DBMS에서 판단하여야 한다.
어떻게 읽는다는 말이 무슨 의미일까? 그냥 읽으면 되는 게 아닐까?
DBMS에서는 SQL 쿼리를 실행할 때 어떻게 하면 해당 쿼리를 효율적으로 실행할지를 고민한다. (사실 전공에서 배운 기억은 있는데 이 부분은 잘 모른다...) 이를 위해서 SQL 파싱 단계에서 만든 parse tree를 사용하기도 하며, 각 DB table에 대한 통계 자료를 사용하기도 한다.
물론 옵션으로 통계 자료를 수집할지 말지, 수집한다면 어느 시점에 수집할지 등을 정할 수 있다. 예를 들면 DBMS는 특정 column의 average, sum, min, max 등의 값을 수집하고 이 값들을 쿼리 최적화(query optimization) 및 쿼리가 효율적인지 평가(query evaluation)하는 데 사용한다고 알고 있다.
✅ 궁금한 점
- Oracle, Mysql, Mariadb와 같은 DBMS들은 어떻게 사용자의 정보를 저장할까?
- 왜 Durability와 Atomicity를 동시에 보장하는 것이 어려울까?
- Durability를 준수하기 위해서 로그 저장소를 따로 두고 I/O를 하면 DB I/O를 하는 것에 비해서 어떤 장점이 있을까? 결국 I/O는 똑같아 보이는데 어떤 이유로 이 방법을 택한 것일까?
- 나는 DB의 구조를 일단은 다음과 같이 이해했는데, 더 구체적인 그림을 그려보고 싶다.
✅ References
'server-side > server' 카테고리의 다른 글
웹 어플리케이션 서버(WAS) (0) | 2024.08.05 |
---|---|
Mac 환경설정 (0) | 2024.07.15 |
Software Release Life Cycle (0) | 2023.07.15 |
OAuth 2.0 기본원리 (0) | 2022.09.26 |
인증(Authentication) (0) | 2022.07.14 |