본문 바로가기
1. 개발/1.8. ActiveMQ

트랜잭션 로그(Transaction Log)가 저장소에 기록되는 시점은?

by 엉짱 2026. 3. 27.
반응형

트랜잭션 로그(Transaction Log)가 저장소에 기록되는 시점은?

엔터프라이즈 시스템에서 데이터베이스나 고성능 메시지 브로커(ActiveMQ, Artemis 등)를 운영할 때, 시스템의 성능과 데이터의 생명(영속성)을 동시에 결정짓는 가장 중요한 컴포넌트가 바로 트랜잭션 로그(Transaction Log)입니다. 시스템에 따라 저널(Journal), WAL(Write-Ahead Log), 또는 리두 로그(Redo Log) 등 다양한 이름으로 불리지만, 그 본질적인 역할은 동일합니다.

우리는 흔히 "클라이언트가 데이터를 전송하고 완료 응답을 받으면 디스크에 저장된 것이다"라고 믿습니다. 하지만 이는 반은 맞고 반은 틀린 이야기입니다. 소프트웨어와 하드웨어 사이에는 속도 차이를 극복하기 위한 수많은 버퍼(Buffer)가 존재하기 때문입니다.

이 가이드에서는 데이터가 메모리를 떠나 물리적인 비휘발성 저장소(디스크)의 트랜잭션 로그에 완벽하게 새겨지는 '정확한 시점'들과, 그 이면에서 동작하는 아키텍처의 메커니즘을 상세히 해부합니다.


1. Write-Ahead Logging (WAL)의 대원칙

기록 시점을 논하기 전에 트랜잭션 로그의 대원칙인 WAL(미리 쓰기 로그) 개념을 이해해야 합니다.

데이터베이스의 원본 테이블 파일이나 브로커의 B-Tree 인덱스 파일은 무겁고 복잡한 구조를 가지고 있어 디스크의 무작위 쓰기(Random Write)를 유발합니다. 따라서 시스템은 이 무거운 원본 파일을 매번 수정하는 대신, "내가 지금부터 어떤 데이터를 어떻게 변경할 것이다"라는 작업 내역(이벤트)만을 가벼운 텍스트 형태의 로그 파일에 순차적으로(Append-only) 먼저 기록합니다.

원본 데이터 파일의 수정은 한참 뒤에 백그라운드에서 천천히 이루어지더라도, 이 '트랜잭션 로그'만 디스크에 안전하게 기록되어 있다면 서버의 전원이 차단되더라도 재부팅 시 로그를 순서대로 다시 실행(Replay)하여 데이터를 100% 복구할 수 있습니다. 즉, 시스템의 영속성은 원본 데이터 파일이 아닌 '트랜잭션 로그 파일의 동기화 상태'에 전적으로 의존합니다.


2. 물리적 디스크 I/O가 발생하는 3가지 결정적 시점

애플리케이션 스레드가 생성한 트랜잭션 로그는 OS의 메모리(페이지 캐시)에 임시로 머물다가, 다음의 3가지 특정 조건이 만족되는 순간 물리적 디스크 컨트롤러로 강제 플러시(Flush / fsync) 됩니다.

A. 트랜잭션의 명시적 커밋 (Commit) 시점
가장 중요하고 보편적인 기록 시점입니다. 클라이언트가 여러 개의 메시지를 보내거나 DB 작업을 수행한 뒤 commit() 명령을 호출하는 바로 그 순간입니다.
시스템은 데이터의 ACID(원자성, 일관성, 격리성, 영속성) 속성 중 영속성(Durability)을 보장하기 위해, 메모리 버퍼에 쌓여있던 해당 트랜잭션의 모든 로그 데이터를 물리적 디스크에 내려씁니다. 이 디스크 쓰기 작업이 하드웨어 레벨에서 완전히 끝나야만 클라이언트에게 "커밋 성공" 응답을 반환합니다.

B. 로그 버퍼(Log Buffer)의 임계치 초과 시점
시스템은 커밋이 발생하지 않은 트랜잭션(In-flight Transaction)의 로그들도 메모리 버퍼에 쌓아둡니다. 하지만 메모리는 무한하지 않습니다.
지정된 로그 버퍼의 크기(예: InnoDB의 innodb_log_buffer_size, 혹은 브로커의 내부 저널 버퍼)가 가득 차게 되면, 시스템은 아직 커밋되지 않은 트랜잭션들의 로그까지도 미리 디스크로 밀어냅니다. (단, 이 데이터들은 커밋 마커가 없으므로 장애 복구 시에는 무시되거나 롤백됩니다.)

