Redis 實戰心得

fcamel
fcamel的程式開發心得
6 min readJul 7, 2020

網路上很多這類心得,像這篇提供許多有用的建議。本篇是我最近實戰後的隨手記。

資料分群

Redis 沒有 table,資料存在同一個地方。這篇分析三種依服務類型存資料的方法:

  • namespace: 在 key 的前面加上如 user: 的 prefix,所有資料放在一個 Redis 內。
  • 使用 database: 用指令 select 切換用不同的 “database”。
  • 用不同的 Redis servers。

Redis Cluster 只能用 database 0,所以為了長遠 scalability 考量,database 並不適用。Redis 作者甚至認為 database 是他作過 Redis 設計裡最糟的決定。

用不同 Redis servers 管理成本較高。或許分成兩群 Redis Cluster 會不錯?例如一群設成 LRU mode 存 cache 資料;另一群存 persistent data。這樣可以各別設適合的 shards、replicas 和 OOM 時的處理方式。

關於 namespace 的作法,建議用 : 區分,因為不少 keys 分析的工具,假設用 : 為 keys 分群。如果已有大量資料沒用 : 分群,可以用 redis-audit,它會自動分群,並可加入部份規則,減少自動分群花的時間。

Keys 的格式

可以的話,不要用 binary 會比較方便。像 redis-cli --scan 無法處理 non-printable 字元。要掃出全部 keys 得另寫小程式。雖然很多事都可寫小程式解決,但是分析和除錯時,就沒那麼方便。例如前面提到的 redis-audit,無法處理 binary keys,在 parsing error 的地方得加上 key.scrub(‘’)才能用。

使用 Expiration

資料多了以後,很難管理。能加上 expiration 就加,避免無止盡的堆垃圾到記憶體裡。

Redis 只支援第一層 keys 加 expiration,若需要用 hash 但 hash 部份內容需要加 expiration,最好將一個大 hash 攤成多個小 hash 或是 string,這樣才能善用 expiration。附帶一提,多個小 hash 比一個大 hash 更省空間,因為 Redis 有針對小 hash 最佳化儲存方式

分析占空間的 keys

除前面提的分析工具外 ( 我是用 redis-audit ),用 redis-cli 也可以找出顯著的問題:

  • redis-cli scan 0 count 100 取樣 100 筆,找出常見的 keys,看看數量分佈是否合理。0 可以換成其它數字,redis-cli 仍能正常運作,藉此當作簡易取樣。
  • redis-cli --bigkeys,這指令會取樣找出各資料類型最大的 keys。
  • DEBUG OBJECT KEY 看 KEY serialized 後的大小。
  • OBJECT IDLETIME KEY 看 KEY 多久沒有被存取。注意,預設設定下,它不會傳回正確的值。

Latency

官方文件 latency 有落落長的教學,另寫一篇摘要重要的部份

我自己有用到的是:

  • 使用 benchmark 了解自己機器合理的 op/s。
  • slowlog 找 ≥ 10ms 的操作。Redis 只有main thread 處理資料,有 100 個 10ms 操作,一秒就只能處理 100 個指令而已了。 10ms 是很久的。
  • 使用 unlink 取代 del 刪除 key。Redis 會用 background thread 處理 unlink 後的 keys,不會像 del 卡到 main thread。試想砍一個 ≥ 1M 筆資料的 hash / zset,可能會花 10s 以上。這表示 Redis 服務會停擺 10s。
  • 留意 Redis client lib 是否有自己的 thread pool。如果是在 lib 的 thread pool 處理 Redis 返回值,要將花 CPU 時間的操作轉移到別的 thread 執行,避免卡到 Redis client lib 的 thread pool。
  • 開啟 latency monitor,使用 latency doctor 檢查有何可改善之處。

Lua Script

127.0.0.1:6379> eval 'return struct.unpack(">i4", ARGV[1])' 0 "\x01\x02\x03\x04"
(integer) 16909060

同步資料

Redis 有 cluster mode 也有單節點的 master-replica 模式。有些別人寫好的工具,可用來同步 standalone node 和 redis cluster,例如 redis-shake。在升級單一節點為 redis cluster 時,可以減少停機時間。

Redis Cluster Client Library 的設定

Redis 有多種模式,像是單機、Master-Replica、Sentinel、Cluster,這些模式 Lettuce 都支援。但要留意 cluster mode 預設只有建立第一個連線後會取得 cluster node topology,之後不會處理 topology change。也就是說,failover 後,app (即 redis client) 不會知道要連往新的 master,導致服務繼續處於異常狀態。

Lettuce 提供三種方式 refresh topology

  1. App 手動呼叫 RedisClusterClient.reloadPartitions()
  2. 定期在背景呼叫 reloadPartitions()
  3. 動態偵測某些條件 (例如收到 MOVED 或是重連數次) 後呼叫 reloadPartitions()

無腦地在背景呼叫最單純,但效能不佳,因為所有 redis client 會連往所有它們知道的節點。也可以設定使用 initial seeds 而非 discovered nodes,減少負擔。

綜合一些考量,設定 connection/command timeout,並在發生 connection/command timeout 後自己手動呼叫 reloadPartitions(),行為比較好控制。或是遇到意外狀況時,重建 client 也行。

之後有什麼適合的內容,再持續補完。

--

--