이번에 싸피에서 DB 커리큘럼을 진행하였다. 커리큘럼 대부분의 시간을 SQL 문법을 배우는데 시간을 할애했는데 이 SQL 문법을 실행했을 때 내부적으로 어떻게 동작하는지에 대해서 호기심이 생겼다. 이 내용을 CS 스터디원들과 공유하여 함께 공부하였고, 그 중 내가 정리한 부분에 대해서 이야기해보고자 한다.
MySQL 서버는 사람의 머리 역할을 담당하는 MySQL 엔진과 손발 역할을 담당하는 스토리지 엔진으로 구분할 수 있으며, 손과 발 역할을 담당하는 스토리지 엔진은 핸들러 API를 만족하면 누구든지 스토리지 엔진을 구현하여 사용할 수 있다.
기본적으로 C를 제외한 웬만한 응용 프로그램 언어에서는 DB 접근 기술에 관련된 인터페이스가 존재한다. 자바의 경우에는 JDBC를 예로 들 수 있다. 하지만 알다시피 자바는 인터페이스뿐만 아니라 구현체가 있어야 클라이언트가 사용할 수 있고, 그러한 구현체는 각각의 DBMS를 제공하는 회사들이 직접 배포한다. MySQL의 Connectors가 그러한 구현체이다.
MySQL 서버의 머리에 해당되는 부분이다.
클라이언트의 요청을 받아 SQL의 질의어가 서버로 들어오게 되면 파서가 질의어를 파싱하여 트리 형태로 구성하게 한다. 트리 형태로 구성된 질의어를 전처리기가 문법상 오류가 없는 지 검증 절차를 가지고, 그 후에 옵티마이저에게 넘겨준다. 옵티마이저는 받은 질의어로 최적화된 실행 계획을 짠다. 이러한 실행 계획을 짜는 방식은 두 가지로 나뉜다.
최적화 및 실행 계획이 끝나면 이제 스토리지 엔진에 요청해 필요한 데이터를 물리적 장치에서 가지고 온다.
스토리지 엔진은 현재 InnoDB가 가장 많이 쓰이므로 InnoDB 위주로 설명한다.
InnoDB의 가장 큰 특징으로는 트랜잭션을 지원한다는 점이다. DB에서는 쪼갤 수 없는 작업의 단위를 트랜잭션이라고 한다.
InnoDB는 In-Memory 구조와 On-Disk 구조로 이루어져 있다.
데이터베이스 시스템의 성능 최적화를 위해 목표로 잡은 것은 IO를 줄이는 것이다. 즉 디스크 영역에 접근하는 횟수를 줄이기 위해 메모리 영역에서 처리하는 것을 우선으로 한다.
아래의 사진 두 개는 개인적으로 트랜잭션의 격리 수준이 어떤건지 확실하게 보기 위해서 해본 실험이다. MySQL 워크벤치에서 하는 작업을 1번 트랜잭션, MySQL CLI에서 하는 작업을 2번 트랜잭션이라고 가정하고 진행한다.
우선 1번 트랜잭션에서 user 필드 하나를 테이블에 저장한 후 커밋한다. 그럼 이 데이터 블록은 버퍼 풀에 담겨있을 것이다.
그리고 1번 트랜잭션에서 다시 이 필드의 이름을 'asd'로 수정하고자 한다. 수정 쿼리문을 처리하고 1번 트랜잭션에서 이 필드를 조회했을 때 수정된 값으로 조회할 수 있다.
하지만 2번 트랜잭션에서는 이 필드를 조회했을 때 마지막 커밋 직전의 값을 보게 된다. 이게 바로 트랜잭션 격리 수준 때문이다. 1번 트랜잭션이 커밋을 하지 않는 이상 다른 트랜잭션에서는 저 변경된 값을 Read uncommited를 제외한 다른 옵션에서는 절대 확인할 수 없다.
위 사진은 1번 트랜잭션으로 본인의 트랜잭션이 변경한 내용은 커밋되지 않아도 확인할 수 있다.(name = asd)
위 사진은 2번 트랜잭션으로 다른 트랜잭션의 조작 결과를 커밋 이전에는 확인할 수 없다. 이러한 결과가 나오는 이유는 트랜잭션이 커밋이 되기 이전에는 undo log에서 테이블의 정보를 조회하기 때문이다.
위 사진의 쿼리문 결과를 보면 확인할 수 있듯이, 테이블 당 테이블스페이스 파일이 ibd파일로써 각각 관리되고 있음을 확인할 수 있다.
MySQL은 빠른 성능과 영속성 유지를 위해서 DBMS의 아키텍처를 정밀하고 효율적으로 개발했다는 것이 느껴졌다. 아키텍처 내부에서 열심히 알아보았는데도 이해 못한 부분들이 많았다. 특히 doublewrite buffer와 redo log에서 구분되는 부분이 무엇인지와, 더티 페이지가 flush가 될 때 doublewrite buffer와 테이블 스페이스에 총 두 번 쓰여지는데 두 배의 오버헤드나 I/O 작업은 필요하지 않다는 점도 구글링을 해보아도 도저히 이해가 되지 않았다.
그래도 이번 포스팅을 통해 우리가 쿼리문을 보낼 때 그 쿼리문들이 어떠한 방식으로 컴퓨터가 이해가능한 코드로 바뀌고 어떤 흐름을 통해서 디스크에 저장이 되는지 알 수 있었다. 조금 더 백엔드 개발자에 가까워진 기분이다!!!!!