쓰기 증폭
프로그램이 쓰라고 넘긴 양보다 저장장치에 실제로 더 많은 양이 쓰이는 현상입니다. 10MB 를 넘겼는데 디스크에는 30MB 가 쓰이는 식입니다. 그 배수를 쓰기 증폭 계수라고 부릅니다.
상세
이삿짐 보관 창고에 상자 하나를 맡깁니다. 직원은 그 상자를 안쪽에 넣으려고 앞에 쌓여 있던 짐을 여러 개 꺼냈다가 도로 쌓습니다. 맡긴 사람이 손에 쥔 영수증에는 상자 한 개가 적혀 있습니다.
쓰기 증폭은 논리적으로 요청한 쓰기량보다 물리적으로 실제 수행되는 쓰기량이 많아지는 현상입니다. 그 정도를 나타내는 값이 쓰기 증폭 계수, 곧 WAF(Write Amplification Factor)입니다. 물리 쓰기량을 논리 쓰기량으로 나눈 비율입니다. 데이터베이스에 초당 10MB 를 쓸 때 디스크 쓰기가 초당 30MB 로 관측되면 쓰기 증폭은 3 입니다.
증폭은 한 층에서만 생기지 않습니다. 응용이 넘긴 쓰기는 데이터를 관리하는 소프트웨어를 지납니다. 그 다음 저장장치 안을 한 번 더 지납니다. 층마다 자기 사정으로 같은 데이터를 다시 씁니다. 마지막에 저장 매체에 실제로 찍히는 양이 물리 쓰기량입니다.
flowchart TD
A["응용의 논리 쓰기"] --> B["저장 엔진"]
B --> C["저장장치 펌웨어"]
C --> D["물리 쓰기"]
B -.->|같은 데이터를 다시 씀| B
C -.->|살아 있는 데이터를 옮겨 씀| C
쓰기 증폭은 읽기 증폭·공간 증폭과 한 묶음으로 다뤄집니다. 셋 다 사용자가 낸 논리적 요청의 크기를 실제 하드웨어에 나간 요청의 크기에 잇는 값입니다.
배경
플래시 메모리로 만든 저장장치는 쓰기 전에 지워야 합니다. 지우는 일은 소자를 소모시킵니다. 실제로 쓰인 양이 넘긴 양보다 많으면 그만큼 수명이 줄어듭니다. 그런데 이 차이는 쓴 쪽에 보이지 않습니다. 호스트는 자기가 넘긴 논리 쓰기량만 셉니다.
데이터를 관리하는 소프트웨어 쪽도 같은 것을 겪습니다. 같은 데이터를 여러 번 다시 쓰면 디스크 처리량에 병목이 걸릴 수 있습니다. 넘긴 양이 늘지 않아도 장치는 계속 바쁩니다.
필요한 것은 하나였습니다. 넘긴 양 대비 실제로 쓰인 양이 몇 배인지를 재는 값입니다. 이 값이 있으면 설정을 바꿨을 때 물리 쓰기가 줄었는지 늘었는지 견줄 수 있습니다. 그 비율에 붙은 이름이 쓰기 증폭 계수입니다.
갈래
물리 쓰기가 어느 층에서 늘어나느냐가 축입니다. 한 층은 데이터를 관리하는 저장 엔진입니다. 다른 한 층은 저장장치 안의 펌웨어입니다. 두 층은 서로를 모른 채 각자 증폭을 보탭니다.
저장 엔진의 컴팩션
로그 구조 병합 트리 계열 저장 엔진은 들어온 데이터를 맨 위 레벨에 먼저 씁니다. 나중에 컴팩션이 그 데이터를 아래 레벨로 합칩니다. 합칠 때 아래 레벨에 이미 있던 데이터까지 같이 다시 씁니다. RocksDB 문서는 레벨 1 크기가 레벨 0 과 같은 구성을 놓고 이 값을 셈해 둡니다. 모든 바이트는 먼저 레벨 0 에 쓰입니다. 레벨 1 은 레벨 0 과 크기가 같아서 레벨 0 에서 레벨 1 로 가는 컴팩션의 쓰기 증폭은 2 입니다. 레벨 2 부터는 아래 레벨이 10배씩 큽니다. 레벨 1 의 한 바이트가 레벨 2 로 내려갈 때는 레벨 2 의 10 바이트와 함께 다시 쓰입니다. 문서는 이 구성의 전체 쓰기 증폭을 대략 1 + 2 + 10 + 10 + 10 = 33 으로 셉니다.
flowchart TD
W["새 데이터 한 번 쓰기"] --> L0["레벨 0"]
L0 -->|컴팩션이 다시 씀| L1["레벨 1"]
L1 -->|컴팩션이 다시 씀| L2["레벨 2"]
L2 -->|컴팩션이 다시 씀| L3["레벨 3"]
컴팩션 방식이 이 값을 정합니다. RocksDB 문서는 고전적인 레벨 컴팩션이 공간 증폭을 최소화하는 대신 읽기 증폭과 쓰기 증폭을 치른다고 적습니다. 레벨당 쓰기 증폭은 최악의 경우 팬아웃과 같습니다. 실제로는 팬아웃보다 작은 편이라고 덧붙입니다. 티어드 컴팩션은 반대쪽입니다. Ln 으로 합칠 때 Ln 에 이미 있던 정렬 런을 읽지도 다시 쓰지도 않아서 레벨당 쓰기 증폭이 1 입니다. 대신 읽기 증폭과 공간 증폭을 치릅니다. 유니버설 컴팩션도 쓰기 증폭을 낮추는 쪽을 겨냥합니다. 읽기 증폭과 공간 증폭을 맞바꾼다고 적습니다.
저장장치 펌웨어의 가비지 컬렉션
SSD(Solid State Drive)는 플래시 메모리를 페이지 단위로 쓰고 블록 단위로만 지웁니다. 페이지는 대개 4KiB 에서 16KiB 입니다. 블록 하나에는 수백 페이지가 들어갑니다. 일부만 유효한 블록을 지우려면 살아 있는 페이지를 먼저 다른 자리로 옮겨 써야 합니다. 이 옮겨 쓰기는 호스트가 시킨 적이 없는 물리 쓰기입니다.
Micron 의 기술 노트는 이 값을 SMART(Self-Monitoring, Analysis and Reporting Technology) 속성으로 재는 식을 적어 둡니다. WAF = 1 + (속성 248 / 속성 247) 입니다. 속성 247 은 호스트가 일으킨 플래시 페이지 프로그램 횟수, 속성 248 은 플래시 변환 계층(Flash Translation Layer)이 그 위에 더 일으킨 횟수입니다. 같은 문서는 작은 블록 무작위 쓰기가 큰 블록 순차 쓰기보다 높은 WAF 와 큰 소모를 내는 편이라고 적습니다. 꽉 찬 드라이브는 덜 찬 드라이브보다 높은 WAF 를 겪는다고도 적습니다.
예시
LevelDB 공식 문서는 컴팩션 한 번이 실제로 읽고 쓰는 양을 적어 둡니다. 레벨 0 컴팩션은 레벨 0 의 1MB 파일을 최대 네 개까지 읽습니다. 최악의 경우 레벨 1 파일 전부인 10MB 도 함께 읽습니다. 그래서 14MB 를 읽고 14MB 를 씁니다. 레벨 L 의 파일 하나를 아래로 내릴 때는 최악의 경우 레벨 L+1 의 파일 약 12 개와 겹칩니다. 그때는 26MB 를 읽고 26MB 를 씁니다.
26MB 가운데 응용이 새로 넘긴 몫은 일부입니다. 나머지는 이미 저장돼 있던 데이터를 아래 레벨로 옮기느라 다시 쓴 양입니다.
관련 항목
쓰기 증폭을 만드는 저장 엔진 요소
LSM 트리 · 컴팩션 · 레벨 컴팩션 · 티어드 컴팩션 · 유니버설 컴팩션 · 정렬 런 · 팬아웃 · 쓰기 선행 로그 · RocksDB · LevelDB
함께 재는 값
읽기 증폭 · 공간 증폭 · RUM 트레이드오프(Read Update Memory 트레이드오프, 읽기·갱신·메모리 셋을 맞바꾸는 관계)
쓰기 증폭을 만들고 재는 SSD 펌웨어 요소
SSD · 플래시 메모리 · 가비지 컬렉션 · 플래시 변환 계층 · SMART
이를 줄이는 SSD 관리 기법
TRIM · 오버프로비저닝 · 웨어 레벨링
다른 이름: write amplification · write amplification factor