<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
在Redis中,良好的鍵值設計可以達成事半功倍的效果,而不好的鍵值設計可能會帶來Redis服務停滯,網路阻塞,CPU使用率飆升等一系列問題,今天就教大家如何設計一個良好的key-value
Redis的Key雖然可以自定義,但最好遵循下面的幾個最佳實踐約定:
遵循基本格式:[業務名稱]:[資料名]:[id]
,例如我們的登入業務,需要儲存使用者資訊,其key可以設計成如下格式
這種設計的好處不僅在於可讀性強,還在於可以避免key的衝突問題,而且方便管理
Key的長度不超過44位元組
無論是哪種資料型別, key都是string型別,string型別的底層編碼包含int、embstr和raw三種。如果key中全是數位,那麼就會直接以int型別去儲存,而int佔用的空間也是最小的,當然出於業務需求,我們不可能將key設計為一個全數位的,而如果不是純數位,底層儲存的就是SDS內容,如果小於44位元組,就會使用embstr型別,embstr在記憶體中是一段連續的儲存空間,記憶體佔用相對raw來說較小,而當位元組數大於44位元組時,會轉為raw模式儲存,在raw模式下,記憶體空間不是連續的,而是採用一個指標指向了另外一段記憶體空間,在這段空間裡儲存SDS內容,這樣空間不連續,存取的時候效能也就會收到影響,還有可能產生記憶體碎片
需要注意的是,如果你的redis版本低於4.0,那麼界限是39位元組而非44位元組
Key中不包含一些特殊字元
BigKey顧名思義就是一個很大的Key,這裡的大並不是指Key本身很大,而是指包括這個Key的Value在內的一整個鍵值對很大
BigKey通常以Key-Value的大小或者Key中成員的數量來綜合判定,例如:
那麼如何判斷元素的大小呢?redis中為我們提供了相應的命令,語法如下:
memory usage 鍵名
這條命令會返回一條資料佔用記憶體的總大小,這個大小不僅包括Key和Value的大小,還包括資料儲存時的一些元資訊,因此可能你的Key與Value只佔用了幾十個位元組,但最終的返回結果是幾百個位元組
但是我們一般不推薦使用memory指令,因為這個指令對CPU的佔用率是很高的,實際開發中我們一般只需要衡量Value的大小或者Key中的成員數即可
例如如果我們使用的資料型別是String,就可以使用以下命令,返回的結果是Value的長度
strlen 鍵名
如果我們使用的資料型別是List,就可以使用以下命令,返回的結果是List中成員的個數
llen 鍵名
一般我們推薦,單個key的value小於10KB,集合型別的key元素數量小於1000
網路阻塞
當我們對一個BigKey發起讀請求時,只需少量的QPS就可能導致頻寬使用率被佔滿,導致Redis範例乃至所在物理機變慢,例如一個bigkey佔用5M記憶體,只需要QPS達到20,那麼1秒鐘就會佔100M的頻寬
資料傾斜
叢集環境下,由於所有插槽一開始都是均衡分配的,因此BigKey所在的Redis範例記憶體使用率會遠超其他範例,從而無法使資料分片的記憶體資源達到均衡,最後不得不手動重新分配插槽,增加運維人員的負擔
Redis阻塞
對元素較多的hash、list、zset等做運算會耗時較久,而且由於Redis是單執行緒的,在運算過程中會導致服務阻塞,無法接收其他使用者請求
CPU壓力
對BigKey的資料進行序列化或反序列化都會導致CPU的使用率飆升,影響Redis範例和本機其它應用
既然我們知道了什麼叫BigKey以及BigKey的危害,那麼如何去快速發現Redis中所有的BigKey呢?這裡為大家提供以下幾種方案:
1)利用Redis本身提供的命令
利用以下命令,可以遍歷分析所有key,並返回Key的整體統計資訊與每種資料型別中Top1的BigKey
redis-cli -a 密碼 --bigkeys
演示如下(這裡我的redis沒有設定密碼,如果你的redis設定了密碼,則需要使用 -a 密碼
進行連線)
2)自己手動編寫程式進行掃描
我們可以通過自己編寫程式,將Redis中所有的資料查詢出來並一一統計長度來找出BigKey,這裡不建議使用keys *
來查詢所有資料,因為keys *
是一次將所有的資料全部查詢出來,如果資料量很大,key *
一次可能要幾十秒甚至幾分鐘,在如此長的時間內,Redis的主執行緒會因為執行該命令而被阻塞。
這裡建議使用redis提供的scan命令,語法如下:
scan 起始位置 count 數量
scan掃描有點類似於分頁查詢,而被分頁的物件是redis中所有的資料,scan命令呼叫一次只會從指定的起始位置開始返回指定數量的資料,以及此次掃描結束時遊標所在的位置,下一次掃描時就需要從這個遊標開始繼續往下掃描
這裡提供一個已經編寫好的查詢BigKey的測試類,大家可以參考一下
import com.heima.jedis.util.JedisConnectionFactory; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import redis.clients.jedis.Jedis; import redis.clients.jedis.ScanResult; import java.util.HashMap; import java.util.List; import java.util.Map; public class JedisTest { private Jedis jedis; @BeforeEach void setUp() { // 1.建立連線 // jedis = new Jedis("192.168.150.101", 6379); jedis = JedisConnectionFactory.getJedis(); // 2.設定密碼 jedis.auth("123321"); // 3.選擇庫 jedis.select(0); } //設定string型別的長度上限,超過這個上限就判斷為BigKey final static int STR_MAX_LEN = 10 * 1024; //設定集合型別允許的成員數量上限,超過這個上限就判斷為BigKey final static int HASH_MAX_LEN = 500; @Test void testScan() { int maxLen = 0; long len = 0; String cursor = "0"; do { // 掃描並獲取一部分key ScanResult<String> result = jedis.scan(cursor); // 記錄cursor cursor = result.getCursor(); List<String> list = result.getResult(); if (list == null || list.isEmpty()) { break; } // 遍歷 for (String key : list) { // 判斷key的型別 String type = jedis.type(key); switch (type) { case "string": len = jedis.strlen(key); maxLen = STR_MAX_LEN; break; case "hash": len = jedis.hlen(key); maxLen = HASH_MAX_LEN; break; case "list": len = jedis.llen(key); maxLen = HASH_MAX_LEN; break; case "set": len = jedis.scard(key); maxLen = HASH_MAX_LEN; break; case "zset": len = jedis.zcard(key); maxLen = HASH_MAX_LEN; break; default: break; } if (len >= maxLen) { System.out.printf("Found big key : %s, type: %s, length or size: %d %n", key, type, len); } } } while (!cursor.equals("0")); } @AfterEach void tearDown() { if (jedis != null) { jedis.close(); } } }
3)第三方工具
利用第三方工具,這裡推薦Redis-Rdb-Tools,它會針對Redis的RDB快照檔案來分析記憶體使用情況,由於分析的是快照檔案,因此不會佔用Redis服務的任何效能,但是時效性相對較差
Redis-Rdb-Tools的github網址:https://github.com/sripathikrishnan/redis-rdb-tools
4)網路監控
使用自定義工具,監控進出Redis的網路資料,超出預警值時主動告警。一般阿里雲搭建的雲伺服器就有相關監控頁面:
BigKey記憶體佔用較多,因此即便我們使用的是刪除操作,刪除BigKey也需要耗費很長時間,導致Redis主執行緒阻塞,引發一系列問題。
如果redis版本在4.0之後,我們可以通過非同步刪除命令unlink來刪除一個BigKey,該命令會先把資料標記為已刪除,然後再非同步執行刪除操作。
如果redis版本在4.0之前,針對集合型別,我們可以先遍歷BigKey中所有的元素,先將子元素逐個刪除,最後再刪除BigKey。至於如何遍歷,針對不同的集合型別,可以參考以下不同的命令
找出BigKey中,我們應該如何對BigKey進行優化呢?這裡我們需要選擇恰當的資料型別
如果我們要儲存一個User物件,有三種儲存方式:
1)JSON字串
將一整個物件轉成Json格式進行儲存
user:1 | {“name”: “Jack”, “age”: 21} |
---|
優點:實現簡單粗暴
缺點:資料耦合,不夠靈活,且需要維護JSON結構,佔用記憶體相對較大
2)欄位打散
將物件的不同屬性儲存到不同的key中
key | value |
---|---|
user:1:name | Jack |
user:1:age | 21 |
優點:可以靈活存取物件任意欄位
缺點:由於每條資料都會有一些元資訊需要儲存,因此將一個Key分成多個Key進行儲存,佔用的記憶體會變的更大,且由於欄位分散,當我們需要做統一控制時會變得很困難
3)hash(推薦)
使用hash結構來儲存物件,物件的一個屬性對應集合中的一個成員
user:1 | name | jack |
age | 21 |
優點:hash結構底層會使用ziplist壓縮列表,空間佔用小,且可以靈活存取物件的任意欄位
缺點:程式碼編寫時相對複雜
假如有一個hash型別的key,其中有100萬對field和value,field是自增id,這個key存在什麼問題?如何優化?
key | field | value |
someKey | id:0 | value0 |
..... | ..... | |
id:999999 | value999999 |
當hash的entry數量超過500時,底層會使用雜湊表儲存而不是ZipList,記憶體佔用會變得比較高,雖然這個數量限制我們是可以通過以下命令進行修改的
config set hash-max-ziplist-entries 數量
但是entry數量如果實在太大了還是會導致BigKey問題,這是需要優化的,這裡提供以下兩種解決思路:
1)拆分為String型別(不推薦)
將Hash中的每個成員單獨使用一個String型別的key進行儲存
key | value |
id:0 | value0 |
..... | ..... |
id:999999 | value999999 |
這種方案是不推薦的,存在的問題如下
2)拆分成多個Hash型別
拆分為小的hash,將 id / 100 作為key, 將id % 100 作為field,這樣每100個元素為一個Hash,這種方式相對上面兩種來說記憶體佔用會少很多,而且解決了Bigkey的問題,當然多少個元素作為一個Hash是自己定義的,這裡建議數量不要超過500
key | field | value |
key:0 | id:00 | value0 |
..... | ..... | |
id:99 | value99 | |
key:1 | id:00 | value100 |
..... | ..... | |
id:99 | value199 | |
.... | ||
key:9999 | id:00 | value999900 |
..... | ..... | |
id:99 | value999999 |
到此這篇關於Redis鍵值設計的實踐的文章就介紹到這了,更多相關Redis鍵值設計內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45