---
title: "InnoDB Log Buffer와 커밋 지연: flush 정책과 innodb_flush_log_at_trx_commit"
description: "InnoDB Log Buffer가 redo log 기록과 commit latency에 미치는 영향, flush 정책 선택 기준을 운영 관점에서 정리한다."
tags: [ MySQL, InnoDB, 트랜잭션, 운영 ]
image: "mysql-report-bg.png"
published: "2026-06-06"
updated: "2026-06-06"
author: "MySQL 기술 노트"
source_url: ""
---

## 1. 왜 Log Buffer를 별도로 이해해야 하는가

InnoDB에서 데이터 변경은 곧바로 tablespace page에만 반영되는 작업이 아니다. 트랜잭션이 `INSERT`, `UPDATE`, `DELETE`를 수행하면 buffer pool의 data page가 변경되고, 동시에 그 변경을 복구할 수 있는 redo record가 생성된다. 이 redo record는 먼저 메모리 영역인 InnoDB Log Buffer에 쌓인 뒤 redo log file로 기록되고, 필요 시 운영체제와 storage 장치에 flush된다.

운영자가 Log Buffer를 이해해야 하는 이유는 commit latency와 durability가 이 경로에서 결정되기 때문이다. 애플리케이션 입장에서는 `COMMIT`이 빠르면 쓰기 성능이 좋아 보인다. 그러나 `COMMIT`이 어느 시점에 반환되는지는 redo log가 단순히 메모리에 있는지, OS page cache까지 쓰였는지, 물리 storage에 flush되었는지에 따라 의미가 달라진다. `innodb_flush_log_at_trx_commit`은 이 경계선을 제어하는 핵심 변수다.

Log Buffer는 보통 문제 없이 동작하므로 평소에는 눈에 띄지 않는다. 하지만 다음과 같은 상황에서는 직접적인 성능·복구 이슈로 드러난다.

- 초당 commit 수가 높아져 redo log write 또는 fsync가 병목이 되는 경우
- 큰 트랜잭션이 Log Buffer를 빠르게 채워 중간 flush를 유발하는 경우
- `innodb_flush_log_at_trx_commit` 값을 성능 목적으로 완화했지만 장애 시 손실 범위를 정확히 이해하지 못한 경우
- binary log를 함께 사용하는 환경에서 `sync_binlog`와 InnoDB flush 정책의 조합이 일관되지 않은 경우
- Aurora MySQL처럼 storage 계층이 다르지만 여전히 commit 경로와 durability 의미를 구분해야 하는 경우

이 글은 Log Buffer를 단순한 “메모리 버퍼”로 보지 않고, redo 생성부터 commit 반환까지의 경로, flush 정책, 장애 시 손실 범위, 진단 방법을 운영 매뉴얼 관점에서 정리한다.

## 2. Redo log 경로에서 Log Buffer의 위치

InnoDB는 WAL(Write-Ahead Logging) 원칙을 사용한다. 변경된 data page를 디스크에 쓰기 전에, 그 변경을 재현할 수 있는 redo log가 먼저 안정적인 저장소에 기록되어야 한다. 이 원칙 때문에 InnoDB는 장애 후 crash recovery에서 redo log를 읽어 committed 변경을 복구할 수 있다.

단순화한 변경 경로는 다음과 같다.

```mermaid
flowchart TD
    A[SQL DML 실행] --> B[Buffer Pool의 page 변경]
    B --> C[Redo record 생성]
    C --> D[InnoDB Log Buffer]
    D --> E{flush 정책과 압력 판단}
    E -- commit 시점 --> F[Redo log file write]
    F --> G{fsync 또는 storage flush}
    G --> H[COMMIT 반환]
    E -- checkpoint/공간 압력 --> I[background flush]
    I --> F
    B --> J[Dirty page]
    J --> K[checkpoint에 따라 tablespace flush]
```

여기서 Log Buffer는 redo record의 임시 저장소다. 하지만 “임시”라는 표현이 중요도를 낮춘다는 뜻은 아니다. commit 시점에 redo record가 Log Buffer에만 남아 있으면 mysqld 프로세스 장애 또는 OS 장애 때 복구할 수 없을 수 있다. 반대로 매 commit마다 storage flush까지 완료하면 내구성은 강하지만 fsync 비용이 commit latency에 직접 반영된다.

