본문 바로가기

데이터베이스

(11)
[PostgreSQL] MVCC 완전 정복 설계 배경 — 읽기와 쓰기가 서로를 막지 않아야 한다전통적인 동시성 제어 방식인 2PL(Two-Phase Locking)은 명쾌하다. 읽기는 공유 락을, 쓰기는 배타 락을 잡는다. 충돌하는 연산은 서로를 기다리게 만들어 실행 순서를 강제로 직렬화한다. 그러나 실제 운영 환경에서는 치명적인 문제가 드러난다. 읽기가 쓰기를 막고, 쓰기가 읽기를 막으면서 트래픽이 조금만 몰려도 대기 체인이 폭발적으로 늘어난다. 오래 걸리는 리포트 쿼리 하나가 OLTP(Online Transaction Processing, 짧고 빈번한 쓰기·읽기가 뒤섞인 실시간 거래 처리) 쓰기 전체를 멈추게 하는 장애가 반복됐다. PostgreSQL의 전신인 Berkeley POSTGRES는 1980년대 후반 이 문제에 근본적으로 다른 해답..
[PostgreSQL] PostgreSQL이 메모리를 관리하는 법 - Shared Buffer Cache & Clock Sweep 한 줄 요약: PostgreSQL은 OS의 page cache를 믿지 않고, shared_buffers로 지정한 메모리에 자체 버퍼 풀을 운영한다. 어떤 페이지를 내보낼지는 LRU가 아닌 Clock Sweep이 결정한다.설계 배경 — OS page cache가 있는데 왜 또 버퍼가 필요한가파일 I/O를 하면 운영체제는 자동으로 읽은 블록을 page cache에 보관한다. 같은 블록을 다시 읽으면 디스크에 가지 않고 메모리에서 돌려준다. 그렇다면 PostgreSQL이 shared_buffers라는 별도 버퍼 풀을 유지하는 이유는 무엇인가?첫째, OS page cache는 WAL 선행 원칙을 알지 못한다. PostgreSQL은 dirty page를 디스크에 쓰기 전에 반드시 해당 페이지 변경을 기술한 WAL..
[PostgreSQL] 스토리지 레이어: 페이지 레이아웃 & TOAST 왜 페이지가 8KB인가PostgreSQL의 모든 테이블 데이터는 8KB 고정 크기 블록(페이지) 단위로 관리된다. 행이 1바이트짜리 값 하나를 담든, 수백 컬럼을 담든 관계없이 디스크 I/O의 최소 단위는 항상 8KB다. 이 결정은 PostgreSQL 초창기에 내려졌고, 오늘날까지 기본값으로 이어진다. 왜 8KB인가? 답은 세 가지 트레이드오프에 있다.행 크기와 페이지 크기의 균형이다.4KB 페이지를 쓰면 텍스트 컬럼 몇 개만 있어도 행 하나가 페이지를 꽉 채워 버리고, 페이지당 행이 하나뿐인 극단적인 상황이 생긴다. 반면 32KB처럼 너무 크면 단 한 행을 읽으려 해도 불필요하게 많은 데이터를 메모리에 올려야 한다.8KB는 "웬만한 행은 한 페이지 안에 담기고, 메모리 낭비도 크지 않다"는 실용적 타협..
[PostgreSQL] 프로세스 아키텍처 & 공유 메모리 PostgreSQL은 프로세스인가많은 현대 데이터베이스는 멀티스레드 모델을 채택한다. MySQL의 InnoDB, MongoDB, SQL Server 모두 하나의 프로세스 안에서 여러 스레드가 클라이언트 요청을 처리한다. 그런데 PostgreSQL은 정반대다. 클라이언트가 접속할 때마다 OS 수준의 새로운 프로세스가 fork()되어 그 연결을 전담한다. 이 설계는 1986년 UC Berkeley에서 시작된 POSTGRES 프로젝트의 유산이다. 당시는 POSIX 스레드 표준이 정립되기 전이었고, UNIX 계열 시스템 간에 스레드 API가 통일되어 있지 않았다. 가장 이식성 높은 동시성 단위는 fork() 한 번으로 복제되는 프로세스뿐이었다. 하지만 30년이 지난 지금까지 PostgreSQL이 프로세스 모델을..
[데이터베이스] 데이터베이스 복구 메커니즘 — Redo, Undo, Checkpoint 트랜잭션을 커밋했다. 그런데 그 직후 서버가 죽었다. 데이터는 살아있는가?이 질문에 "그렇다"고 답할 수 있으려면 데이터베이스가 내부적으로 어떤 메커니즘을 갖추고 있는지 이해해야 한다. ACID의 D, Durability(지속성)는 그냥 선언이 아니다. Redo Log, Undo Log, Checkpoint라는 구체적인 구현이 그것을 보장한다.ACID와 복구의 관계ACID에서 Undo, Redo와 직접 연결된 속성은 세 가지다. Atomicity(원자성):트랜잭션은 전부 반영되거나 전혀 반영되지 않아야 한다. 중간 상태로 남으면 안 된다. 트랜잭션 도중 실패했을 때 지금까지의 변경을 모두 되돌려야 하는데, 이것이 Undo Log의 첫 번째 역할이다.Isolation(고립성):트랜잭션이 진행 중인 동안 다..
[MySQL] 트랜잭션 격리가 구현되는 원리 — InnoDB MVCC 데이터베이스를 다루는 개발자라면 트랜잭션 격리 수준을 한 번쯤 공부한다. REPEATABLE READ에서는 Phantom Read가 발생할 수 있고, READ COMMITTED에서는 Non-repeatable Read가 발생한다. 이 정도는 많이 알고 있지만 "왜 그런가"를 설명할 수 있는 사람은 훨씬 적다. 격리 수준은 단순한 설정 플래그가 아니다. 그 아래에는 InnoDB가 데이터를 버전 단위로 관리하는 정교한 메커니즘이 있다. 이것이 MVCC(Multi-Version Concurrency Control)다. MVCC를 이해하면 격리 수준의 동작이 "그냥 그렇게 되는 것"이 아니라 필연적인 결과임을 알게 된다. 그리고 READ COMMITTED와 REPEATABLE READ를 언제 선택해야 하는지, ..
[MySQL] MySQL Primary-Replica: 내부 동작 원리부터 운영까지 이번 글에서는 복제가 내부적으로 어떻게 동작하는지에 대해 네트워크 프로토콜 수준부터 스레드 아키텍처, GTID 복구 메커니즘, 장애 시나리오별 동작, 그리고 실제 운영에서 놓치기 쉬운 함정들까지 깊게 다룬다.Replica 복제는 어떤 방식으로 어떻게 전송되는가1. MySQL 복제는 TCP 위의 이벤트 스트리밍많은 사람들이 복제를 "주기적으로 변경 내역을 가져오는 것"으로 막연하게 이해한다.실제로는 TCP 위에서 동작하는 MySQL 고유의 Binary Protocol로 이벤트를 스트리밍하는 구조다.OSI 계층 관점:Layer 7 (Application) → MySQL Binary Protocol (COM_BINLOG_DUMP_GTID)Layer 6 (Presentation) → 선택적 압축 (zstd /..
[MySQL] Container MySQL Primary-Replica 셋팅 읽기와 쓰기가 같은 DB 인스턴스를 공유하면 검색 쿼리가 커넥션과 CPU를 독점하여 쓰기 트랜잭션이 타임아웃된다.이 문서는 기존 데이터를 유지하면서 단일 MySQL 컨테이너를 Primary(쓰기 전용) - Replica(읽기 전용) 구조로 전환하는 전 과정을 다룬다.아키텍처 개요[Application] │ ├── Write (INSERT/UPDATE/DELETE) → [Primary :3306] 쓰기 전용 └── Read (SELECT) → [Replica :3307] 읽기 전용[Primary] Binlog(ROW) ──── 비동기 복제 ────→ [Replica] max_connections=60 max_connection..