문득 글을 쓰면서 내가 DB를 여러 기능을 가진 마법의 블랙박스처럼 대하고 있다는 생각을 했다. 사실 여러 상용화된 DBMS는 전부 프로그램인데... 소스코드를 찾아봐야겠다는 생각을 했다. 다행히 요즘은 GPT와 함께라면 뭔들 쉽게 도전해볼 수 있는 시대였다.

oracle, mysql, mariadb, postgresql과 같은 dbms 중에서 오픈소스 dbms가 있을까? 나는 이 프로그램의 소스 코드를 보고 싶어.

 

GPT 피셜 Oracle을 제외한 대부분의 데이터베이스는 오픈소스이며 깃허브 레포 등을 통해서 코드를 볼 수 있었다. 다만 오픈소스여도 라이센스에 따라 범위가 제한되어 있었다. 

 

가령 MySQL은 GNU GPL 라이센스 하에 배포된다고 하는데, 이는 사적인 이익을 추구하지 않는 한(non-proprietary) 사용자가 해당 소프트웨어의 코드를 복제나 변경하여 사용하는 것이 자유로운 라이센스다. 

 

요즘에는 회사에서 Oracle을 사용한다고는 하지만, 그 전에는 MySQL을 대부분 사용해 왔기에 MySQL의 공식 레포로 들어가 보았다. 대부분의 소스 코드는 C++로 작성되어 있어서 구체적인 이해는 어려울 것 같았으나, 어차피 GPT도 있으니 대략적인 구조나 자주 쓰는 명령어들이 어떻게 실행되는지는 이해해볼 수 있겠다. 

mysql의 여러 오퍼레이션이 어떻게 동작하는지를 소스코드를 보면서 이해하고 싶어. 소스코드의 어떤 부분을 보면 될까?

 

소스 코드의 기본 구조는 다음과 같다고 한다. 

  • sql/: MySQL 서버의 주요 기능을 구현했다. SQL Parser, Optimizer, 실행 엔진 로직이 들어있다.
  • storage/: 실행 엔진이 구현되어 있다. ex. InnoDB나 MyISAM 코드가 포함되어 있다. 
  • libmysql/: MySQL 클라이언트 라이브러리 코드라고 한다. 
  • cmd-line-utils/: MySQL 명령어와 관련된 코드라고 한다. 

오늘은 이 중에서도 SQL Parser에 대해 알아볼 것이니 sql/ 디렉토리의 코드를 보면 되겠다. 헤더 파일과 로직 파일로 구분되어 있었다. GPT에게 물어보니 sql/sql_parse.cc 파일에 핵심 로직이 있다고 했다. 그래서 그걸 보려고 했었다.

 

라인의 개수를 확인하기 전까지는 말이다. 7000개의 라인을 그대로 읽기는 불가능했다. 여기서의 핵심 메소드만 찾아봤다. GPT 피셜, 'parse_sql' 함수에 핵심 로직이 있다고 한다. 

 

공식문서의 설명을 보니 해당 함수는 SQL문을 AST(abstract syntax tree)로 변환해주는 함수이다. AST란 문법적인 구조를 트리 형태로 나타나기 위해 사용하는 자료구조라고 한다. 

 

✅ 궁금한 점

  • 실행 엔진의 정의는 무엇인가?
  • InnoDB나 MyISAM은 실행엔진의 한 종류인가? 맞다면 둘은 어떻게 다른가?
  • libmysql/에는 MySQL 클라이언트 라이브러리 코드가 있다고 하는데, 서버면 서버인 것이지 클라이언트는 또 무엇인가?

✅ 궁금한 점 셀프 답변

실행 엔진(execution engine)은 옵티마이저(optimizer)로부터 쿼리 실행 계획을 받은 다음, 스토리지 엔진과 통신하여 데이터를 읽거나 쓰는 등의 실제로 계획을 수행하는 핵심 컴포넌트라고 한다. 여기서 '스토리지 엔진'과 '실행 엔진'이 다르다는 것을 알았다. 

 

스토리지 엔진(storage engine)은 실행 엔진의 지시에 따라 데이터를 디스크에 저장하고 관리하는 역할을 하는 하위 모듈이다. MySQL은 플러그인 방식의 스토리지 엔진 구조를 사용한다고 한다. 따라서 플러그인을 쉽게 교체하는 것처럼 하나의 MySQL DB를 사용할 때에도 InnoDB, MyISAM 등 다양한 스토리지 엔진을 사용할 수 있다고 한다. 

 

스토리지 엔진은 데이터를 디스크(저장 장치)에 저장하는 작업뿐만 아니라 인덱스(Index), 트랜잭션(Transaction), 동시성 제어를 위한 잠금(Locking) 관리 등을 담당한다고 한다. 그리고 데이터의 무결성을 관리하고, 장애가 났을 경우 세이브포인트를 사용하여 데이터를 복구한다고 한다. 

 

대표적인 스토리지 엔진은 InnoDB와 MyISAM이 있다. GPT는 메모리도 기술하였지만 메모리의 경우 장기적인 데이터 저장보다는 일시적인 캐시를 저장하는 용도로 많이 쓰이는 것 같다. 그리고 몰랐던 사실인데 MyISAM의 경우 트랜잭션을 지원하지 않는다고 한다...! 나는 이전에 DB의 ACID 중 Isolation에 대한 설명이 트랜잭션 단위라고 이해하고 있었는데, MyISAM에 트랜잭션이라는 개념 자체가 사용되지 않는다면 MyISAM의 Isolation level은 어떻게 되는지도 궁금하다. 

 

그리고 데이터의 무결성을 관리하기 위해서 쓰기 작업 시 잠금을 사용한다는 것 까지는 알았는데, 이것도 InnoDB와 MyISAM에서의 방식이 달랐다. InnoDB에서는 테이블 잠금(한 트랜잭션이 어떤 테이블에 쓰기 작업을 하고 있는 경우 테이블 전체에 대해서 다른 트랜잭션의 쓰기 작업을 제한)과 행 잠금(한 트랜잭션이 어떤 테이블에 쓰기 작업을 하고 있는 경우 해당 행에 대해서만 다른 트랜잭션의 쓰기 작업을 제한) 모두를 지원하는 반면, MyISAM은 테이블 잠금만 지원한다고 한다. 

 

'server-side > database' 카테고리의 다른 글

DB - ACID, Isolation level, Query parsing & optimization  (0) 2025.01.26

회사에서 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 > database' 카테고리의 다른 글

MySQL 한 티스푼 뜨기 - Parser  (0) 2025.01.27

+ Recent posts