Write Amplification

SSD 的規格表上寫著「600 TBW」(Total Bytes Written)。如果你每天寫入 50 GB,算一下大約可以用 33 年,聽起來完全不用擔心。但這個計算有個致命的盲點:你以為你寫了 50 GB,NAND flash 實際承受的寫入量可能是 150 GB 甚至 500 GB。差距的來源就是 write amplification。

Write amplification factor(WAF)直接告訴你 SSD 的韌體和上層軟體一共浪費了多少額外的寫入。WAF 越高,SSD 的壽命越短、效能越差。問題在於,write amplification 不只來自 SSD 內部的 garbage collection,整個 storage stack 從應用層到韌體層都在製造額外的寫入。理解 WAF 的全貌,才能做出正確的系統設計決策。

WAF 的定義

Write Amplification Factor 的定義非常簡單:

WAF = 實際寫入 NAND flash 的資料量 / 應用程式要求寫入的資料量

理想情況下 WAF = 1,代表應用程式寫 1 MB,NAND flash 上恰好只寫了 1 MB。但實際上 WAF 幾乎總是大於 1,典型值在 2 到 10 之間,極端情況下可以超過 20。

需要注意的是,WAF 可以在不同層級來定義。有時我們只看 SSD 內部的 WAF(NAND writes / host writes to SSD),有時我們看整個 stack 的 WAF(NAND writes / application logical writes)。後者通常更大,因為中間的軟體層也會製造額外寫入。

Write amplification across the storage stack showing 1 MB application write amplified to 4 MB NAND write through file system journaling and GC

GC 是 WAF 的主要來源

在 SSD 內部,garbage collection 搬移 valid page 是最主要的 write amplification 來源。每次 GC 回收一個 block 時,block 裡的 valid page 必須被讀出、再寫入到新的位置。這些搬移產生的寫入量完全是「額外」的,應用程式並沒有要求這些寫入。

GC 造成的 WAF 與 block 的 valid page ratio 直接相關。假設一個 block 有 256 個 page:

  • 如果只有 10 個 valid page(96% invalid):搬移 10 個 page,回收 256 個 page 的空間。GC 的效率很高。
  • 如果有 200 個 valid page(22% invalid):搬移 200 個 page,回收 256 個 page 的空間。GC 的效率極差,幾乎是 1:1 的搬移。

一個簡化的 GC WAF 估算公式是:

WAF_gc = 1 / (1 - valid_ratio)

如果平均每個被回收的 block 有 50% 的 valid page(valid_ratio = 0.5),WAF_gc = 2。也就是說應用程式每寫 1 MB,GC 額外搬移的資料量也約為 1 MB,NAND flash 實際寫了 2 MB。

什麼因素影響 valid ratio?主要有兩個:

  1. Over-provisioning 比例:OP 越大,SSD 有更多 free block 可以用,GC 不需要回收那些 valid page 比例高的 block,平均 valid ratio 較低,WAF 較小。
  2. Workload pattern:如果應用程式的寫入非常 random 且分散,invalid page 會均勻地散布在所有 block 中,每個 block 的 valid ratio 都很高,GC 效率差。反之,如果寫入有 locality(集中在少數 LBA),某些 block 會累積較多 invalid page,GC 效率較好。

軟體層的 write amplification

GC 不是唯一的 WAF 來源。在 SSD 之上,各種軟體層也會產生額外寫入:

File system journaling:ext4 等 journaling file system 為了確保 crash consistency,會把每筆 metadata 修改(有時包括 data)先寫到 journal area,確認成功後再寫到正式位置。這等於同一份資料寫了兩次。ext4 在 data=journal 模式下的 WAF 可以達到 2 以上;即使在預設的 data=ordered 模式下,metadata 的 journaling 也會增加一些寫入量。

LSM-Tree compaction:RocksDB、LevelDB 等 key-value store 使用 LSM-Tree 結構。資料先寫入 memory 的 memtable,flush 到 disk 成為 Level-0 SSTable,然後經過多次 compaction 從 Level-0 合併到 Level-1、Level-2、...。每次 compaction 都需要讀出舊的 SSTable、合併、寫入新的 SSTable。一筆資料從被寫入到穩定存放在最底層,可能被 compaction 搬移 10-30 次。LSM-Tree 本身的 WAF 就可能高達 10-30 倍。

LSM-Tree compaction write amplification showing memtable flush to Level 0 through Level 2 with ~10x amplification per level totaling ~30x

