티스토리 뷰

GitHub: https://github.com/ohksj77/kmysql

 

GitHub - ohksj77/kmysql: Kotlin으로 만든 MySQL 프로젝트

Kotlin으로 만든 MySQL 프로젝트. Contribute to ohksj77/kmysql development by creating an account on GitHub.

github.com

 

👋🏻 들어가며

이전에 응시한 면접에서 이러한 질문을 받은 적이 있습니다.

“직접 DB를 구현한다면 어떻게 Repeatable Read를 구현하고 싶으신가요?”

 

당시 질문을 받은 후 추상적인 생각들만 겉돌며 당황한 기억이 있습니다.

이러한 계기로 언젠가는 간단하게라도 DBMS를 구현해보고 싶은 욕구가 생겼고, 이를 실제로 구현해봤습니다.

 

 

기술 스택: Kotlin 1.9+, Java 17+, Gradle 8.0+

 

언어나 빌드 툴은 다양한 선택지가 있지만, 성능 등의 이점 보다는 자주 사용 중인 기술로 선정해 진행했습니다.

 

📌 동작 예시

 

🏗️ KMySQL의 핵심 컴포넌트들

전체 아키텍처 개요

 

각 컴포넌트의 역할

  1. File Manager: 디스크 I/O 관리, 페이지 and 블록 단위 파일 접근
  2. Buffer Manager: 메모리 캐싱, LRU 기반 버퍼 교체
  3. Transaction Manager: ACID 보장, 동시성 제어, 복구 관리
  4. Index Manager: B-Tree, Hash 인덱스 관리
  5. Query Planner: SQL 실행 계획 생성 및 최적화
  6. JDBC Interface: 외부 애플리케이션 연동

 

🔄 트랜잭션 관리: ACID의 핵심

ACID 속성 개념과 구현 방식

원자성 (Atomicity)

  • 모든 작업이 성공하거나 모두 실패해야 함
  • 로그 기반 복구로 롤백 보장

일관성 (Consistency)

  • 데이터베이스가 항상 유효한 상태 유지
  • 제약조건과 트리거로 보장

격리성 (Isolation)

  • 동시 실행되는 트랜잭션들이 서로 간섭하지 않음
  • MVCC와 락킹으로 구현

지속성 (Durability)

  • 커밋된 트랜잭션은 영구적으로 저장
  • WAL(Write-Ahead Logging) 프로토콜로 보장

격리 수준과 동시성 제어

격리 수준별 동시성 vs 일관성 트레이드오프

READ UNCOMMITTED  ←─── 높은 동시성, 낮은 일관성
READ COMMITTED
REPEATABLE READ
SERIALIZABLE      ←─── 낮은 동시성, 높은 일관성

 

MVCC (Multi-Version Concurrency Control)

  • 각 트랜잭션이 데이터의 특정 시점 스냅샷을 보게 함
  • 읽기 작업이 쓰기 작업을 블록하지 않음
  • 메모리 사용량과 성능 간의 균형

 

💾 버퍼 관리: 성능 최적화의 핵심

메모리 계층 구조

CPU Cache (L1, L2, L3)
    ↓ (10-100배 빠름)
Main Memory (Buffer Pool)
    ↓ (100,000-1,000,000배 빠름)
Disk Storage

버퍼 관리 전략

LRU (Least Recently Used) 교체 정책

  • 가장 오래전에 사용된 페이지를 먼저 교체
  • 지역성 원리(locality)를 활용한 성능 최적화

핀/언핀 메커니즘

  • 페이지가 사용 중일 때는 교체되지 않도록 보호
  • 참조 카운트로 안전한 메모리 해제 보장

지연 쓰기 (Lazy Writing)

  • 변경된 페이지를 즉시 디스크에 쓰지 않음
  • 버퍼가 가득 찰 때나 체크포인트 시에만 쓰기

 

🗂️ 인덱스: 검색 성능의 핵심

인덱스의 종류와 특징

B-Tree 인덱스

  • 범위 검색과 정렬에 최적화
  • 삽입/삭제 시 자동으로 균형 유지
  • 검색 시간: O(log n)

Hash 인덱스

  • 등호 검색에 최적화
  • 매우 빠른 검색 속도: O(1)
  • 범위 검색은 비효율적

 

💾 로그 기반 복구: 데이터 안정성의 보장

WAL (Write-Ahead Logging) 기법

핵심 원칙: 데이터 페이지를 디스크에 쓰기 전에 로그를 먼저 써야 함

