운영체제와 DBMS가 함께 만드는 트랜잭션 보장

ACID, 잠금, MVCC, WAL을 중심으로 운영체제와 DBMS가 동시성·영속성·복구를 나누어 보장하는 방식을 정리한다.

2026-08-14 · 최초 발행 2026-01-19

ACID는 DBMS만의 기능으로 끝나지 않는다

데이터베이스는 운영체제 위에서 동작하지만, 동시성 제어와 트랜잭션 관리는 별도의 계층으로 구현한다. DBMS는 자체 잠금, 로깅, 복구 체계를 갖추고 OS의 프로세스 관리, 파일 시스템, 메모리 관리 기능을 이용한다. 이 둘의 역할 분담과 충돌 지점이 성능과 안정성을 좌우한다.

ACID 속성Atomicity(원자성)Consistency(일관성)Isolation(격리성)Durability(영속성)DBMS: 로깅, Undo/RedoDBMS: 제약 조건 검증DBMS: 잠금, MVCCOS+DBMS: fsync, WAL

원자성은 트랜잭션을 전부 수행하거나 전혀 반영하지 않는 성질이다. DBMS는 Write-Ahead Logging(WAL)과 롤백 로그를 담당하고, OS는 원자적 쓰기와 파일 시스템 저널링으로 이를 지원한다.

일관성은 무결성 제약 조건이 유지되는 상태를 뜻한다. 외래키, 트리거, 체크 제약은 DBMS가 주도하며, OS는 데이터 손상을 막는 기반을 제공한다.

격리성에서는 잠금과 MVCC(Multi-Version Concurrency Control)가 동시 트랜잭션의 간섭을 제어한다. OS의 스레드·프로세스 동기화와 메모리 보호도 이 계층을 뒷받침한다.

영속성은 커밋된 트랜잭션을 영구 저장하는 문제다. DBMS의 WAL과 OS의 fsync/fdatasync가 연결되고, 디스크 쓰기 보장과 파일 시스템 일관성은 OS 책임에 속한다.

논리적 잠금과 OS 동기화가 만나는 지점

DBMS 잠금(논리적)테이블 잠금 잠금인덱스 잠금OS 잠금(물리적)뮤텍스세마포어파일 잠금(fcntl)

DBMS 잠금은 행, 페이지, 테이블처럼 데이터의 논리적 단위를 대상으로 한다. 공유 잠금(S), 배타 잠금(X), 의도 잠금(IS, IX)을 구분하며, 대기 그래프와 타임아웃으로 데드락을 감지한다. 필요하면 잠금 범위를 행에서 페이지, 테이블로 에스컬레이션한다.

반면 OS 수준의 잠금은 DBMS 내부 자료구조를 보호한다. 버퍼 풀, 로그 버퍼, 해시 테이블, LRU 리스트, 파일 디스크립터 테이블이 대표적인 대상이다.

2단계 잠금 프로토콜(2PL)은 잠금 획득 단계와 해제 단계를 분리한다.

데이터잠금 관리자트랜잭션데이터잠금 관리자트랜잭션Growing PhaseShrinking Phase잠금 획득 (A)승인읽기 A잠금 획득 (B)승인쓰기 B잠금 해제 (A)잠금 해제 (B)COMMIT

Strict 2PL은 모든 배타 잠금을 COMMIT 또는 ABORT 시점까지 유지한다. 이 방식은 Cascading Abort를 막으며, 대부분의 DBMS가 채택한다.

잠금을 기다리는 트랜잭션은 OS에서 블록 상태가 되고, 잠금 해제 뒤에는 OS가 대기 스레드를 깨운다. 따라서 스케줄링 지연은 트랜잭션 처리율에도 영향을 준다.

MVCC의 버전 관리와 메모리 계층

포인터가시성 체크현재 버전(V3, XID=300)이전 버전(V2, XID=200) 이전(V1, XID=100)읽기 트랜잭션(XID=250)

PostgreSQL은 행마다 xmin(생성 트랜잭션 ID)과 xmax(삭제 트랜잭션 ID)를 기록하고, 스냅샷을 기준으로 가시성을 판단한다. 읽기와 쓰기의 충돌이 없다는 장점이 있지만, 오래된 버전은 VACUUM으로 정리해야 한다.

MySQL InnoDB는 Undo 로그에 이전 버전을 보관한다. Read View에는 트랜잭션 시작 시점의 활성 트랜잭션 목록이 담기며, MVCC는 잠금 프로토콜과 함께 사용된다.

버퍼(공유 메모리)페이지 버전 1페이지 버전 2OS 페이지 캐시디스크mmap 또는 read/write

