커널 아래로 내려간 스토리지 엔진 최적화
스토리지 엔진에서 다음 성능이 나오는 자리가 컴팩션 정책에서 I/O 경로로 옮겨갔습니다. 소프트웨어 튜닝으로 벌 몫과 I/O 경로 투자로만 얻는 몫을 나눠서 판단해야 합니다.
스토리지 엔진에서 다음 성능을 벌어들이는 자리가 컴팩션 정책에서 I/O 경로로 옮겨갔습니다.
컴팩션 정책을 바꿔 쓰기 증폭을 줄이는 일은 오랫동안 스토리지 엔진 최적화의 기본이었습니다. 2025~2026년 연구와 프로덕션 사례를 나란히 놓으면 다른 그림이 보입니다. 알고리즘을 한 줄도 고치지 않고 커널과 주고받는 방식만 바꿔서 같은 자릿수의 이득을 낸 결과가 여럿 나왔습니다. 그래서 지금은 소프트웨어 튜닝으로 얻을 몫과 I/O 경로 투자로만 얻는 몫을 나눠서 판단해야 합니다.
LSM 트리의 기본 구조와 쓰기 증폭이 왜 생기는지는 LSM 트리와 쓰기 증폭에서 다뤘습니다. 이 글은 그 위에서 최적화의 지렛대가 어느 층으로 내려갔는지를 봅니다.
병목이 위쪽에 없다는 신호
RESYSTANCE 논문(ICDE 2026, arXiv:2603.05162)은 첫 문장에서 전제를 밝힙니다. NVMe SSD 같은 고속 장치가 보급되면서 I/O 병목이 하드웨어에서 소프트웨어로 옮겨갔다는 것입니다. 장치가 빨라진 만큼 커널을 드나드는 소프트웨어 스택의 비용이 상대적으로 커졌다는 뜻입니다.
EcoTune 논문(SIGMOD 2025)은 다른 각도에서 같은 지점을 짚습니다. 이 논문이 인용한 메타의 실제 워크로드 최고 쓰기 속도는 초당 약 45MB인데, 현대 NVMe SSD의 쓰기 대역폭은 초당 2GB를 넘습니다. 쓰기 대역폭이 이만큼 남으면 쓰기 증폭 자체가 쓰기 성능의 제약이 되지 않습니다. EcoTune 저자들이 컴팩션의 목표를 쓰기 증폭 최소화가 아니라 평균 질의 처리량 최대화로 다시 정의한 근거가 이 격차입니다.
두 논문은 서로 다른 문제를 풀지만 같은 전제 위에 서 있습니다. 장치 대역폭은 남고, 그 대역폭에 도달하기까지의 경로가 비용을 결정합니다.
소프트웨어 튜닝으로 벌 수 있는 몫
알고리즘 층의 최근 성과는 분명합니다. Vertiorizon(SIGMOD 2025)은 레벨을 깊게 쌓는 수직 방식과 넓게 키우는 수평 방식을 결합합니다. RocksDB에 통합한 평가에서 수평 방식 대비 추가 공간 비용이 약 6분의 1이었습니다. EcoTune은 컴팩션을 자원 투자로 보고 동적 계획법으로 정책을 정해, leveling 대비 평균 질의 처리량을 1.5~3배 올렸습니다. ArceKV는 구조 제약을 없앤 ElasticLSM에 경량 결정 엔진을 붙여, 읽기·쓰기 비율이 바뀌는 워크로드에서 약 3배 빠른 성능을 보고했습니다.
데이터 배치를 바꾸는 지렛대도 같은 층에 있습니다. CockroachDB는 v25.4에서 스토리지 엔진 Pebble에 값 분리를 도입했습니다. 큰 값을 SSTable 밖의 blob 파일에 두고 컴팩션 때는 값 핸들만 복사하는 방식입니다. BIGINT 기본키에 4KiB 값을 upsert하는 벤치마크에서 동일 하드웨어 기준 처리량이 약 47% 늘었고, 값이 작은 워크로드에서는 개선 폭이 줄어든다고 같은 글이 밝힙니다.
파라미터 조정만으로도 상당한 몫이 나옵니다. 토스는 Flink 상태 백엔드로 쓰는 RocksDB에서 쓰기 증폭을 '배수 10 × (활성 레벨 수 - 1)'로 근사했습니다. 최상위 레벨을 비활성화해 활성 레벨을 3단계에서 2단계로 줄이자 쓰기 증폭이 20에서 10으로 내려갔습니다.
| 지렛대 | 대표 사례 | 바꾸는 것 | 보고된 이득 |
|---|---|---|---|
| 레벨 성장 전략 | Vertiorizon | 트리가 커지는 방향 | 수평 방식 대비 추가 공간 비용 약 1/6 |
| 컴팩션 정책 | EcoTune | 병합 시점과 강도 | leveling 대비 처리량 1.5~3배 |
| 워크로드 적응 | ArceKV | 실행 중 정책 전환 | 동적 워크로드에서 약 3배 |
| 값 배치 | Pebble 값 분리 | 컴팩션이 복사하는 바이트 | 4KiB 값 upsert 처리량 약 47% 증가 |
| 파라미터 튜닝 | 토스 RocksDB | 활성 레벨 수 | 쓰기 증폭 20 → 10 |
이 표의 이득은 모두 워크로드 가정에 묶여 있습니다. 값이 작으면 값 분리가 힘을 잃고, 워크로드가 고정이면 적응형 구조에는 결정 오버헤드만 남습니다.
커널 안으로 들어간 컴팩션
RESYSTANCE는 LSM 트리 구조도 컴팩션 알고리즘도 건드리지 않습니다. 백그라운드 컴팩션이 읽기 시스템 콜을 대량으로 발생시킨다는 점을 실측으로 짚습니다. 그리고 eBPF와 io_uring으로 핵심 I/O 루틴을 커널 안에서 처리해 사용자·커널 공간 전환을 없앱니다. db_bench와 YCSB, OLTP 워크로드로 평가했습니다.
| 지표 | 기본 RocksDB 대비 |
|---|---|
| 컴팩션 중 평균 시스템 콜 호출 수 | 99% 감소 |
| 컴팩션 소요 시간 | 50% 단축 |
| 쓰기 집약 워크로드 처리량 | 최대 75% 향상 |
| p99 지연 | 40% 감소 |
이 수치가 주장을 떠받치는 지점은 크기가 아니라 위치입니다. 정책을 하나도 바꾸지 않고 실행 경로만 바꿔서, 앞 절의 알고리즘 재설계와 같은 자릿수의 이득이 나왔습니다. 두 축이 겹치지 않으므로 앞 표의 이득 위에 포개어 쓸 수 있다는 것이 논문의 설계 의도이기도 합니다.
프로덕션에서 실제로 걸린 자리
토스 사례에서 더 흥미로운 부분은 쓰기 증폭 튜닝 다음에 나옵니다. 컴팩션이 밀리고 CPU가 포화된 앱을 프로파일링했더니, 수집한 1,766개 샘플 중 96.2%가 필터 블록을 디스크에서 읽는 경로에 몰려 있었습니다. 컴팩션 정책과는 상관이 없는 병목이었습니다.
원인은 I/O 경로 설정이었습니다. 쿠버네티스에서 컨테이너 메모리 사용을 예측 가능하게 만들려고 Direct I/O를 켜면 OS 페이지 캐시를 완전히 우회합니다. 블록 캐시에 없는 필터 블록은 매번 디스크에서 읽어야 하는데, SST 파일당 약 800만 개 키에 키당 14비트를 쓰면 단일 필터 블록이 약 15MB에 달합니다. 캐시 미스 한 번이 15MB 읽기가 됩니다.
해결은 필터 블록을 약 4KB 단위로 쪼개는 partitioned-index-filters 활성화였고, 미스당 읽기량이 약 2,750배 줄었습니다. 알고리즘을 바꾼 조치가 아니라 데이터가 커널과 장치를 지나는 경로를 바꾼 조치입니다. 페이지 캐시와 I/O 처리 방식의 기본은 운영체제 IO 관리와 파일 시스템에 정리해 뒀습니다.
I/O 경로가 네트워크를 건너갈 때
컴퓨트와 스토리지를 분리하면 I/O 경로에 네트워크가 끼어듭니다. PostgreSQL과 RocksDB로 구현한 SIGMOD 2024 실증 연구는 이 비용을 단계별로 측정했습니다(2026-09 확인). 버퍼 없이 원격 디스크만 쓰는 가장 단순한 분리에서 읽기는 16.4배, 쓰기는 17.9배 느려졌습니다. 8GB 버퍼로 히트율 80%를 확보하면 읽기 격차는 1.8배까지 줄지만, 쓰기는 버퍼를 아무리 키워도 개선되지 않습니다.
쓰기가 버퍼에 반응하지 않는 이유는 커밋마다 로그를 원격으로 플러시해야 하기 때문입니다. 논문은 버퍼 히트율을 99.5%까지 올려도 쓰기 성능이 크게 나아지지 않는다고 결론짓습니다. 이 논문을 정리한 Murat Demirbas의 글은 모든 최적화를 적용한 뒤에도 쓰기 처리량이 단일 노드의 약 50%에 머문다고 그래프에서 읽었습니다(추정). 알고리즘 층의 어떤 튜닝도 이 격차를 메우지 못하는 이유는 경로 자체가 바뀌었기 때문입니다.
반대 방향으로 밀어붙인 연구도 있습니다. O3-LSM(SIGMOD 2026)은 공유 분리 메모리를 써서 memtable과 flush, 컴팩션을 세 층으로 오프로딩합니다. 컴팩션만 오프로딩하던 기존 분리형 구현들과 비교해 쓰기 처리량 최대 4.5배, 범위 질의 최대 5.2배, p99 지연 최대 76% 감소를 보고했습니다.
캐시 축출에서 먼저 벌어진 같은 이동
같은 이동이 스토리지 엔진 안의 다른 부품에서도 일어났습니다. S3-FIFO와 SIEVE는 무엇을 지울지를 다시 설계해 미스율을 낮췄습니다. 그다음 나온 Mobius(SIGMETRICS 2025)는 히트율을 비슷하게 유지한 채 락 경합만 제거했습니다. 두 개의 lock-free FIFO 큐와 연속 감지 기법으로, 기존 최고 수준 기법 대비 동시 처리량을 1.2배에서 8.5배까지 올렸습니다.
Mobius 저자들이 밝힌 문제 인식은 RESYSTANCE와 겹칩니다. 장치는 빨라지는데 기존 축출 정책이 굵은 단위 락과 복잡한 자료구조에 의존해 동시 처리량을 내지 못한다는 진단입니다. 축출 알고리즘을 그대로 두고 접근 경로만 바꿔 배수 이득을 냈다는 형태도 같습니다. 이 흐름은 LRU를 대체하는 FIFO 캐시 축출에서 자세히 다뤘습니다.
Mobius가 CacheLib과 RocksDB에 실제로 구현됐다는 점도 이 관찰을 뒷받침합니다. 캐시 축출과 컴팩션은 서로 다른 서브시스템인데 병목이 같은 방향으로 옮겨갔습니다.
이 주장이 깨지는 조건
첫째, 장치가 느리면 성립하지 않습니다. RESYSTANCE의 전제는 고속 스토리지에서 소프트웨어 스택이 상대적으로 비싸졌다는 것입니다. 회전 디스크나 대역폭이 낮은 원격 볼륨에서는 여전히 장치가 병목이고, 그때는 쓰기 증폭을 줄이는 쪽이 직접 이득입니다.
둘째, 워크로드가 알고리즘의 전제를 깨면 알고리즘이 다시 결정적입니다. SOLAR 연구(arXiv:2607.00394)는 LLM 에이전트의 의미 검색 버퍼를 다룹니다. 시간적 지역성과 빈도 집중이 없는 이런 워크로드에서는 LRU와 LFU가 단순 FIFO보다도 못했습니다. 학습 증강 교체 방식은 타이트한 캐시 크기 기준 FIFO 대비 5~75%의 상대 개선을 냈습니다.
셋째, 이 글의 수치를 더하면 안 됩니다. 각 수치는 서로 다른 벤치마크와 하드웨어, 서로 다른 기준선에서 나왔고 2026-09 기준으로 각 논문 초록과 공개 글에서 확인한 값입니다. 세 층을 동시에 적용했을 때의 누적 효과를 측정한 연구는 찾지 못했습니다. 축이 겹치지 않는다는 것은 설계상의 주장이지 측정된 사실이 아닙니다.
넷째, I/O 경로 최적화는 비용을 없애지 않고 옮깁니다. eBPF와 io_uring은 커널 버전에 의존하고, Direct I/O는 토스 사례처럼 새로운 캐시 미스 문제를 만듭니다. 분리 스토리지는 네트워크 가용성을 데이터베이스 가용성에 직접 연결합니다.
층별 점검 순서
순서를 정하는 기준은 측정입니다. 아래 순서로 확인하면 어느 층에 예산을 쓸지가 갈립니다.
- 장치 쓰기 대역폭이 실제로 포화되는지 본다. 포화되면 쓰기 증폭 감소가 직접 이득이므로 컴팩션 정책과 활성 레벨 수부터 조정한다.
- 포화되지 않는데 처리량이 안 나오면 CPU 프로파일을 뜬다. 시스템 콜과 캐시 미스 경로가 상위에 있으면 I/O 경로 문제다.
- 값 크기 분포를 잰다. 4KB 이상 값이 상당 비중이면 값 분리가 파라미터 수준의 조치로 효과를 낸다.
- 스레드를 늘려도 처리량이 비례하지 않으면 공유 자료구조의 락을 의심한다. 캐시와 큐 구현을 본다.
- 네트워크가 I/O 경로에 끼어 있으면 쓰기 커밋 경로부터 본다. 버퍼로는 해결되지 않는다.
| 관측된 신호 | 손댈 층 | 참고할 수치 |
|---|---|---|
| 쓰기 대역폭 포화, 쓰기 증폭 두 자릿수 | 컴팩션 정책·레벨 수 | 토스 쓰기 증폭 20 → 10 |
| 대역폭 여유, 시스템 콜이 프로파일 상위 | 커널 I/O 경로 | RESYSTANCE 컴팩션 시간 50% 단축 |
| 큰 값이 컴팩션 바이트를 지배 | 값 배치 | Pebble 처리량 약 47% 증가 |
| 캐시 미스가 CPU를 지배 | 캐시 구조·필터 배치 | 토스 미스당 읽기 약 2,750배 감소 |
| 스레드 확장에서 처리량 정체 | 동시성 구조 | Mobius 동시 처리량 1.2~8.5배 |
| 원격 스토리지 쓰기 지연 | 아키텍처 | 버퍼를 키워도 쓰기 미개선, 단일 노드 대비 약 50% (추정) |
정리
스토리지 엔진에서 다음 성능이 나오는 자리는 컴팩션 정책에서 I/O 경로로 옮겨갔습니다. 알고리즘 층의 최신 연구가 내는 배수와, 알고리즘을 전혀 고치지 않고 커널 경로만 바꿔 얻는 배수가 같은 자릿수라는 점이 그 근거입니다. 다만 이 이동은 장치가 빠르고 워크로드에 시간적 지역성이 있을 때만 성립하며, 둘 중 하나가 깨지면 판단을 뒤집어야 합니다. 실무에서는 장치 대역폭이 포화됐는지 먼저 재고, 포화되지 않았다면 CPU 프로파일에서 시스템 콜과 캐시 미스 경로를 확인하는 순서를 권합니다.