트랜잭션 실행 순서:
1. 로그 레코드 작성
2. 로그를 디스크에 강제 쓰기 (flush)
3. 데이터 페이지 수정
4. 커밋 로그 작성
5. 커밋 로그를 디스크에 강제 쓰기

복구 시나리오

시스템 크래시 복구

  • 마지막 체크포인트부터 로그를 재실행 (REDO)
  • 커밋되지 않은 트랜잭션 롤백 (UNDO)

트랜잭션 실패 복구

  • 해당 트랜잭션의 로그를 역순으로 읽어서 변경사항 되돌리기

 

🔍 쿼리 처리: SQL에서 결과까지

쿼리 처리 파이프라인

SQL 쿼리
    ↓
Lexical Analysis (토큰화)
    ↓
Parsing (구문 분석)
    ↓
Semantic Analysis (의미 분석)
    ↓
Query Optimization (최적화)
    ↓
Execution Plan (실행 계획)
    ↓
Query Execution (실행)
    ↓
Result Set (결과)

실행 계획의 종류

  • Table Scan: 전체 테이블을 순차적으로 읽기
  • Index Scan: 인덱스를 사용한 효율적인 검색
  • Nested Loop Join: 중첩 루프를 이용한 조인
  • Hash Join: 해시 테이블을 이용한 조인
  • Sort Merge Join: 정렬 후 병합하는 조인

 

🌭 실제 사용 시나리오

대화형 SQL 클라이언트

-- 데이터베이스 연결
Connect> kmysql_db

-- 테이블 생성 및 데이터 삽입
CREATE TABLE users (
    id INT PRIMARY KEY,
    name VARCHAR(50),
    email VARCHAR(100)
);

INSERT INTO users VALUES (1, '홍길동', 'hong@example.com');
INSERT INTO users VALUES (2, '김철수', 'kim@example.com');

-- 트랜잭션 내에서 작업
BEGIN;
UPDATE users SET name = '김영희' WHERE id = 2;
SAVEPOINT sp1;
DELETE FROM users WHERE id = 1;
ROLLBACK TO SAVEPOINT sp1;
COMMIT;

JDBC를 통한 프로그래밍

// 데이터베이스 연결
val driver = EmbeddedDriver()
val connection = driver.connect("kmysql_db", null)

try {
    connection.setAutoCommit(false)  // 트랜잭션 시작

    val statement = connection.createStatement()
    statement.executeUpdate("INSERT INTO users VALUES (3, '이철수', 'lee@example.com')")

    val resultSet = statement.executeQuery("SELECT * FROM users WHERE id = 3")
    while (resultSet.next()) {
        println("Name: ${resultSet.getString("name")}")
    }

    connection.commit()  // 트랜잭션 커밋

} catch (e: Exception) {
    connection.rollback()  // 오류 시 롤백
    throw e
} finally {
    connection.close()
}

 

💯 성능과 안정성 테스트

동시성 테스트

여러 트랜잭션이 동시에 실행될 때:

  • 데드락 감지: 무한 대기 방지
  • 락 타임아웃: 일정 시간 후 자동 롤백
  • 격리 수준별 동작: 각 격리 수준에서의 일관성 보장

복구 테스트

시스템 크래시 시나리오:

  • 체크포인트 복구: 마지막 체크포인트부터 복구
  • 로그 재실행: 커밋된 트랜잭션 재실행
  • 롤백 복구: 미커밋 트랜잭션 롤백

 

🚶🏻 향후 발전 방향

  • 현재는 Embedded DB 로만 동작할 수 있기에 단독 DB 서버로서 동작할 수 있도록 발전시키고 싶습니다.
    • 현재는 서버 내부에서 파일 시스템을 활용해 동작 중입니다.
  • 세부 구현들이 아직 실제 DBMS 수준에 미치지 못한 경우가 있어서 보완하고 싶습니다.

 

🤝🏻 Maven & Gradle 배포

  • Maven Repository에 Public하게 배포하는 방법은 절차가 다소 길기에 GitHub Package로 우선 배포해보았습니다.

 

👍🏻 후기

  • 상상만 하지 않고 실현했다는 점이 가장 의미 깊었습니다.
  • 그 외로 DBMS의 내부 구현에 대해 더 깊게 알게 되었고, 상용 DBMS 제품들은 정말 많은 고민이 들어간 시스템이라는 것을 다시 한 번 깨닫게 되었습니다.

 

공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
«   2025/07   »
1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30 31
글 보관함