DBMS는 LRU, Clock 같은 자체 페이지 교체 알고리즘으로 버퍼 풀을 관리한다. 이 버퍼 풀은 OS 페이지 캐시와 중복될 수 있어 메모리 낭비가 생기며, Direct I/O(O_DIRECT)로 OS 캐시를 우회할 수 있다.

MVCC의 새 버전 생성과 기존 버전 보존은 OS의 CoW(fork, mmap)와 유사한 면이 있다. 이 구조에서는 Undo 로그 공간 관리가 중요하다.

WAL이 fsync까지 이어져야 하는 이유

데이터 페이지로그 디스크WAL 버퍼트랜잭션데이터 페이지로그 디스크WAL 버퍼트랜잭션로그 영속화 완료(1) 로그 레코드 기록(2) fsync (강제 플러시)(3) COMMIT 성공(4) 데이터 페이지 수정 (지연)(5) 체크포인트 시 쓰기

WAL은 데이터 페이지를 디스크에 쓰기 전에 로그를 먼저 기록하고, 트랜잭션 커밋 전에 로그를 fsync하는 규칙이다. Undo 로그는 ABORT 시 롤백할 이전 값을, Redo 로그는 복구 시 다시 실행할 새 값을 담는다. ARIES 알고리즘은 Undo와 Redo를 결합한 산업 표준이다.

// PostgreSQL의 WAL 쓰기 예시
int fd = open("pg_wal/000000010000000000000001", O_WRONLY);
write(fd, wal_buffer, WAL_SEGMENT_SIZE);
fsync(fd);  // 디스크 쓰기 보장

// fdatasync: 메타데이터 제외 (더 빠름)
fdatasync(fd);

쓰기 캐시가 있는 디스크 컨트롤러에서는 전력 손실 시 데이터가 사라질 수 있다. RAID 컨트롤러의 BBU(Battery Backup Unit), NVMe 명령어를 통한 플러시 확인도 이 문제와 맞닿아 있다.

파일 시스템도 쓰기 순서에 영향을 준다. ext4의 data=ordered는 메타데이터만 저널링하고, data=journal은 데이터까지 저널링해 이중 쓰기가 발생한다. XFS와 Btrfs는 트랜잭션 파일 시스템이다.

I/O 경로를 고를 때의 트레이드오프

// O_DIRECT 플래그 사용 (Linux)
int fd = open("data.db", O_RDWR | O_DIRECT);
posix_memalign(&buf, 4096, BUF_SIZE);  // 정렬된 버퍼
pread(fd, buf, 8192, offset);  // 페이지 캐시 우회

Direct I/O를 쓰면 DBMS가 LRU와 프리페칭을 완전히 제어하고 메모리 중복을 줄일 수 있다. 대신 섹터 경계에 맞춘 정렬이 필요하고 OS의 read-ahead 최적화는 비활성화되며 구현 복잡도도 커진다.

io_submitio_geteventsDBMS 워커 스레드AIO 요청 제출커널 AIO 서브시스템I/O 완료디스크

Linux AIO(libaio)는 여러 I/O 요청을 동시에 발행한 뒤 대기 없이 다른 작업을 수행하고, 완료 이벤트를 폴링한다. io_uring은 공유 메모리 링 버퍼로 시스템 콜 오버헤드를 줄이며 PostgreSQL 13+에서 실험적으로 지원된다.

// SQLite의 mmap 사용
void *map = mmap(NULL, file_size, PROT_READ | PROT_WRITE,
                 MAP_SHARED, fd, 0);
// 파일이 메모리처럼 접근 가능
int *value = (int *)(map + offset);
*value = 42;  // 직접 쓰기

msync(map, length, MS_SYNC);  // 영속화

mmap 기반 접근은 read/write 호출 없이 파일을 메모리처럼 다룰 수 있고 OS 페이지 폴트를 활용한다. 하지만 SIGBUS에 따른 에러 처리가 어렵고, 64비트 주소 공간이 필요하며 WAL 구현도 복잡해진다.

버퍼·파일 시스템·스케줄러의 간섭

디스크(100GB DB)OS 페이지 캐시(20GB)DBMS 버퍼(20GB)중복!(메모리 낭비)

DBMS 버퍼 풀과 OS 페이지 캐시가 같은 데이터를 잡으면 중복이 생긴다. Direct I/O로 OS 캐시를 우회하거나, 버퍼 풀 크기를 줄이고 OS 캐시를 활용하는 방식으로 조정할 수 있다. MongoDB WiredTiger는 OS 캐시 의존도가 높다.

파일 시스템 단편화도 데이터베이스 I/O에 영향을 준다. 순차 쓰기가 랜덤 쓰기로 변질될 수 있고 WAL 파일 성장이나 테이블스페이스 확장이 원인이 된다. fallocate(2)로 사전 할당하고, XFS의 allocsize 옵션이나 pg_repack, OPTIMIZE TABLE 같은 주기적 재구성을 활용할 수 있다.

