5 用戶端路由
5.1 moved重定向
![](https://img.laitimes.com/img/__Qf2AjLwojIjJCLyojI0JCLicmbw5iMhF2Y2YTYxYTMmJTOxEDOhBTN0EDOlRDNjNDO5UTZ18CX5d2bs92Yl1iclB3bsVmdlR2LcNWaw9CXt92Yu4GZjlGbh5yYjV3Lc9CX6MHc0RHaiojIsJye.png)
每個節點通信共享Redis Cluster中槽和叢集中對應節點的關系。
- 用戶端向Redis Cluster的任一節點發送指令
- 接收指令的節點再計算自己的槽和對應節點
-
- 如果儲存資料的槽被配置設定給目前節點,則去槽中執行指令,并把指令執行結果傳回給用戶端
如果儲存資料的槽不在目前節點的管理範圍内,則向用戶端傳回moved重定向異常
- 用戶端接收到節點傳回的結果,如果是moved異常,則從moved異常中擷取目标節點的資訊
- 用戶端向目标節點發送指令,擷取指令執行結果
用戶端不會自動找到目标節點執行指令,需要二次執行
5.2 ask重定向
由于叢集伸縮時,需要資料遷移。
- 當用戶端通路某key,節點告訴用戶端key在源節點,再去源節點通路時,卻發現key已遷移到目标節點,就會傳回ask。
-
看完這篇Redis-Cluster,穩拿30W年薪大廠offer(中)5 用戶端路由6 批量操作
- 用戶端向目标節點發送指令,目标節點中的槽已經遷移到其它節點
- 目标節點會傳回ask轉向給用戶端
- 用戶端向新節點發送Asking指令
- 再向新節點發送指令
- 新節點執行指令,把指令執行結果傳回給用戶端
為什麼不能簡單使用MOVED重定向?
雖然MOVED意味着我們認為哈希槽由另一個節點永久提供,并且應該對指定節點嘗試下一個查詢,是以ASK意味着僅将下一個查詢發送到指定節點。
之是以需要這樣做,是因為下一個關于哈希槽的查詢可能是關于仍在A中的鍵的,是以我們始終希望用戶端嘗試A,然後在需要時嘗試B。由于隻有16384個可用的哈希槽中有一個發生,是以群集上的性能下降是可以接受的。
5.3 moved V.S ask
都是用戶端重定向:
- moved:槽已經确定轉移
- ask:槽還在遷移中
5.4 智能用戶端
目标
追求性能
設計思路
- 從叢集中選一個可運作節點,使用
初始化槽和節點映射Cluster slots
- 将Cluster slots的結果映射在本地,為每個節點建立JedisPool,然後就可以進行資料讀寫操作
注意事項
- 每個JedisPool中緩存了slot和節點node的關系
- key和slot的關系:對key進行CRC16規則進行hash後與16383取餘得到的結果就是槽
- JedisCluster啟動時,已經知道key,slot和node之間的關系,可以找到目标節點
- JedisCluster對目标節點發送指令,目标節點直接響應給JedisCluster
- 如果JedisCluster與目标節點連接配接出錯,則JedisCluster會知道連接配接的節點是一個錯誤的節點
- 此時JedisCluster會随機節點發送指令,随機節點傳回moved異常給JedisCluster
- JedisCluster會重新初始化slot與node節點的緩存關系,然後向新的目标節點發送指令,目标指令執行指令并向JedisCluster響應
- 如果指令發送次數超過5次,則抛出異常"Too many cluster redirection!"
- 基本圖示
-
看完這篇Redis-Cluster,穩拿30W年薪大廠offer(中)5 用戶端路由6 批量操作 - 全面圖示
-
看完這篇Redis-Cluster,穩拿30W年薪大廠offer(中)5 用戶端路由6 批量操作
6 批量操作
mget、mset須在同一槽。
Redis Cluster 不同于 Redis 單節點,甚至和一個 Sentinel 監控的主從模式也不一樣。主要因為叢集自動分片,将一個key 映射到16384槽之一,這些槽分布在多節點。是以操作多 key 的指令必須保證所有的key都映射同一槽,避免跨槽執行錯誤。
一個單獨的叢集節點,隻服務一組專用的keys,請求一個指令到一個Server,隻能得到該Server上擁有keys的對應結果。
一個非常簡單的例子是執行KEYS指令,當釋出該指令到叢集中某節點時,隻能得到該節點上擁有key,并非叢集中所有key。要得到叢集中所有key,必須從叢集的所有主節點上擷取所有key。
對于分散在redis叢集中不同節點的資料,如何比較高效地批量擷取資料呢?
6.1 串行mget
定義for循環,周遊所有key,分别去所有的Redis節點中擷取值并進行彙總,簡單,但效率不高,需n次網絡時間。
6.2 串行I/O
優化串行的mget,在用戶端本地做内聚,對每個key hash,然後取餘,知道key對應槽
本地已緩存了槽與節點的對應關系,然後對key按節點進行分組,成立子集,然後使用pipeline把指令發送到對應的node,需要nodes次網絡時間,大大減少了網絡時間開銷。
6.3 并行I/O
優化串行IO,分組key後,根據節點數量啟動對應的線程數,根據多線程模式并行向node節點請求資料,隻需1次網絡時間
6.4 hash_tag
不做任何改變,hash後就比較均勻地散在每個節點上
是否能像單機,一次IO将所有key取出呢?hash-tag提供了這樣功能:若将上述key改為如下,即大括号括起來相同的内容,保證所有的key隻向一個node請求資料,這樣執行類似mget指令隻需要去一個節點擷取資料即可,效率更高。
6.5 選型對比
第一種方式,使用多線程解決批量問題,減少帶寬時延,提高效率,這種做法就如上面所說簡單便捷(我們目前批量操作類型比較多),有效。但問題比較明顯。批量操作數量不大即可滿足。
搜狐的cachecloud采用第二點,先将key擷取槽點,然後分node pipeline操作。這種做法相對比第一種做法較優。