Redis进阶-Redis键值设计及BigKey问题
文章目錄
- 鍵值設計
- key設計
- value設計
- big key
- 定義
- 反例
- bigkey的產生
- 如何優化bigkey
- 刪除bigKey的注意事項
- bigkey的危害
鍵值設計
key設計
-
(1)【建議】: 可讀性和可管理性
以業務名(或數據庫名)為前綴(防止key沖突),用冒號分隔,比如業務名:表名:id
o2o:order:1
-
(2)【建議】:簡潔性
保證語義的前提下,控制key的長度,當key較多時,內存占用也不容忽視,例如:
user:{uid}:friends:messages:{mid} 簡化為 u:{uid}🇫🇷m:{mid}
-
(3)【強制】:不要包含特殊字符
反例:包含空格、換行、單雙引號以及其他轉義字符
value設計
-
(1)【強制】:拒絕bigkey(防止網卡流量、慢查詢)
-
(1)(2)【推薦】:選擇適合的數據類型。
例如:實體類型(要合理控制和使用數據結構,但也要注意節省內存和性能之間的平衡)反例:
set user:1:name tomset user:1:age 19set user:1:favor football正例:
hmset user:1 name tom age 19 favor football
- (1)3.【推薦】:控制key的生命周期,redis不是垃圾桶
建議使用expire設置過期時間(條件允許可以打散過期時間,防止集中過期)
big key
我們知道,Redis 的一個字符串最大512M,一個二級數據結構(比如 hash、list、set 、zset)可以存儲2^32-1 個元素 ,約40億個元素。 但是不是以為著我們可以任意存儲元素呢? 時刻牢記,在讀寫這個角度上,目前Redis還是單線程的。
定義
其實不然,按照經驗來說 ,如何定義bigKey 呢?
- 字符串類型:它的big體現在單個value值很大,一般認為超過10KB就是bigkey。
- 非字符串類型:哈希、列表、集合、有序集合,它們的big體現在元素個數太多。
一般來說,string類型控制在10KB以內,hash、list、set、zset元素個數不要超過5000。當然了這不是絕對的,請依據場景,靈活處理。
反例
我們來看個反例: 一個hash 存儲用戶信息,我有100萬用戶,我全都放到一個key里。。。。這不管從哪個角度看 ,bigkey無疑。
bigkey的產生
一般來說,bigkey的產生都是由于程序設計不當,或者對于數據規模預料不清楚造成的,來看幾個例子:
- 社交類:粉絲列表,如果某些明星的粉絲數據,如果不精心設計下,一個明星的粉絲 百萬很少了吧,你都把這百萬的粉絲數據放到一個key中存儲,毫無疑問是bigkey
- 統計類:比如按天存儲某項功能或者網站的用戶集合,用戶很少,倒是沒多大問題,一旦用戶多了起來,必是bigkey
- 緩存類:將數據從數據庫加載出來以后序列化放到Redis里,這個方式非常常用,但有兩個地方需要注意,第一,是不是有必要把所有字段都緩存;第二,有沒有相關關聯的數據,不要為了圖方便把相關數據都存一個key下,產生bigkey。
如何優化bigkey
核心思想: 分治 拆分
-
拆
big list: list1、list2、…listN
big hash:可以講數據分段存儲,比如一個大的key,假設存了1百萬的用戶數據,可以拆分成 200個key,每個key下面存放5000個用戶數據 -
如果bigkey不可避免,也要思考一下要不要每次把所有元素都取出來(例如有時候僅僅需要
hmget,而不是hgetall),刪除也是一樣,盡量使用優雅的方式來處理 -
【推薦】:選擇適合的數據類型
例如:實體類型(要合理控制和使用數據結構,但也要注意節省內存和性能之間的平衡)
set user:1:name tom set user:1:age 19 set user:1:favor football
反例:正例:
hmset user:1 name tom age 19 favor football -
【推薦】:控制key的生命周期,redis不是垃圾桶
建議使用expire設置過期時間(條件允許可以打散過期時間,防止集中過期)。
刪除bigKey的注意事項
對于非字符串的bigkey,比如 hash list set zset , 不要使用del 刪除, 請使用 hscan 、sscan、zscan方式漸進式刪除。
同時要注意防止bigkey過期時間自動刪除問題(例如一個100萬的hash設置1小時過期,會觸發del操作,造成阻塞)
bigkey的危害
-
導致redis阻塞
這個也很好理解: 我們知道Redis 官方號稱10萬QPS, 我們通常打不到這個值,但是大幾萬的QPS還是沒問題的,這也就意味著 redis 的執行速度 1秒幾萬條, 速度相當的快的。 假設你有個bigKey , 操作一次耗時1秒,那Redis 單線程 在這1秒鐘就只能處理你這個Key, 后面堵了一堆請求。。。。 并且你的應用 序列化和反序列化這種大key , 也消耗CPU 。
-
導致網絡擁塞
假設我們的交換機,千兆網絡(小b),那么 實際帶寬 1024 / 8 = 128M . 假設你的這個key的大小 500KB, 客戶端并發 1000獲取這個key, 那么就意味著 1000 * 500KB = 500M ,那就是每秒產生500M的流量。先不說你的Redis能不能處理的過來這個并發下的bigKey,單說你的這個千兆網絡, 你說你這個網絡I/O能扛得住嗎? 一般服務器會采用單機多實例的方式來部署,也就是說一個bigkey可能會對其他實例也造成影響,其后果不堪設想。
-
過期刪除- Redis4.0新特性(三)-Lazy Free
針對那種我們設置了過期時間的big key , 在redis4.0前,沒有lazy free功能,我們只能通過類似scan big key,每次刪除少量的元素,分多次刪除;但在面對“被動”刪除鍵的場景,這種取巧的刪除就無能為力。
舉個例子:Redis Cluster大集群,業務緩慢地寫入一個帶有TTL的2000多萬個字段的Hash鍵,當這個鍵過期時,redis開始被動清理它時,導致redis被阻塞20多秒,結果發生了fail over ,造成故障。
Redis 4.0提供了過期異步刪除(lazyfree-lazyexpire yes)
lazy free 惰性刪除或延遲釋放: 當刪除鍵的時候,redis提供異步延時釋放key內存的功能,把key釋放操作放在Background I/O單獨的子線程處理中,減少刪除big key對redis主線程的阻塞,有效地避免刪除big key帶來的性能和可用性問題。
redis4.0有lazy free功能后,這類主動或被動的刪除big key時,時間復雜度O(1)。
總結
以上是生活随笔為你收集整理的Redis进阶-Redis键值设计及BigKey问题的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Redis进阶-Redis缓存优化
- 下一篇: Redis进阶-Redis使用建议一二事