또한 redo log와 data page flush는 같은 작업이 아니다. InnoDB는 트랜잭션 commit 때 변경된 data page를 반드시 즉시 tablespace에 쓰지 않는다. data page는 buffer pool에서 dirty page로 남고, checkpoint 진행과 background page cleaner에 의해 나중에 flush된다. commit durability를 보장하는 핵심은 data page 즉시 기록이 아니라 redo log의 안정적 기록이다.

## 3. Log Buffer 내부 동작과 commit 지연의 원인

Log Buffer에는 여러 사용자 thread가 생성한 redo record가 들어간다. InnoDB는 redo record에 LSN(Log Sequence Number)을 부여하고, Log Buffer에서 redo log file로 write할 위치를 관리한다. 운영자가 세부 구현을 모두 외울 필요는 없지만, 다음 네 가지 경계는 성능 해석에 중요하다.

1. **redo 생성**: 트랜잭션이 변경한 page에 대한 redo record가 만들어진다.
2. **log buffer append**: redo record가 메모리 Log Buffer에 복사된다.
3. **log write**: Log Buffer의 내용을 redo log file로 write system call을 통해 넘긴다.
4. **log flush/fsync**: OS page cache 또는 storage cache에 머물지 않도록 안정적 저장소로 flush한다.

`COMMIT` 지연은 이 중 어느 단계에서든 발생할 수 있다. 예를 들어 CPU가 부족하거나 내부 mutex 경합이 심하면 redo 생성과 append 자체가 느려질 수 있다. I/O subsystem이 느리면 log write 또는 fsync에서 대기한다. 큰 트랜잭션이 Log Buffer를 많이 사용하면 commit 전에도 Log Buffer 공간 확보를 위해 write가 필요할 수 있다.

Log Buffer 크기는 `innodb_log_buffer_size`로 제어한다. 이 값이 너무 작으면 큰 transaction이 commit 전에 Log Buffer 공간을 확보하려고 더 자주 flush를 유발할 수 있다. 그러나 값을 크게 한다고 모든 commit latency가 줄어드는 것은 아니다. 짧은 OLTP 트랜잭션에서 병목이 매 commit의 fsync라면 Log Buffer를 키워도 근본 원인은 storage flush 비용이다.

## 4. innodb_flush_log_at_trx_commit 값의 의미

`innodb_flush_log_at_trx_commit`은 commit 시 redo log를 어디까지 밀어 넣을지 제어한다. 운영 환경에서 가장 많이 논의되는 값은 `1`, `2`, `0`이다.

| 값 | commit 시 동작의 핵심 | 장애 시 손실 가능성 | 일반적 해석 |
|---:|---|---|---|
| `1` | 매 commit마다 redo log write와 flush를 수행 | 정상적인 storage 보장을 전제로 가장 작음 | 금융성·주문성·정합성 중심 OLTP의 기본값 |
| `2` | 매 commit마다 redo log file로 write하되 flush는 주기적으로 수행 | OS crash 또는 전원 장애 시 최근 트랜잭션 손실 가능 | 성능과 내구성 절충, 손실 허용 범위 명확해야 함 |
| `0` | commit마다 write/flush하지 않고 주기적으로 write/flush | mysqld crash에서도 최근 트랜잭션 손실 가능 | 일반 OLTP 기본값으로는 부적합, 특수 배치·캐시성 데이터에서만 검토 |

정확한 표현에서 주의할 점은 `write`와 `flush`를 구분하는 것이다. `write`는 보통 운영체제에 redo log file로 쓰도록 넘기는 단계이며, 아직 OS page cache나 storage cache에 있을 수 있다. `flush` 또는 fsync는 더 강한 내구성 경계다. storage controller, cloud volume, filesystem, mount option에 따라 실제 보장 수준은 달라질 수 있지만, MySQL 운영 정책을 세울 때는 이 경계부터 명확히 해야 한다.

기본값 `1`은 가장 보수적인 설정이다. 매 commit마다 fsync 비용을 지불하므로 쓰기 처리량이 storage latency에 민감하다. 반대로 `2`는 commit마다 OS로 write하되 flush는 보통 1초 단위 background 작업에 의존하므로 fsync 빈도를 줄일 수 있다. `0`은 commit마다 write조차 강제하지 않기 때문에 mysqld 프로세스 crash만으로도 최근 commit이 사라질 수 있다.

