Lazy Queue
說明 Lazy Queue 的運作原理,如何將訊息直接寫入磁碟以減少記憶體壓力,適用於大量訊息堆積的場景
Classic queue 在堆積時會拖垮 broker
RabbitMQ 最危險的生產事故之一是訊息堆積:consumer 掛掉、處理變慢、下游不可用時訊息會在 queue 裡累積,然後整個 broker 陷入連鎖反應。理解 lazy queue 之前,要先知道 classic queue 為什麼會出這種問題。
Classic queue 預設策略是盡量把訊息放記憶體。訊息進來放 RAM、consumer 從 RAM 取,吞吐和延遲都最佳;只有當記憶體壓力超過 vm_memory_high_watermark(預設 40% RAM)時才 page out 到磁碟。這在訊息穩定流動時很好——但堆積時會變成陷阱。
場景:consumer 從每秒 1000 筆掉到 100 筆、producer 還是 1000 筆送進來。差額 900 筆/秒累積在 queue,幾分鐘就是幾十萬筆堆在記憶體。觸到 high watermark 後 RabbitMQ 開始 paging,paging 本身要吃 CPU 和 IO 拖慢 broker;再往上壓則觸發 memory alarm,整個 broker 停止接收新訊息,所有 producer 被阻塞,連鎖反應擴散到整個系統。
根因是 classic queue 的預設策略針對「小量、穩定流動」最佳化,對「大量、可能堆積」是災難。
Lazy queue:訊息直接寫磁碟
Lazy queue 的策略相反:訊息一進來就寫磁碟,只在 consumer 取時才載入記憶體。無論 queue 裡有 10 萬筆還是 1000 萬筆,記憶體只保留 buffer 與索引,大約幾十 MB。訊息可以堆積到磁碟上限而不影響 broker 記憶體——把問題從「記憶體」轉化成「磁碟空間」,而磁碟大上幾個數量級且容易擴充。
啟用方式
宣告時指定:
await channel.declare_queue(
"big_backlog",
durable=True,
arguments={"x-queue-mode": "lazy"},
)
或用 policy(推薦,不用改程式):
rabbitmqctl set_policy Lazy "^lazy\." \
'{"queue-mode":"lazy"}' \
--apply-to queues
已有訊息的 queue 切換 mode 會觸發全部重寫,挑維運時間執行。
兩種模式的取捨
| 項目 | Default Queue | Lazy Queue |
|---|---|---|
| 入隊速度 | 快(寫 RAM) | 較慢(寫磁碟) |
| 出隊速度 | 非常快 | 較慢(讀磁碟) |
| 記憶體使用 | 高 | 極低 |
| 堆積能力 | 受記憶體限制 | 受磁碟限制 |
| Broker 穩定性 | 堆積會拖垮 | 穩如磐石 |
Lazy queue 的吞吐大約是 default 的一半(例如 default 50k msg/s,lazy 25k~30k msg/s),換來的是堆積時的穩定性。問題從「誰比較好」變成「能不能接受吞吐減半,換堆積穩定性」。
適用與不適用
適合:訊息量可能暴增、consumer 速度不穩、堆積是正常狀態、延遲不敏感。例如促銷突發流量、爬蟲、批次 ETL、AI 推論、影片轉碼、跨夜批次。
不適合:超低延遲(高頻交易、即時排行榜、廣告競價)、訊息量穩定不堆積(用 default 就好)、訊息小又多且每秒極高(磁碟 IOPS 會變瓶頸)。
無法判斷時預設用 lazy。吞吐從 50k 降到 25k 通常不是瓶頸,但穩定性的提升通常是生產環境的救命稻草。
和 Persistent Message 的差別
| 概念 | 解決的問題 | 訊息放哪? |
|---|---|---|
| Persistent Message | Broker 重啟後不丟訊息 | Flush 到磁碟,但仍在 RAM |
| Lazy Queue | 堆積時不爆 RAM | 直接寫磁碟,幾乎不進 RAM |
Persistent message (delivery_mode=2) 是「壞事發生時能還原」,classic queue 預設仍把它放 RAM 優化消費。Lazy queue 則是「平常就放磁碟」的策略。兩者正交,可以同時使用——生產環境建議組合是 delivery_mode=2 + x-queue-mode=lazy + durable + publisher confirms + quorum queue。
和 Quorum Queue 的關係
Quorum queue 天生就內建類似 lazy 的行為:訊息寫到 Raft log(磁碟),記憶體只保留索引與 in-flight。用了 quorum queue 就不需要也不能再設 x-queue-mode=lazy。新專案直接用 quorum queue,一口氣解決 HA 與記憶體穩定性。
注意事項
- Mode 切換會觸發訊息重寫,挑流量低時段執行。
- Lazy queue 對磁碟壓力大,HDD 無法應付,必須 SSD 或 NVMe。
- 單筆處理延遲多幾毫秒到十幾毫秒,超低延遲場景需考量。