🌐 This page hasn't been translated yet — showing the original Chinese. Translated posts

Redis:記憶體為界的有狀態服務

· tech

#infrastructure#redis

📑 目錄

第二個有狀態的重量級是 Redis,而它跟 上一篇的 Kafka 剛好是一組完美對照:Kafka 磁碟為王,Redis 記憶體為界。兩者都有狀態,但體檢表第②題「狀態放哪」的答案不同——一個在磁碟、一個在記憶體——它們的 infra 決策就走向了完全不同的方向。

記憶體為界:硬牆在 RAM

記憶體為界:硬牆在 RAM(對照 Kafka 磁碟為王) fork headroom(持久化時可能翻倍)實體 RAM 頂 ↑ ← maxmemory 硬牆 可成長空間 已用資料(在記憶體) 資料在記憶體→ 容量硬上限 = RAM 撞牆 → evict或 noeviction 報錯 磁碟 RDB/AOF 重啟回暖 對照:Kafka 瓶頸=磁碟 throughput / 容量 · Redis 瓶頸=記憶體容量
Redis 的資料主要活在記憶體,所以它的容量有一道硬牆——maxmemory。撞到牆會怎樣?看 eviction 政策:要嘛淘汰舊 key、要嘛(noeviction)直接讓寫入報錯。而 maxmemory 不能貼著實體 RAM 設,得留一段 fork headroom——因為持久化時 fork + copy-on-write 會讓記憶體短暫暴增、最壞接近翻倍。至於落到磁碟的 RDB/AOF,不是主角,只是為了「重啟能快速回暖」。看 Redis,先看記憶體

這張圖和 Kafka 那張放在一起看,就是這系列想訓練的眼睛:同樣有狀態,但狀態的「介質」不同,瓶頸和決策就全不一樣。 Kafka 的資料在磁碟,你煩惱的是磁碟 throughput 與 TB 級容量;Redis 的資料在記憶體,你煩惱的是那道貴又硬的 RAM 牆——記憶體不像磁碟能便宜地一直加,所以 Redis 的 infra,幾乎都圍繞「怎麼在有限記憶體裡活好」打轉。

拓撲階梯:你到底需要哪一種?

Redis 的第二個核心 infra 決策,是拓撲。從單機到 Cluster 是一道階梯,每爬一階,解決一個新問題、也多一分複雜:

拓撲階梯:每爬一階,解一個新問題 可用性 / 容量 · 複雜度 ↑ ④ Cluster(分片)16384 slot 分片、突破單機 RAM|multi-key 受限、較複雜 ③ Sentinel監控 + 自動故障轉移(過半選 leader 主導 failover) ② 主從複製replica 分擔讀 + HA 基礎|故障要人工切 ① 單機最簡單|單點故障、容量 = 單機記憶體 要 HA → 上 Sentinel · 資料超過單機 RAM → 才上 Cluster 別為了不需要的規模上 Cluster——它的複雜度不是免費的
四種拓撲,對應四種需求:單機夠簡單但單點又受限於單機記憶體;主從複製讓 replica 分擔讀、也是 HA 的地基(但故障得人工切);Sentinel 加上自動故障轉移——由多個 Sentinel 過半選出一個來主導 failover;Cluster 才用 slot 分片突破單機記憶體上限,代價是 multi-key 操作受限、整體複雜。選型的關鍵是別跳級:要高可用就上 Sentinel,但只有當資料真的裝不進單機記憶體時,才需要 Cluster

容量、監控、在 k8s 上

  • 容量:記憶體是硬牆。設好 maxmemory + eviction 政策,並預留 fork headroom(別讓 maxmemory 貼死實體 RAM,否則持久化一 fork 就 OOM)。
  • 監控:used_memory vs maxmemory(離牆多近)、eviction / expired 數、快取命中率、replication lag(replica 落後多少)、slowlog(慢命令)、連線數。記憶體使用率是我第一個看的。
  • 在 k8s 上:StatefulSet + PV 放持久化檔;但重點是 memory 的 requests/limits 要設對——memory 超 limit 會被 OOMKill,而 Redis 又要留 fork headroom,所以 pod 的 memory limit 得比 maxmemory 高出一截,否則持久化一觸發就被 k8s 殺掉。這是很多人「Redis 一備份就被重啟」的元兇。

反思

同樣有狀態,「狀態放哪」決定了一切

把 Kafka 和 Redis 並排體檢,是我覺得這系列最值得的一組對照。它們都在stateful 那一端,但因為狀態的介質不同——一個磁碟、一個記憶體——幾乎所有 infra 決策都分了岔:Kafka 煩惱磁碟 throughput 與 retention,Redis 煩惱 maxmemory 與 fork headroom;Kafka 擴容是搬 partition,Redis 擴容是加 replica 或上 Cluster。這讓我對體檢表第②題的威力更有感——「有沒有狀態」只是第一層,「狀態存在什麼介質上」才真正決定一個工具的脾氣。 看一個新的有狀態系統,我現在會追問到底:它的狀態是在記憶體、本地磁碟、還是遠端儲存?答案一出來,它的瓶頸、擴縮方式、故障模式就八九不離十了。

拓撲選型,是「別過度設計」的一道考題

Redis Cluster 很酷——slot 分片、自動 rebalance、水平擴展,聽起來就很「大規模」。但我看過太多團隊,資料明明幾 GB、單機記憶體綽綽有餘,卻一上來就架 Cluster,然後被 multi-key 操作的限制、slot 遷移的複雜、維運的負擔搞得焦頭爛額。Cluster 解決的是「單機記憶體裝不下」這個特定問題;如果你沒有這個問題,它帶來的只有複雜度。 大多數人真正需要的,其實只是「主從 + Sentinel」——有 HA、有讀擴展,就夠了。這道拓撲選型題,教我一個更普遍的紀律:先確認你有那個問題,再上解那個問題的方案;為了不存在的規模預先架設複雜架構,是我看過最常見、也最貴的一種過度設計。

記憶體是硬牆,而硬牆逼你面對取捨

我最喜歡 Redis 的一點,反而是它的「限制」——記憶體不像磁碟,不能便宜地一直加,所以它逼著你面對取捨:哪些資料值得放進這麼貴的記憶體?該設多長的 TTL?撞牆時要淘汰誰?這些問題,磁碟系統可以靠「再加一顆硬碟」拖延,但記憶體系統躲不掉。而我發現,正是這種「資源有硬上限」的約束,反而養出更好的設計直覺——當你不能無限擴張,你就被迫去想「什麼才是真正重要、值得留下的」。無論是記憶體、時間、還是團隊的注意力,有限的資源逼出的取捨,往往比無限的資源養出的鋪張,更接近好的工程