운영 정책에서 중요한 질문은 “몇 초 손실이 괜찮은가”가 아니라 “어떤 장애 범위에서 어떤 데이터 손실을 계약상, 업무상 허용할 수 있는가”다. 단순 조회 캐시, 재생성 가능한 통계, 임시 집계처럼 손실 허용 데이터라면 완화된 설정을 검토할 수 있다. 그러나 주문, 결제, 재고, 계정 잔액처럼 애플리케이션이 commit 반환을 영속성 승인으로 해석하는 데이터에는 기본값 `1`을 기준으로 설계해야 한다.

## 5. binary log와 sync_binlog까지 함께 보아야 하는 이유

복제나 point-in-time recovery를 사용하는 서버에서는 InnoDB redo log만 보면 부족하다. MySQL Server layer는 binary log를 기록하고, InnoDB는 redo log를 기록한다. crash-safe replication과 복구 일관성을 위해서는 두 로그의 commit 순서와 flush 정책이 함께 맞아야 한다.

일반적으로 강한 내구성을 원하면 다음 조합을 기준선으로 삼는다.

- `innodb_flush_log_at_trx_commit = 1`
- `sync_binlog = 1`

이 조합은 commit마다 InnoDB redo log와 binary log를 모두 안정적으로 flush하는 방향이다. 성능 비용은 증가하지만, source crash 후 binary log에는 남았는데 InnoDB에는 없거나, InnoDB에는 commit되었는데 binary log에는 없는 식의 불일치 위험을 낮춘다.

반대로 `innodb_flush_log_at_trx_commit=2`, `sync_binlog=0`처럼 완화된 조합은 commit latency를 줄일 수 있으나 장애 후 손실 범위와 복제 재시작 시나리오를 더 엄격히 검토해야 한다. 특히 외부 시스템이 binary log 기반 CDC, Debezium, DMS, audit pipeline을 사용한다면 binary log flush 정책은 단순 복제 성능 설정이 아니라 데이터 전달 보장 수준의 일부가 된다.

## 6. 현재 설정과 redo log 상태를 확인하는 기본 SQL

다음 SQL은 MySQL 8.0 테스트 인스턴스에서 실행 가능한 기본 점검이다. 운영 서버에서는 단일 값만 보지 말고 commit latency, I/O 대기, checkpoint 상황, binary log 설정과 함께 해석해야 한다.

```sql
SELECT VERSION() AS mysql_version;
SHOW VARIABLES WHERE Variable_name IN (
  'innodb_log_buffer_size',
  'innodb_flush_log_at_trx_commit',
  'sync_binlog',
  'innodb_redo_log_capacity'
);
SHOW GLOBAL STATUS WHERE Variable_name IN (
  'Innodb_log_waits',
  'Innodb_log_write_requests',
  'Innodb_log_writes',
  'Innodb_os_log_fsyncs',
  'Innodb_os_log_written'
);
```

실행 결과(MySQL 8.0.46):

```text
mysql> SELECT VERSION() AS mysql_version;

+---------------+
| mysql_version |
+---------------+
| 8.0.46        |
+---------------+
1 row in set (0.00 sec)

mysql> SHOW VARIABLES WHERE Variable_name IN (
    ->   'innodb_log_buffer_size',
    ->   'innodb_flush_log_at_trx_commit',
    ->   'sync_binlog',
    ->   'innodb_redo_log_capacity'
    -> );

+--------------------------------+-----------+
| Variable_name                  | Value     |
+--------------------------------+-----------+
| innodb_flush_log_at_trx_commit | 1         |
| innodb_log_buffer_size         | 16777216  |
| innodb_redo_log_capacity       | 104857600 |
| sync_binlog                    | 1         |
+--------------------------------+-----------+
4 rows in set (0.00 sec)

mysql> SHOW GLOBAL STATUS WHERE Variable_name IN (
    ->   'Innodb_log_waits',
    ->   'Innodb_log_write_requests',
    ->   'Innodb_log_writes',
    ->   'Innodb_os_log_fsyncs',
    ->   'Innodb_os_log_written'
    -> );

+---------------------------+-------+
| Variable_name             | Value |
+---------------------------+-------+
| Innodb_log_waits          | 0     |
| Innodb_log_write_requests | 837   |
| Innodb_log_writes         | 25    |
| Innodb_os_log_fsyncs      | 19    |
| Innodb_os_log_written     | 65536 |
+---------------------------+-------+
5 rows in set (0.00 sec)
```

