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