[G1-Sync] Manual knowledge update
This commit is contained in:
@@ -0,0 +1,263 @@
|
||||
---
|
||||
id: cs-wal-write-ahead-log
|
||||
title: WAL (Write-Ahead Log) — Durability / Recovery
|
||||
category: Coding
|
||||
status: draft
|
||||
source_trust_level: B
|
||||
verification_status: conceptual
|
||||
created_at: 2026-05-09
|
||||
updated_at: 2026-05-09
|
||||
tags: [cs, wal, database, durability, vibe-coding]
|
||||
tech_stack: { language: "Concept", applicable_to: ["Database"] }
|
||||
applied_in: []
|
||||
aliases: [WAL, write-ahead log, journal, redo log, transaction log, checkpoint]
|
||||
---
|
||||
|
||||
# WAL (Write-Ahead Log)
|
||||
|
||||
> Crash 후 복구 / replication 의 기반. **변경을 disk 에 먼저 log → 그 후 apply**. ACID 의 Durability 보장. Postgres / MySQL InnoDB / SQLite WAL.
|
||||
|
||||
## 📖 핵심 개념
|
||||
- WAL: append-only log of changes.
|
||||
- Commit: WAL flush = durable.
|
||||
- Checkpoint: log → data file 적용.
|
||||
- Recovery: crash 후 WAL replay.
|
||||
|
||||
## 💻 코드 패턴
|
||||
|
||||
### Postgres WAL
|
||||
```
|
||||
1. Transaction 가 변경 — buffer cache 에 적용 (fast).
|
||||
2. WAL record 만들고 WAL buffer 에.
|
||||
3. Commit 시 WAL fsync (durable).
|
||||
4. Background 가 buffer → data file (lazy).
|
||||
5. Checkpoint 가 모든 변경 flush + WAL 일부 retire.
|
||||
```
|
||||
|
||||
### Recovery
|
||||
```
|
||||
Crash 후:
|
||||
1. 마지막 checkpoint 부터 시작.
|
||||
2. WAL replay (REDO).
|
||||
3. Uncommitted transaction = abort (UNDO 옛 system).
|
||||
4. Database 가 일관 state.
|
||||
```
|
||||
|
||||
### Postgres 설정
|
||||
```ini
|
||||
# postgresql.conf
|
||||
wal_level = replica # replica / logical
|
||||
synchronous_commit = on # commit 가 fsync 까지 wait
|
||||
fsync = on
|
||||
full_page_writes = on
|
||||
checkpoint_timeout = 5min
|
||||
max_wal_size = 1GB
|
||||
min_wal_size = 80MB
|
||||
```
|
||||
|
||||
### Replication 의 기반
|
||||
```
|
||||
Streaming replication = WAL stream.
|
||||
Primary 가 WAL → Standby.
|
||||
Standby 가 WAL replay.
|
||||
|
||||
→ Hot standby 가 read 가능.
|
||||
```
|
||||
|
||||
→ [[DB_Read_Replica_Patterns]] / [[DB_Replica_Operations]].
|
||||
|
||||
### Logical decoding
|
||||
```sql
|
||||
SELECT * FROM pg_create_logical_replication_slot('my_slot', 'pgoutput');
|
||||
|
||||
-- Subscribe (다른 system, e.g. Debezium)
|
||||
-- WAL → row-level changes (INSERT / UPDATE / DELETE)
|
||||
```
|
||||
|
||||
→ CDC 의 기반. [[DB_Change_Data_Capture]].
|
||||
|
||||
### MySQL InnoDB redo log
|
||||
```ini
|
||||
innodb_log_file_size = 1G
|
||||
innodb_flush_log_at_trx_commit = 1 # 1=fsync per commit, 0=once/sec, 2=os flush
|
||||
sync_binlog = 1 # binlog fsync
|
||||
```
|
||||
|
||||
### SQLite WAL mode
|
||||
```sql
|
||||
PRAGMA journal_mode = WAL;
|
||||
PRAGMA synchronous = NORMAL;
|
||||
```
|
||||
|
||||
→ [[DB_SQLite_Patterns]].
|
||||
|
||||
### Performance tradeoff
|
||||
```
|
||||
Synchronous commit:
|
||||
on: Durable. Latency ↑ (fsync per commit).
|
||||
off: Fast. 마지막 < 1초 commit 잃을 수 있음.
|
||||
remote_apply: Replica 도 적용 후. Highest durability.
|
||||
remote_write: Replica 가 받은 후.
|
||||
|
||||
→ 보통 'on'. 대량 batch + non-critical = off OK.
|
||||
```
|
||||
|
||||
```sql
|
||||
-- Per-transaction
|
||||
SET synchronous_commit = off;
|
||||
INSERT INTO log VALUES (...);
|
||||
COMMIT;
|
||||
SET synchronous_commit = on;
|
||||
```
|
||||
|
||||
### Group commit
|
||||
```
|
||||
여러 commit 가 같이 fsync.
|
||||
높은 throughput 시 자동.
|
||||
```
|
||||
|
||||
### Checkpoint tuning
|
||||
```ini
|
||||
checkpoint_timeout = 15min # 자주 = WAL 작고, 자주 IO
|
||||
checkpoint_completion_target = 0.9 # 점진 — IO smooth
|
||||
max_wal_size = 4GB # 큰 = checkpoint 적게
|
||||
```
|
||||
|
||||
→ Long checkpoint = recovery 길음.
|
||||
|
||||
### Archive log (PITR)
|
||||
```ini
|
||||
wal_level = replica
|
||||
archive_mode = on
|
||||
archive_command = 'aws s3 cp %p s3://wal-archive/%f'
|
||||
```
|
||||
|
||||
```bash
|
||||
# 복구 — 특정 시점
|
||||
restore_command = 'aws s3 cp s3://wal-archive/%f %p'
|
||||
recovery_target_time = '2026-05-09 14:00:00'
|
||||
```
|
||||
|
||||
→ Point-in-time recovery.
|
||||
|
||||
### WAL 크기 모니터링
|
||||
```sql
|
||||
SELECT pg_size_pretty(pg_wal_lsn_diff(pg_current_wal_lsn(), '0/0')) AS total_wal;
|
||||
|
||||
-- Replication slot 의 WAL retention
|
||||
SELECT slot_name, pg_size_pretty(pg_wal_lsn_diff(pg_current_wal_lsn(), confirmed_flush_lsn)) AS lag
|
||||
FROM pg_replication_slots;
|
||||
```
|
||||
|
||||
→ Slot 가 inactive + WAL 무한 누적 = disk full.
|
||||
|
||||
### Inactive slot (위험)
|
||||
```sql
|
||||
-- 옛 slot drop
|
||||
SELECT pg_drop_replication_slot('unused_slot');
|
||||
```
|
||||
|
||||
### App 영향
|
||||
```
|
||||
1. Commit latency = fsync 시간 (~1ms SSD, ~10ms HDD).
|
||||
2. Long-running write transaction = WAL 큰 — replica lag.
|
||||
3. Bulk insert = COPY + 적은 commit = 빠름.
|
||||
4. Synchronous_commit off = 마지막 < 1s 잃을 수 있음 (banking 안 됨, log OK).
|
||||
```
|
||||
|
||||
### COPY (bulk)
|
||||
```sql
|
||||
COPY orders FROM '/data.csv' CSV;
|
||||
-- 매 row WAL — but 작은 overhead per row
|
||||
-- 또한 commit 한 번 = fsync 한 번
|
||||
```
|
||||
|
||||
```ts
|
||||
import { from as copyFrom } from 'pg-copy-streams';
|
||||
const stream = client.query(copyFrom('COPY orders (col1, col2) FROM STDIN CSV'));
|
||||
fs.createReadStream('data.csv').pipe(stream);
|
||||
```
|
||||
|
||||
### Unlogged tables (no WAL)
|
||||
```sql
|
||||
CREATE UNLOGGED TABLE temp_data (...);
|
||||
-- WAL 안 — 빠름
|
||||
-- Crash 시 truncated
|
||||
```
|
||||
|
||||
→ 임시 / cache / 작업 table.
|
||||
|
||||
### 다른 storage engine
|
||||
```
|
||||
Postgres / MySQL InnoDB / SQL Server: redo log + WAL.
|
||||
Cassandra: commit log.
|
||||
RocksDB: WAL + memtable.
|
||||
SQLite: rollback journal 또는 WAL.
|
||||
File system (ext4, ZFS): journaling — 같은 idea.
|
||||
```
|
||||
|
||||
### App-level WAL 패턴 (custom)
|
||||
```
|
||||
Event sourcing = app-level WAL.
|
||||
Outbox = transactional log.
|
||||
|
||||
→ 같은 idea 다른 layer.
|
||||
```
|
||||
|
||||
→ [[Backend_Event_Sourcing]] / [[Backend_Outbox_Pattern]].
|
||||
|
||||
### fsync 의 비용
|
||||
```
|
||||
HDD seek: ~10ms
|
||||
SSD: ~0.1-1ms
|
||||
Network FS / EBS: ~1-5ms (variable)
|
||||
|
||||
Group commit + WAL batching = 100s commit / sec OK.
|
||||
```
|
||||
|
||||
### Crash recovery 시간
|
||||
```
|
||||
WAL 큼 = recovery 길음.
|
||||
Checkpoint 자주 = recovery 짧음 (WAL 작음).
|
||||
|
||||
→ Trade-off.
|
||||
```
|
||||
|
||||
### Backup + WAL
|
||||
```bash
|
||||
# pg_basebackup
|
||||
pg_basebackup -D /backup -Ft -X stream -P
|
||||
|
||||
# 또는 file-system snapshot + WAL archive
|
||||
# Recovery: snapshot 복원 + WAL replay
|
||||
```
|
||||
|
||||
## 🤔 의사결정 기준
|
||||
| 요구 | 설정 |
|
||||
|---|---|
|
||||
| Durability strict | synchronous_commit = on |
|
||||
| 빠른 bulk insert | unlogged table 또는 sync off |
|
||||
| Replication | WAL archive + slot |
|
||||
| PITR | WAL archive |
|
||||
| Edge / embedded | SQLite WAL |
|
||||
| 매우 큰 throughput | Group commit + tune checkpoint |
|
||||
|
||||
## ❌ 안티패턴
|
||||
- **fsync off prod**: durability 깨짐.
|
||||
- **Replica slot drop 안 함**: WAL 무한 누적.
|
||||
- **Checkpoint 너무 자주 (1min)**: IO 폭발.
|
||||
- **Long transaction**: WAL 거대.
|
||||
- **Backup 없는 archive only**: WAL 만으로는 복구 불가.
|
||||
- **Async commit + critical**: 데이터 잃음.
|
||||
- **HDD prod + sync commit**: 큰 latency.
|
||||
|
||||
## 🤖 LLM 활용 힌트
|
||||
- WAL = ACID Durability + replication + recovery 기반.
|
||||
- Postgres + WAL archive + 정기 backup.
|
||||
- synchronous_commit = on (default).
|
||||
- Slot 모니터링 + 옛 slot drop.
|
||||
|
||||
## 🔗 관련 문서
|
||||
- [[DB_Vacuum_Autovacuum]]
|
||||
- [[DB_Replica_Operations]]
|
||||
- [[DevOps_Disaster_Recovery]]
|
||||
Reference in New Issue
Block a user