`Innodb_log_waits`는 Log Buffer 공간이 부족해 기다린 횟수를 나타내는 대표 지표다. 값이 증가한다면 큰 트랜잭션, 너무 작은 Log Buffer, redo write 지연을 의심할 수 있다. 다만 이 값이 0이라고 commit latency 문제가 없다는 뜻은 아니다. 매 commit fsync가 느린 문제는 `Innodb_log_waits`가 아니라 I/O latency, statement latency, wait event에서 드러나는 경우가 많다.

`Innodb_log_write_requests`와 `Innodb_log_writes`의 비율은 많은 redo write request가 실제 log write로 어떻게 묶이는지 보는 참고 지표다. `Innodb_os_log_fsyncs`는 fsync 빈도를 이해하는 출발점이다. 이 값들은 누적 counter이므로 서버 uptime, workload 변화, 측정 구간을 반드시 함께 기록해야 한다.

## 7. 작은 트랜잭션 예제로 보는 redo 증가

다음 예제는 테스트 컨테이너에서 실행 가능한 재현형 SQL이다. 실제 운영의 redo 크기와 latency를 재현하려는 목적이 아니라, DML과 commit이 redo 관련 상태 counter를 증가시키는 흐름을 확인하기 위한 축소 예제다.

```sql
DROP TABLE IF EXISTS log_buffer_note_demo;
CREATE TABLE log_buffer_note_demo (
  id BIGINT NOT NULL AUTO_INCREMENT,
  payload VARCHAR(200) NOT NULL,
  created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (id)
) ENGINE=InnoDB;

SHOW GLOBAL STATUS WHERE Variable_name IN (
  'Innodb_log_write_requests',
  'Innodb_log_writes',
  'Innodb_os_log_written'
);

START TRANSACTION;
INSERT INTO log_buffer_note_demo (payload)
VALUES (REPEAT('redo-log-buffer-', 8)),
       (REPEAT('commit-path-', 10)),
       (REPEAT('flush-policy-', 10));
COMMIT;

SELECT COUNT(*) AS rows_after_commit
FROM log_buffer_note_demo;

SHOW GLOBAL STATUS WHERE Variable_name IN (
  'Innodb_log_write_requests',
  'Innodb_log_writes',
  'Innodb_os_log_written'
);

DROP TABLE log_buffer_note_demo;
```

실행 결과(MySQL 8.0.46):

```text
mysql> CREATE TABLE log_buffer_note_demo (... ) ENGINE=InnoDB;
Query OK, 0 rows affected (0.00 sec)

mysql> SHOW GLOBAL STATUS WHERE Variable_name IN (...);
+---------------------------+-------+
| Variable_name             | Value |
+---------------------------+-------+
| Innodb_log_write_requests | 987   |
| Innodb_log_writes         | 37    |
| Innodb_os_log_written     | 84480 |
+---------------------------+-------+
3 rows in set (0.00 sec)

mysql> START TRANSACTION;
Query OK, 0 rows affected (0.00 sec)

mysql> INSERT INTO log_buffer_note_demo (payload) VALUES (...);
Query OK, 3 rows affected (0.00 sec)
Records: 3  Duplicates: 0  Warnings: 0

mysql> COMMIT;
Query OK, 0 rows affected (0.00 sec)

mysql> SELECT COUNT(*) AS rows_after_commit FROM log_buffer_note_demo;
+-------------------+
| rows_after_commit |
+-------------------+
|                 3 |
+-------------------+
1 row in set (0.00 sec)

mysql> SHOW GLOBAL STATUS WHERE Variable_name IN (...);
+---------------------------+-------+
| Variable_name             | Value |
+---------------------------+-------+
| Innodb_log_write_requests | 1023  |
| Innodb_log_writes         | 42    |
| Innodb_os_log_written     | 90112 |
+---------------------------+-------+
3 rows in set (0.01 sec)

mysql> DROP TABLE log_buffer_note_demo;
Query OK, 0 rows affected (0.00 sec)
```

이 예제에서 핵심은 `COMMIT`이 data page를 즉시 tablespace에 모두 기록한다는 뜻이 아니라는 점이다. `INSERT`로 buffer pool의 page가 변경되고 redo가 생성되며, commit 정책에 따라 redo log write/flush가 수행된다. data page flush는 checkpoint와 dirty page 관리 흐름에서 별도로 진행된다.