C. 백그라운드 타이머에 의한 주기적 플러시 시점
트랜잭션이 오랫동안 열려있거나, 비동기(Async) 커밋 모드로 작동하는 경우, 시스템의 백그라운드 스레드가 개입합니다. OS나 애플리케이션 데몬은 주기적으로(예: 1초에 한 번씩) 타이머를 돌리며 메모리에 남아있는 모든 로그 데이터를 디스크로 강제 동기화하여 예기치 않은 데이터 유실 창(Window of Vulnerability)을 최소화합니다.


3. 메시지 브로커의 극단적 성능 최적화: '비동기 저널링'

ActiveMQ Artemis와 같은 고성능 메시지 브로커에서는 모든 커밋마다 디스크 플러시를 기다리는 것(A 시점)이 초당 수만 건의 메시지를 처리하는 데 있어 가장 큰 병목이 됩니다. 이를 우회하기 위해 브로커는 스토리지 설정(broker.xml)에서 기록 시점을 조작할 수 있는 강력한 통제권을 제공합니다.

  • journal-sync-transactional="true" (동기 커밋 모드):
    전통적인 방식입니다. 트랜잭션 커밋이 요청되면 디스크에 로그가 쓰일 때까지 스레드가 블로킹(Blocking)됩니다. 절대적인 데이터 무결성이 보장되지만 I/O 대기 시간만큼 전체 처리량(TPS)이 떨어집니다.
  • journal-sync-transactional="false" (비동기 커밋 모드):
    브로커 튜닝의 마법이자 양날의 검입니다. 커밋 요청이 들어오면 브로커는 OS 메모리 버퍼에 로그를 얹어두기만 하고 클라이언트에게 즉시 성공 응답을 보냅니다. 실제 디스크 쓰기는 OS의 스케줄링이나 브로커의 내부 타이머(수 밀리초 단위)에 의해 백그라운드에서 나중에 일괄적으로 이루어집니다.
    속도는 메모리 속도에 필적할 만큼 폭발적으로 증가하지만, 서버 전원이 갑자기 차단되면 "클라이언트는 성공 응답을 받았지만 디스크에는 없는" 데이터 증발 현상이 발생합니다.

4. 성능과 정합성의 타협점: '그룹 커밋(Group Commit)' 매커니즘

동기 모드의 신뢰성과 비동기 모드의 속도를 모두 쟁취하기 위해 현대 아키텍처가 도입한 기술이 바로 그룹 커밋(Group Commit)입니다.

디스크 I/O에서 가장 시간이 오래 걸리는 작업은 디스크 헤더를 움직이고 동기화 명령(fsync)을 내리는 오버헤드입니다.
그룹 커밋은 커밋 요청이 들어올 때마다 즉시 디스크로 달려가는 대신, 아주 찰나의 시간(예: 수 밀리초) 동안 스레드를 잠깐 멈추고 대기실(큐)에 다른 트랜잭션들의 커밋 요청들을 모읍니다.

수십 개의 커밋 요청이 모이면, 시스템은 단 한 번의 디스크 쓰기 연산으로 수십 개의 트랜잭션 로그를 한꺼번에 디스크에 플러시합니다. 개별 클라이언트 입장에서는 응답 시간이 1~2밀리초 정도 지연되지만, 디스크 I/O 횟수가 수십 분의 일로 줄어들면서 브로커 서버 전체의 초당 처리량(Throughput)은 수십 배 증가하는 아키텍처적 기적이 일어납니다.


5. 아키텍처 설계 시의 트레이드오프와 결단

결국 트랜잭션 로그가 디스크에 기록되는 시점은 인프라 엔지니어의 비즈니스적 결단에 따라 달라집니다.

  • 결제, 금융 시스템: 데이터 유실은 기업의 파산으로 직결됩니다. 반드시 동기 커밋 모드를 활성화하여 트랜잭션 커밋과 디스크 쓰기 시점을 완벽하게 일치시켜야 합니다. 고성능 NVMe SSD와 그룹 커밋 튜닝을 통해 I/O 병목을 하드웨어 스펙으로 극복해야 합니다.
  • 로그 수집, 단순 알림 시스템: 10만 건 중 1~2건의 데이터가 유실되어도 비즈니스에 치명적이지 않다면, 비동기 커밋 모드로 설정하여 로그의 기록 시점을 OS의 페이지 캐시 플러시 시점으로 지연시킵니다. 이를 통해 최소한의 하드웨어로 극한의 데이터 흡수 능력을 발휘할 수 있습니다.

결론적으로 트랜잭션 로그의 기록 시점은 고정된 물리적 법칙이 아니라, 비즈니스의 중요도에 따라 튜닝 가능한 아키텍처의 핵심 레버입니다. 브로커나 데이터베이스가 제공하는 동기화 옵션(fsync 타이밍)을 완벽히 이해하고 시스템에 가장 적합한 방어선을 구축하시기 바랍니다.

반응형