---
# System prepended metadata

title: Redis 延遲波動與性能變慢問題整理（下）

---

# 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 一個「安靜」的環境，有助於保持穩定高效的性能。

