Redis命令性能優化及事務使用過程

場景

`假設有這樣一個使用場景,依次執行下面的5條命令
命令1:hset mall:sale:freq:ctrl:860000000000001 599055114591 1(hash結構,field表示購買的商品ID,value表示購買次數)
簡單說明: mall:sale:freq:ctrl:860000000000001是一個hash表;599055114591表示key;1表示key對應的value
命令2:hset mall:sale:freq:ctrl:860000000000001 599055114592 2
命令3:expire mall:sale:freq:ctrl:860000000000001 3127(設置過時時間)
簡單說明:給hash表mall:mall:sale:freq:ctrl:860000000000001設置過時時間
命令4:set mall:total:freq:ctrl:860000000000001 3
簡單說明:set key vlaue
命令5:expire mall:total:freq:ctrl:860000000000001 3127(設置過時時間)
簡單說明: set key 過時時間`html

優化原因

執行一條命令 經歷的過程java

  • 發送命令網絡傳輸時間
  • 命令在Redis服務端隊列中等待的時間
  • 命令執行的時間(Redis中的slowlog只是檢測這一步驟的時間)
  • 結果返回的Redis客戶端的時間

`執行一條命令 就須要通過上面的過程,發送命令-〉命令排隊-〉命令執行-〉返回結果
那麼執行5條命令,可想而知,性能優化的空間仍是蠻大的,
下面我們來進行優化一下吧`git

優化

第一次優化:利用hmset命令將兩條hmset命令合二爲一

`命令1和命令2 合二爲一
hmset mall:sale:freq:ctrl:860000000000001 599055114591 1 599055114592 2
expire mall:sale:freq:ctrl:860000000000001 3127
set mall:total:freq:ctrl:860000000000001 3
expire mall:total:freq:ctrl:860000000000001 3127`redis

第二次優化:將set和expire命令合二爲一

`將命令4和命令5合二爲一
命令a: hmset mall:sale:freq:ctrl:860000000000001 599055114591 1 599055114592 2
命令b: expire mall:sale:freq:ctrl:860000000000001 3127
命令c: setex mall:total:freq:ctrl:860000000000001 3127 3`spring

第三次優化:使用pipeline

須要注意:RedisCluster中使用pipeline時必須知足pipeline打包的全部命令key在RedisCluster的同一個slot上數據庫

分析下是否在同一個slot上

slot原理簡介

`Redis 集羣使用數據分片(sharding)而非一致性哈希(consistency hashing)來實現: 一個 Redis 集羣包含 16384 個哈希槽(hash slot), 數據庫中的每一個鍵都屬於這 16384 個哈希槽的其中一個, 集羣使用公式 CRC16(key) % 16384 來計算鍵 key 屬於哪一個槽, 其中 CRC16(key) 語句用於計算鍵 key 的 CRC16 校驗和 。
集羣中的每一個節點負責處理一部分哈希槽。
舉個例子, 一個集羣能夠有三個哈希槽, 其中:
節點 A 負責處理 0 號至 5500 號哈希槽。
節點 B 負責處理 5501 號至 11000 號哈希槽。
節點 C 負責處理 11001 號至 16384 號哈希槽。
這種將哈希槽分佈到不一樣節點的作法使得用戶能夠很容易地向集羣中添加或者刪除節點。 好比說:
若是用戶將新節點 D 添加到集羣中, 那麼集羣只須要將節點 A 、B 、 C 中的某些槽移動到節點 D 就能夠了。
與此相似, 若是用戶要從集羣中移除節點 A , 那麼集羣只須要將節點 A 中的全部哈希槽移動到節點 B 和節點 C , 而後再移除空白(不包含任何哈希槽)的節點 A 就能夠了。
由於將一個哈希槽從一個節點移動到另外一個節點不會形成節點阻塞, 因此不管是添加新節點仍是移除已存在節點, 又或者改變某個節點包含的哈希槽數量, 都不會形成集羣下線。`vim