## 8. performance_schema로 commit 대기를 볼 때의 접근

MySQL 8.0에서는 `performance_schema`의 wait event와 statement summary를 통해 commit 경로의 대기 성격을 추적할 수 있다. 다만 instrument 이름은 minor version과 활성화 상태에 따라 달라질 수 있으므로, 먼저 현재 인스턴스에 어떤 instrument가 있는지 확인하는 방식이 안전하다.

```sql
SELECT NAME, ENABLED, TIMED
FROM performance_schema.setup_instruments
WHERE NAME LIKE 'wait/io/file/innodb/%log%'
   OR NAME LIKE 'wait/synch/%/innodb/%log%'
ORDER BY NAME
LIMIT 30;

SELECT EVENT_NAME, COUNT_STAR, SUM_TIMER_WAIT
FROM performance_schema.events_waits_summary_global_by_event_name
WHERE EVENT_NAME LIKE 'wait/io/file/innodb/%log%'
   OR EVENT_NAME LIKE 'wait/synch/%/innodb/%log%'
ORDER BY SUM_TIMER_WAIT DESC
LIMIT 10;
```

실행 결과(MySQL 8.0.46):

```text
mysql> SELECT NAME, ENABLED, TIMED
    -> FROM performance_schema.setup_instruments
    -> WHERE NAME LIKE 'wait/io/file/innodb/%log%'
    ->    OR NAME LIKE 'wait/synch/%/innodb/%log%'
    -> ORDER BY NAME
    -> LIMIT 30;
+--------------------------------------------------+---------+-------+
| NAME                                             | ENABLED | TIMED |
+--------------------------------------------------+---------+-------+
| wait/io/file/innodb/innodb_log_file              | YES     | YES   |
| wait/io/file/innodb/meb::redo_log_archive_file   | YES     | YES   |
| wait/synch/mutex/innodb/log_checkpointer_mutex   | NO      | NO    |
| wait/synch/mutex/innodb/log_closer_mutex         | NO      | NO    |
| wait/synch/mutex/innodb/log_cmdq_mutex           | NO      | NO    |
| wait/synch/mutex/innodb/log_files_mutex          | NO      | NO    |
| wait/synch/mutex/innodb/log_flush_notifier_mutex | NO      | NO    |
| wait/synch/mutex/innodb/log_flusher_mutex        | NO      | NO    |
| wait/synch/mutex/innodb/log_limits_mutex         | NO      | NO    |
| wait/synch/mutex/innodb/log_sn_mutex             | NO      | NO    |
| wait/synch/mutex/innodb/log_sys_arch_mutex       | NO      | NO    |
| wait/synch/mutex/innodb/log_write_notifier_mutex | NO      | NO    |
| wait/synch/mutex/innodb/log_writer_mutex         | NO      | NO    |
| wait/synch/sxlock/innodb/index_online_log        | NO      | NO    |
| wait/synch/sxlock/innodb/log_sn_lock             | NO      | NO    |
+--------------------------------------------------+---------+-------+
15 rows in set (0.00 sec)

mysql> SELECT EVENT_NAME, COUNT_STAR, SUM_TIMER_WAIT
    -> FROM performance_schema.events_waits_summary_global_by_event_name
    -> WHERE EVENT_NAME LIKE 'wait/io/file/innodb/%log%'
    ->    OR EVENT_NAME LIKE 'wait/synch/%/innodb/%log%'
    -> ORDER BY SUM_TIMER_WAIT DESC
    -> LIMIT 10;
+--------------------------------------------------+------------+----------------+
| EVENT_NAME                                       | COUNT_STAR | SUM_TIMER_WAIT |
+--------------------------------------------------+------------+----------------+
| wait/io/file/innodb/innodb_log_file              |        367 |    32378483748 |
| wait/synch/mutex/innodb/log_limits_mutex         |          0 |              0 |
| wait/synch/mutex/innodb/log_files_mutex          |          0 |              0 |
| wait/synch/mutex/innodb/log_checkpointer_mutex   |          0 |              0 |
| wait/synch/mutex/innodb/log_closer_mutex         |          0 |              0 |
| wait/synch/mutex/innodb/log_writer_mutex         |          0 |              0 |
| wait/synch/mutex/innodb/log_flusher_mutex        |          0 |              0 |
| wait/synch/mutex/innodb/log_write_notifier_mutex |          0 |              0 |
| wait/synch/mutex/innodb/log_flush_notifier_mutex |          0 |              0 |
| wait/synch/mutex/innodb/log_sys_arch_mutex       |          0 |              0 |
+--------------------------------------------------+------------+----------------+
10 rows in set (0.00 sec)
```

