# Redis 延遲波動與性能變慢問題整理(下) ## 一、文件系統與持久化 ### Redis 持久化依賴於文件系統 - Redis 是內存資料庫,但會透過持久化機制(AOF 或 RDB)將資料寫入磁碟。 - 文件系統的寫入機制會直接影響持久化效率。 ### AOF 三種寫回策略與系統調用 - Redis AOF 支援三種寫回策略: - `no`:不自動同步寫入磁碟。 - `everysec`:每秒同步一次。 - `always`:每筆操作都同步。 - 寫回依賴兩個系統調用: - `write`:寫入到內核緩衝區即可返回。(還沒寫入磁碟) - `fsync`:必須寫入到磁碟才能返回。 ### 每種策略的實作差異 - `everysec`: - 允許最多丟失一秒的資料。 - 使用後台子執行緒執行 `fsync`,避免阻塞主執行緒。 - `always`: - 每筆操作都需同步寫入磁碟。 - `fsync` 在主執行緒中執行,確保立即性與資料一致性。 ### AOF 重寫與磁碟壓力 - Redis 為避免 AOF 日誌檔過大,會進行 AOF 重寫(rewrite)=> 經由重寫來減少指令紀錄來優化。 - AOF 重寫使用子進程執行,以避免阻塞主執行緒。 - 但重寫過程中若磁碟 IO 壓力大,會導致: - `fsync` 子執行緒被阻塞:當 AOF 重寫進程進行大量磁碟寫入時,會佔用大量磁碟 IO 資源,造成 `fsync` 操作變慢甚至卡住。 - Redis 主執行緒發現上一次 `fsync` 未完成會等待,進而發生阻塞:雖然 `fsync` 是由背景子執行緒處理,但為了確保寫入資料的順序與一致性,主執行緒每次寫入後會檢查上一個 `fsync` 是否完成。如果未完成,主執行緒會等待 `fsync` 結束後再繼續處理下一個寫入。這樣的設計是為了避免資料重放順序錯亂或一致性問題,但也意味著當 `fsync` 被磁碟 IO 拖慢時,Redis 整體的請求處理會被連帶拖慢。 ### 圖解說明 ![截圖 2025-07-16 下午5.21.24](https://hackmd.io/_uploads/HJJXklSIgl.png) - 展示磁碟壓力小與大時,`fsync` 子執行緒對主執行緒的影響差異。 ## 二、排查與優化建議 ### 檢查 AOF 策略 - `appendfsync` 可設定為: - `no` - `everysec` - `always` - 根據業務對資料可靠性的需求選擇適當策略。 ### 設定 `no-appendfsync-on-rewrite` - 若允許部分資料丟失,設定為: ```bash! no-appendfsync-on-rewrite yes ``` - 表示在 AOF 重寫期間不進行 `fsync`,減少阻塞。 ### 使用高速 SSD - 若需高效能與高可靠性,建議使用 SSD 作為 AOF 寫入設備。 - SSD 提供更高 IO 吞吐與並發能力,可降低 `fsync` 與重寫對主執行緒的影響。 --- ## 三、操作系統層級的影響:Swap 問題 ### Swap 是什麼 - Swap 是作業系統為了彌補實體記憶體不足而提供的一種「交換空間」機制,會將記憶體中暫時不用的資料移到磁碟上,釋放出記憶體空間給其他程序使用。 - 實際上是把資料從 RAM 搬到磁碟中的 Swap 區域,稱為「swap out」;當資料再次需要時,會從磁碟搬回 RAM,稱為「swap in」。 - 因為磁碟的讀寫速度遠慢於記憶體,這個過程會大幅增加磁碟 IO,導致系統整體效能下降。 - Redis 是全記憶體型資料庫,所有資料都存在 RAM 中以保證高速存取。 - 一旦 Redis 使用的記憶體被系統 swap 出去: - Redis 處理某筆資料時會發現它已不在記憶體中,必須等待資料從磁碟讀回,造成明顯延遲。 - 與 AOF 檔案使用 fsync 時只會影響背景執行緒不同,Swap 發生時會直接影響 Redis 的主執行緒,導致整體操作都變慢。 - Swap 對 Redis 造成的影響可能非常劇烈,尤其在記憶體壓力大、或 Redis 和其他高記憶體應用共享同一台機器時更容易發生。 ### 案例分析 - 一個 Redis 實例正常處理 5000 萬筆 GET 請求僅需 300 秒。 - 觸發 swap 後,處理時間增為 4 小時,效能下降近 48 倍。 ### 觸發 Swap 的情況 1. Redis 本身佔用記憶體過多。 2. 與 Redis 共享機器的其他應用產生大量磁碟 IO,佔用記憶體資源。 ### Swap 檢查方式 1. 查詢 Redis 進程 ID: ```bash! redis-cli info | grep process_id ``` 2. 進入 /proc 目錄檢查 swap 使用情況: ```bash! cd /proc/<process_id> cat smaps ``` 3. Swap 與 Size 對應代表該記憶體區塊已被換出到磁碟。 ### Swap 判斷指標 - 若單個記憶體區塊 swap 使用達百 MB 甚至 GB,表示記憶體壓力已非常大。 ### 解決建議 - 增加機器記憶體。 - 使用 Redis Cluster 分攤記憶體壓力。 - Redis 遷移至獨立機器。 - 主從切換:將記憶體大的從庫設為主庫,處理前端請求。 --- ## 四、操作系統層級:內存大頁(Transparent Huge Page, THP) #### 背景:記憶體是怎麼管理的? - 作業系統在分配記憶體時,會以「**頁(page)**」為單位來管理。 - 一般情況下,Linux 使用的標準記憶體頁大小為 **4KB(4096 bytes)**,這叫做「普通頁(Normal Page)」。 - 但從 Linux kernel 2.6.38 開始,作業系統支援「透明大頁機制(THP)」,也就是**每一頁記憶體可以用 2MB 的大小**。 #### 為什麼有大頁機制? - 當系統需要分配大量記憶體時(例如資料庫、大型應用),用 4KB 一頁的方式會: - 建立大量的頁面映射(page mapping)記錄。 - 增加 CPU 管理記憶體的負擔(需要追蹤更多的頁表 entry)。 - 使用 2MB 大頁的好處: - **減少頁表數量**,降低記憶體管理開銷。 - **提高 TLB(Translation Lookaside Buffer)命中率**,TLB 是 CPU 快取記憶體位置的暫存器,頁面越大命中率越高,能加快資料存取。 ### 為什麼大頁不適合 Redis? - 表面上看,大頁能減少內存分配次數,似乎對 Redis 有利。 - 但 Redis 為確保資料一致性,進行持久化時會用「寫時複製(Copy-on-write)」。 - 若使用大頁,修改 100B 的資料也要複製整個 2MB 大頁。 - 若使用一般頁面,只需複製 4KB。 - 結果:使用 THP 時,頻繁寫入操作會導致大量不必要的記憶體拷貝,影響 Redis 效能。 ### 如何檢查與關閉 THP? - 檢查目前狀態: ```bash! cat /sys/kernel/mm/transparent_hugepage/enabled ``` - 若顯示為 `always`,表示 THP 啟用。 - 若為 `never`,則表示已禁用。 - 關閉 THP 的指令: ```bash! echo never > /sys/kernel/mm/transparent_hugepage/enabled ``` --- ## 五、Redis 效能排查 Checklist(9 點建議) 1. **基線性能是否穩定** - 確認是否使用慢查詢指令,盡可能改為輕量操作或交由客戶端計算。 2. **是否大量 Key 設定了相同過期時間** - 建議為每個 Key 加入隨機的過期時間,避免同時批量刪除導致阻塞。 3. **是否存在 bigkey** - Redis 4.0+ 可利用異步刪除機制減少阻塞。 - Redis 4.0- 建議使用 SCAN 命令分批處理。 4. **AOF 策略設定是否合理** - 根據實際業務容忍度設定 `appendfsync`(`everysec`, `always`, `no`)。 - 若可容忍少量資料丟失,可將: ``` no-appendfsync-on-rewrite yes ``` 減少 fsync 和 AOF 重寫磁碟 IO 競爭。 5. **磁碟效能是否足夠** - 若需高效能與可靠性,建議使用高速 SSD 作為 AOF 寫入磁碟。 6. **是否觸發了 swap** - Redis 使用內存大,應避免 swap。 - 解法:加大機器記憶體、使用 Redis 集群、隔離 Redis 與其他高記憶體需求程序。 7. **是否啟用了 THP(透明大頁)** - 建議禁用透明大頁(THP)機制,以避免不必要的記憶體複製。 8. **是否為主從架構** - 主庫記憶體建議控制在 2~4GB,避免從庫加載 RDB 檔時阻塞。 9. **是否為多核心或 NUMA 架構** - 可考慮綁定 Redis 實例至特定 CPU 核。 - 若為 NUMA 架構,將 Redis 與網路中斷處理綁定至同一 Socket。 - NUMA(Non-Uniform Memory Access) 是一種 多處理器系統的記憶體架構,每個 CPU(或 CPU 群組)有自己的本地記憶體,但也可以存取其他 CPU 的記憶體。 --- ## 六、其他建議:注意 Redis 的「鄰居」 - Redis 所在機器上,若有其他程序佔用大量記憶體、磁碟 IO 或網路 IO,例如: - 數據庫程式 - 數據採集程式 - 建議將這些程式遷移至其他機器,讓 Redis 獲得足夠的運算、記憶體與 IO 資源。 - 給 Redis 一個「安靜」的環境,有助於保持穩定高效的性能。