結論

`由此可知 命令a和命令b在同一個slot上,命令c在另一個slot上
因此命令a和命令b用pipline來處理`springboot

如何使用pipline

  • 先建立一個txt文件

`vim pipeline.txt
hmset mall:sale:freq:ctrl:860000000000001 599055114591 1 599055114592 2
expire mall:sale:freq:ctrl:860000000000001 3127`性能優化

  • 格式化 使得 這個文本文件中每一行都必須以\r\n而不是\n結束

須要安裝下dos2unix網絡

a、brew install dos2unix

b、unix2dos pipeline.txt

  • 命令執行

cat pipeline.txt | redis-cli --pipe

第四次優化 使用 高級特性:hashtag

CRC16(key) % 16384 來計算鍵 key 屬於哪一個槽 可知,key決定了存儲在哪一個slot上,那麼使用hashtag可使得 知足部分key一致的全部key都存儲在同一個slot上

好比

mall:sale:freq:ctrl:{860000000000001} 只要key中有{860000000000001}這一部分,就必定落在同一個slot上

**注意:使用hashtag特性 不能把key的離散性變得很是差 **

  • 離散性好

mall:sale:freq:ctrl:{860000000000001} 這種key仍是與用戶相關

  • 離散性差

`mall:{sale:freq:ctrl}:860000000000001
全部的key都會落在同一個slot上,致使整個Redis集羣出現嚴重的傾斜問題`

通過這4次優化 perfect ==> 5條Redis命令壓縮到3條Redis命令,而且3條Redis命令只須要發送一次,而且結果也一次就能所有返回

pipline

  • 未使用pipline

  • 使用了pipline

  • 性能對比

這是一組統計數據出來的數據,使用Pipeline執行速度比逐條執行要快,特別是客戶端與服務端的網絡延遲越大,性能體能越明顯

性能測試代碼

pipeline 實現 mdel

redis提供了mset、mget方法 但沒有提供mdel方法 能夠藉助pipeline實現

將不一樣類型的操做命令合併提交

原生批命令(mset, mget)與Pipeline對比

  • 原生批命令是原子性,pipeline是非原子性

    (原子性概念:一個事務是一個不可分割的最小工做單位,要麼都成功要麼都失敗。原子操做是指你的一個業務邏輯必須是不可拆分的. 處理一件事情要麼都成功,要麼都失敗,原子不可拆分)

  • 原生批命令一命令多個key, 但pipeline支持多命令(存在事務),非原子性
  • 原生批命令是服務端實現,而pipeline須要服務端與客戶端共同完成

使用pipeline組裝的命令個數不能太多,否則數據量過大,增長客戶端的等待時間,還可能形成網絡阻塞,能夠將大量命令的拆分多個小的pipeline命令完成

事務

  • redsi事務

  • discard事務

  • 命令錯誤,語法不正確,致使事務不能正常結束

  • 運行錯誤,語法正確,但類型錯誤,事務能夠正常結束

  • watch命令

`使用watch後, multi失效,事務失效
WATCH的機制是:
在事務EXEC命令執行時,Redis會檢查被WATCH的key,
只有被WATCH的key從WATCH起始時至今沒有發生過變動,EXEC纔會被執行。
若是WATCH的key在WATCH命令到EXEC命令之間發生過變化,則EXEC命令會返回失敗。`

源碼地址

https://gitee.com/pingfanrenbiji/springboot-jedisCluster/blob/master/demo/src/main/java/com/example/pipline/PipelineTest.java

參考文檔

`https://www.jianshu.com/p/884...
http://www.gxlcms.com/redis-3...
https://blog.csdn.net/huangba...
https://blog.csdn.net/w1lgy/a...
官方文檔 :
http://redisdoc.com/topic/clu...`

本文使用 mdnice 排版

相關文章
相關標籤/搜索