운영에서는 이 결과를 다음과 같이 해석한다.

- redo log file I/O wait가 커지면 storage latency, fsync 빈도, commit batch 효과를 확인한다.
- log 관련 mutex 또는 rwlock 대기가 커지면 초고동시성 쓰기 workload, 큰 transaction, redo subsystem 내부 경합을 의심한다.
- statement latency는 높지만 log wait가 낮으면 row lock, index contention, buffer pool miss, binary log flush 등 다른 commit 경로를 함께 보아야 한다.

중요한 점은 `performance_schema` 결과를 단독 증거로 과잉 해석하지 않는 것이다. 계측이 꺼져 있거나 timed가 비활성화되어 있으면 값이 작게 보일 수 있고, server restart 후 누적 시간이 짧으면 대표성이 낮다. 장애 분석에서는 측정 구간을 고정하고 전후 차이를 보는 방식이 더 안전하다.

## 9. Log Buffer 크기 조정 기준

`innodb_log_buffer_size`는 “크면 빠르다”가 아니라 “큰 트랜잭션이 commit 전에 불필요하게 기다리지 않게 한다”는 관점으로 접근해야 한다. 다음과 같은 workload에서는 기본값보다 큰 Log Buffer가 도움이 될 수 있다.

- 하나의 트랜잭션에서 대량 row를 변경하는 배치
- 큰 `BLOB` 또는 긴 `TEXT` 컬럼 변경이 많은 작업
- bulk load 중 secondary index 유지 비용과 redo 생성량이 큰 작업
- migration, backfill, cleanup처럼 장시간 transaction을 만들기 쉬운 작업

반대로 짧은 OLTP transaction이 대부분이고 `Innodb_log_waits`가 증가하지 않는다면 Log Buffer를 크게 늘려도 체감 효과가 제한적이다. 이 경우 commit latency는 보통 storage fsync, binary log fsync, group commit 효율, row lock 대기, CPU saturation에서 결정된다.

조정 절차는 다음 순서가 바람직하다.

1. 측정 구간을 정하고 `Innodb_log_waits`, redo write 관련 counter, commit latency를 수집한다.
2. 큰 transaction이 있는지 애플리케이션 batch와 slow query log를 확인한다.
3. Log Buffer 공간 부족 대기가 실제로 증가하면 `innodb_log_buffer_size` 증설을 검토한다.
4. 증설 후 같은 workload에서 counter 증가율과 latency 변화를 비교한다.
5. 효과가 없으면 fsync, binary log, lock, checkpoint 압력 등 다른 병목으로 분석 범위를 넓힌다.

## 10. Aurora MySQL에서의 운영 해석

Aurora MySQL은 Community MySQL과 storage architecture가 다르다. Aurora는 compute node와 분산 storage layer를 분리하고, redo-log-like record를 storage service로 전달하는 방식으로 durability와 replication을 구성한다. 따라서 EBS volume 위의 단일 mysqld가 redo log file을 flush하는 전통적 모델과 내부 구현은 다르다.

그렇다고 `innodb_flush_log_at_trx_commit`의 의미를 무시해도 된다는 뜻은 아니다. Aurora에서도 commit path는 storage layer와의 동기화, quorum write, writer와 reader 간 가시성, failover 후 복구 시간과 관련된다. 다만 운영자가 직접 보는 병목 지표는 Community MySQL의 local fsync보다 Aurora 지표와 Performance Insights의 wait event로 나타나는 경우가 많다.

Aurora 운영에서는 다음을 함께 확인한다.

- DB parameter group에서 `innodb_flush_log_at_trx_commit`, `sync_binlog`가 의도한 값인지 확인한다.
- Performance Insights에서 commit, log, storage 관련 wait event가 증가하는지 본다.
- CloudWatch의 write latency, commit throughput, volume write I/O 성격의 지표를 함께 비교한다.
- failover 또는 crash recovery 테스트에서 애플리케이션이 commit 성공 후 재조회·재처리를 어떻게 처리하는지 검증한다.