RAID partial-stripe write:在 RAID-5 或 RAID-6 架構中,如果一次寫入的資料量不足以填滿一個 stripe,就需要 read-modify-write:讀出 stripe 中其他 disk 的資料,重新計算 parity,然後把更新後的 data 和 parity 都寫回去。一個只更新 1 個 disk 的小寫入,可能導致 3-4 個 disk 的寫入。

Database WAL(Write-Ahead Log):與 journaling 類似,資料庫的 WAL 先把每筆 transaction 的修改寫入 log file,之後再寫入實際的 data page。這也是一種 write amplification。

WAF 對 SSD 壽命的影響

SSD 壽命通常用 TBW(Total Bytes Written)或 DWPD(Drive Writes Per Day)來衡量。這些規格是基於寫入到 SSD interface 的資料量,但 NAND flash cell 真正承受的寫入量是這個數字乘以 WAF。

以一顆 1 TB TLC SSD 為例:

  • NAND flash P/E cycle 上限:3,000 次
  • 理論上 NAND flash 能承受的總寫入量:1 TB x 3,000 = 3,000 TB
  • 如果 WAF = 3(GC + 軟體層合計),NAND flash 每承受 3 TB 的寫入,其中只有 1 TB 是「有用的」
  • 所以使用者實際可寫入的資料量:3,000 TB / 3 = 1,000 TB = 1 PB
  • 如果 WAF 增加到 6(例如跑 LSM-Tree workload),可寫入量降到 500 TB
WAF vs SSD usable lifespan chart showing inverse relationship between write amplification factor and user-writable data under a fixed 3000 TB NAND budget

這就是為什麼在選擇 SSD 和設計 storage stack 時,必須把 WAF 當成一個系統層級的指標來考量。你不能只看 SSD 的 TBW 規格就假設它能撐多久,還必須估算你的 workload 在整個 stack 中會產生多少 write amplification。

一個常見的最佳化思路是:在 host 端減少不必要的寫入。例如:

  • 使用 log-structured file system 減少 random write 造成的 GC 壓力
  • 調整 LSM-Tree 的 compaction 策略(例如 tiered compaction vs leveled compaction)
  • 對冷熱資料做分離,讓熱資料集中在少數 block 中,提高 GC 回收效率
  • 使用 TRIM/UNMAP 命令告知 SSD 哪些 LBA 已不再使用,讓 SSD 提前回收空間

整個系統 WAF 的計算

當我們要估算整個系統的 WAF 時,各層的放大效果是相乘的,不是相加的。例如:

  • 應用層 LSM-Tree compaction WAF:10
  • File system journaling WAF:1.5
  • SSD GC WAF:2

整體 WAF = 10 x 1.5 x 2 = 30

這代表應用程式每寫入 1 MB 的邏輯資料,NAND flash 上實際寫了 30 MB。這個數字聽起來很誇張,但在未經最佳化的 LSM-Tree workload 上並不罕見。這也是為什麼資料庫管理員會對寫入模式斤斤計較:選錯 compaction 策略、用錯 journaling 模式,一顆原本能撐三年的 SSD 可能一年就接近壽命上限。在大規模部署中,WAF 差 2 倍代表每年要多換數百顆 SSD,成本差異以百萬計。

自問自答

  1. 一顆 SSD 的 NAND 總寫入預算為 2,400 TB(800 GB QLC,P/E cycle 上限 3,000)。如果整體 WAF 為 4,使用者實際能寫入多少 TB 的資料?如果透過最佳化把 WAF 降到 2,壽命延長多少?

  2. 為什麼 random write workload 比 sequential write workload 造成更高的 GC WAF?請從 block 內 valid page 分佈的角度解釋。

  3. 一個系統同時使用 RocksDB(LSM-Tree WAF 約 15)和 ext4 data=journal 模式(WAF 約 2),跑在一顆 GC WAF 約 2 的 SSD 上。整體 WAF 大約是多少?如果把 ext4 改為 data=ordered 模式(WAF 約 1.2),整體 WAF 能降低多少?

  4. 為什麼 TRIM/UNMAP 命令能降低 SSD 的 write amplification?它如何影響 GC 在選擇 victim block 時的效率?

  5. 有人說「買大容量的 SSD 不只是多了空間,也延長了壽命」。從 over-provisioning 和 WAF 的角度解釋這句話為什麼成立。