這個問題在面試中還是比較容易遇到的,尤其是在考察 Redis 性能優化相關知識點的時候。
通常情況下,問了 bigkey(大 Key)還會繼續問 hotkey(熱 Key)。即使不準備面試也建議看看,實際開發中也能夠用到(hotkey 相關的內容會在下一篇文章中提到)。
什麼是 bigkey?
簡單來説,如果一個 key 對應的 value 所佔用的內存比較大,那這個 key 就可以看作是 bigkey。具體多大才算大呢?有一個不是特別精確的參考標準:
- String 類型的 value 超過 1MB
- 複合類型(List、Hash、Set、Sorted Set 等)的 value 包含的元素超過 5000 個(不過,對於複合類型的 value 來説,不一定包含的元素越多,佔用的內存就越多)。
bigkey 是怎麼產生的?有什麼危害?
bigkey 通常是由於下面這些原因產生的:
- 程序設計不當,比如直接使用 String 類型存儲較大的文件對應的二進制數據。
- 對於業務的數據規模考慮不周到,比如使用集合類型的時候沒有考慮到數據量的快速增長。
- 未及時清理垃圾數據,比如哈希中冗餘了大量的無用鍵值對。
bigkey 除了會消耗更多的內存空間和帶寬,還會對性能造成比較大的影響。
在 Redis 常見阻塞原因總結這篇文章中我們提到:大 key 還會造成阻塞問題。具體來説,主要體現在下面三個方面:
- 客户端超時阻塞:由於 Redis 執行命令是單線程處理,然後在操作大 key 時會比較耗時,那麼就會阻塞 Redis,從客户端這一視角看,就是很久很久都沒有響應。
- 網絡阻塞:每次獲取大 key 產生的網絡流量較大,如果一個 key 的大小是 1 MB,每秒訪問量為 1000,那麼每秒會產生 1000MB 的流量,這對於普通千兆網卡的服務器來説是災難性的。
- 工作線程阻塞:如果使用 del 刪除大 key 時,會阻塞工作線程,這樣就沒辦法處理後續的命令。
大 key 造成的阻塞問題還會進一步影響到主從同步和集羣擴容。
綜上,大 key 帶來的潛在問題是非常多的,我們應該儘量避免 Redis 中存在 bigkey。
如何發現 bigkey?
1、使用 Redis 自帶的 --bigkeys 參數來查找。
# redis-cli -p 6379 --bigkeys
# Scanning the entire keyspace to find biggest keys as well as
# average sizes per key type. You can use -i 0.1 to sleep 0.1 sec
# per 100 SCAN commands (not usually needed).
[00.00%] Biggest string found so far '"ballcat:oauth:refresh_auth:f6cdb384-9a9d-4f2f-af01-dc3f28057c20"' with 4437 bytes
[00.00%] Biggest list found so far '"my-list"' with 17 items
-------- summary -------
Sampled 5 keys in the keyspace!
Total key length in bytes is 264 (avg len 52.80)
Biggest list found '"my-list"' has 17 items
Biggest string found '"ballcat:oauth:refresh_auth:f6cdb384-9a9d-4f2f-af01-dc3f28057c20"' has 4437 bytes
1 lists with 17 items (20.00% of keys, avg size 17.00)
0 hashs with 0 fields (00.00% of keys, avg size 0.00)
4 strings with 4831 bytes (80.00% of keys, avg size 1207.75)
0 streams with 0 entries (00.00% of keys, avg size 0.00)
0 sets with 0 members (00.00% of keys, avg size 0.00)
0 zsets with 0 members (00.00% of keys, avg size 0.00
從這個命令的運行結果,我們可以看出:這個命令會掃描(Scan) Redis 中的所有 key ,會對 Redis 的性能有一點影響。並且,這種方式只能找出每種數據結構 top 1 bigkey(佔用內存最大的 String 數據類型,包含元素最多的複合數據類型)。然而,一個 key 的元素多並不代表佔用內存也多,需要我們根據具體的業務情況來進一步判斷。
在線上執行該命令時,為了降低對 Redis 的影響,需要指定 -i 參數控制掃描的頻率。redis-cli -p 6379 --bigkeys -i 3 表示掃描過程中每次掃描後休息的時間間隔為 3 秒。
2、使用 Redis 自帶的 SCAN 命令
SCAN 命令可以按照一定的模式和數量返回匹配的 key。獲取了 key 之後,可以利用 STRLEN、HLEN、LLEN等命令返回其長度或成員數量。
| 數據結構 | 命令 | 複雜度 | 結果(對應 key) |
|---|---|---|---|
| String | STRLEN | O(1) | 字符串值的長度 |
| Hash | HLEN | O(1) | 哈希表中字段的數量 |
| List | LLEN | O(1) | 列表元素數量 |
| Set | SCARD | O(1) | 集合元素數量 |
| Sorted Set | ZCARD | O(1) | 有序集合的元素數量 |
對於集合類型還可以使用 MEMORY USAGE 命令(Redis 4.0+),這個命令會返回鍵值對佔用的內存空間。
3、藉助開源工具分析 RDB 文件。
通過分析 RDB 文件來找出 big key。這種方案的前提是你的 Redis 採用的是 RDB 持久化。
網上有現成的代碼/工具可以直接拿來使用:
- redis-rdb-tools:Python 語言寫的用來分析 Redis 的 RDB 快照文件用的工具
- rdb_bigkeys : Go 語言寫的用來分析 Redis 的 RDB 快照文件用的工具,性能更好。
4、藉助公有云的 Redis 分析服務。
如果你用的是公有云的 Redis 服務的話,可以看看其是否提供了 key 分析功能(一般都提供了)。
這裏以阿里雲 Redis 為例説明,它支持 bigkey 實時分析、發現,文檔地址:https://www.alibabacloud.com/help/zh/apsaradb-for-redis/lates... 。
如何處理 bigkey?
bigkey 的常見處理以及優化辦法如下(這些方法可以配合起來使用):
- 分割 bigkey:將一個 bigkey 分割為多個小 key。例如,將一個含有上萬字段數量的 Hash 按照一定策略(比如二次哈希)拆分為多個 Hash。
- 手動清理:Redis 4.0+ 可以使用
UNLINK命令來異步刪除一個或多個指定的 key。Redis 4.0 以下可以考慮使用SCAN命令結合DEL命令來分批次刪除。 - 採用合適的數據結構:例如,文件二進制數據不使用 String 保存、使用 HyperLogLog 統計頁面 UV、Bitmap 保存狀態信息(0/1)。
- 開啓 lazy-free(惰性刪除/延遲釋放) :lazy-free 特性是 Redis 4.0 開始引入的,指的是讓 Redis 採用異步方式延遲釋放 key 使用的內存,將該操作交給單獨的子線程處理,避免阻塞主線程。