Aurora의 장점은 storage layer가 내구성과 복제 구조를 상당 부분 담당한다는 점이지만, 애플리케이션이 commit 반환을 업무 승인으로 해석한다는 사실은 동일하다. 따라서 durability 완화 설정은 Aurora에서도 명확한 RPO와 재처리 설계를 갖춘 경우에만 검토해야 한다.

## 11. 장애 모드와 흔한 오해

### 11.1 “commit이 빠르면 안전하다”는 오해

빠른 commit은 좋은 신호일 수 있지만, 그 자체로 내구성을 증명하지 않는다. `innodb_flush_log_at_trx_commit=2`에서는 commit이 빨라질 수 있으나 OS crash나 power loss 상황에서 최근 commit이 손실될 수 있다. 운영 정책은 latency와 durability를 동시에 명시해야 한다.

### 11.2 “Log Buffer를 키우면 fsync 병목이 해결된다”는 오해

Log Buffer 증설은 공간 부족 대기를 줄일 수 있다. 그러나 매 commit마다 필요한 fsync가 느린 환경이라면 Log Buffer 크기보다 storage latency, group commit, binary log flush, transaction batching이 더 중요하다.

### 11.3 “redo log가 있으니 binary log flush는 중요하지 않다”는 오해

복제, CDC, PITR을 쓰는 환경에서는 binary log도 데이터 보장 경로의 일부다. redo log는 InnoDB crash recovery의 핵심이고, binary log는 server layer의 변경 이력과 복제·복구의 핵심이다. 둘 중 하나만 안전하게 flush되어도 전체 시스템 관점의 일관성이 깨질 수 있다.

### 11.4 “Dirty page flush와 commit flush는 같은 말이다”는 오해

commit에서 중요한 것은 redo log의 안정적 기록이다. 변경된 data page가 즉시 tablespace에 기록되지 않아도 redo log가 있으면 crash recovery로 복구할 수 있다. Dirty page flush는 checkpoint 진행과 buffer pool 관리의 문제이며, commit latency와 연결되기도 하지만 같은 개념은 아니다.

## 12. 운영 점검표

- [ ] 업무 데이터의 RPO가 0에 가까운지, 최근 1초 내외 손실을 허용할 수 있는지 명확히 문서화했다.
- [ ] 기본 OLTP 시스템에서는 `innodb_flush_log_at_trx_commit=1`을 기준으로 검토했다.
- [ ] 복제·CDC·PITR 환경에서는 `sync_binlog`까지 함께 확인했다.
- [ ] `Innodb_log_waits` 증가 여부를 측정 구간 단위로 확인했다.
- [ ] 큰 트랜잭션, batch, bulk load가 Log Buffer 공간 압력을 만들 수 있는지 확인했다.
- [ ] commit latency를 볼 때 redo log I/O, binary log I/O, row lock, CPU, checkpoint 압력을 분리해 보았다.
- [ ] Log Buffer 크기 변경 전후를 같은 workload와 같은 측정 구간으로 비교했다.
- [ ] Aurora MySQL에서는 Performance Insights와 CloudWatch storage 관련 지표를 함께 확인했다.
- [ ] durability 완화 설정을 적용할 경우 장애 시 손실 범위, 재처리 절차, 사용자 영향 범위를 사전에 승인받았다.

## 13. 결론

InnoDB Log Buffer는 redo log를 쓰기 전에 잠시 거치는 메모리 영역이지만, 운영 의미는 단순한 버퍼보다 훨씬 크다. `innodb_flush_log_at_trx_commit`은 commit latency와 장애 시 손실 범위의 경계를 결정하고, `sync_binlog`와 함께 전체 MySQL 서버의 crash-safe 특성을 좌우한다.

성능 문제가 있을 때 Log Buffer 크기만 조정하는 것은 충분하지 않다. redo 생성량, Log Buffer 공간 대기, redo write와 fsync, binary log flush, group commit, storage latency, Aurora storage 계층의 차이를 함께 보아야 한다. 다음 글에서는 redo log file과 checkpoint 흐름을 더 깊게 다루며, Log Buffer에서 내려간 redo가 어떻게 checkpoint age와 dirty page flush 압력으로 이어지는지 살펴볼 것이다.