# DBMS 프로세스 우선순위 조정
nice -n -10 postgres

# CPU 피닝 (NUMA 고려)
taskset -c 0-7 mysqld

# 실시간 스케줄링 (주의 필요)
chrt -f 50 oracle

우선순위를 과도하게 올리면 시스템 안정성을 해칠 수 있다. NUMA 노드 경계를 넘는 메모리 접근을 피하고, cgroup으로 자원을 격리하는 점도 함께 고려한다.

분산 환경에서 트랜잭션을 조율하는 방식

참여자 2참여자 1코디네이터참여자 2참여자 1코디네이터Phase 1 (Prepare)Phase 2 (Commit)PREPAREPREPARE로그 기록, 잠금 유지로그 기록, 잠금 유지READYREADY결정 로그 기록COMMITCOMMIT커밋, 잠금 해제커밋, 잠금 해제ACKACK

2단계 커밋(2PC)은 TCP 소켓과 gRPC 같은 네트워크 통신, OS 타이머를 통한 타임아웃 관리, keepalive와 하트비트에 의한 장애 감지에 의존한다. 코디네이터가 실패하면 참여자가 대기하는 블로킹 문제가 있으며, 네트워크 라운드트립은 2회 발생한다.

Google Spanner는 TrueTime API(원자 시계)를, CockroachDB는 Raft 기반 복제를, Kafka는 Zookeeper 또는 KRaft 메타데이터 관리를 사용한다. 시간 동기화에서는 NTP가 밀리초 정확도, PTP(Precision Time Protocol)가 마이크로초를 제공하며, Spanner의 GPS+원자시계는 7ms 오차 한계를 둔다.

장애 뒤에는 로그와 파일 시스템이 함께 복구한다

시스템 크래시재시작Analysis Pass(체크포인트 분석)Redo Pass(로그 재실행)Undo Pass(미완료 트랜잭션 롤백)정상 운영

ARIES는 마지막 체크포인트부터 로그를 스캔해 Dirty Page Table과 Active Transaction Table을 재구성한다. Redo 단계는 커밋 여부와 관계없이 모든 업데이트를 재실행하며, 여러 번 실행해도 결과가 같은 Idempotent 성질을 이용한다. Undo 단계에서는 미완료 트랜잭션을 로그 역순으로 롤백하고 Compensation Log Record(CLR)를 기록한다.

ext4는 메타데이터를 빠르게 복구하지만 데이터 손상은 DBMS가 체크섬으로 검출한다. Btrfs와 ZFS는 스냅샷과 체크섬을 내장한 Copy-On-Write 파일 시스템이므로 DBMS 기능과 겹치는 부분을 설정에서 조율해야 한다.

커널·디스크·잠금 상태를 함께 관찰하기

# Linux sysctl 설정
# 공유 메모리 크기 (PostgreSQL)
sysctl -w kernel.shmmax=17179869184
sysctl -w kernel.shmall=4194304

# 더티 페이지 플러시 지연
sysctl -w vm.dirty_ratio=15
sysctl -w vm.dirty_background_ratio=5

# 스왑 비활성화 (DBMS용)
sysctl -w vm.swappiness=0

# TCP 튜닝 (분산 DB)
sysctl -w net.core.somaxconn=4096
sysctl -w net.ipv4.tcp_max_syn_backlog=4096
# deadline 또는 noop 스케줄러 (SSD)
echo deadline > /sys/block/nvme0n1/queue/scheduler

# 큐 깊이 증가
echo 1024 > /sys/block/nvme0n1/queue/nr_requests
-- PostgreSQL 통계
SELECT * FROM pg_stat_database;
SELECT * FROM pg_stat_bgwriter;

-- InnoDB 상태
SHOW ENGINE INNODB STATUS;

-- 잠금 대기
SELECT * FROM pg_locks WHERE NOT granted;

OS 레벨에서는 iostat으로 디스크 I/O 대기를, vmstat으로 스왑과 컨텍스트 스위칭을 확인할 수 있다. perf는 CPU 프로파일링에, eBPF는 bpftrace를 통한 커널 추적에 사용한다.

DBMS는 고수준 트랜잭션 의미론을 제공하지만, 최종 영속성과 성능은 OS의 파일 시스템, 메모리 관리, 스케줄링에 의존한다. WAL과 fsync의 조합은 영속성을 보장하고, MVCC는 OS 메모리 관리와 유사한 버전 관리 방식을 취한다. Direct I/O와 AIO는 성능을 높이기 위한 대표적인 협력 지점이며, NVMe, 영구 메모리(Optane), eBPF 기반 커널 우회 기술은 이 관계를 더욱 긴밀하게 만들 것이다.

운영체제데이터베이스트랜잭션ACIDWALMVCC