1. 客戶端讀寫(get/set)操做執行過程:若是是一個普通的客戶端鏈接到redis cluster中的任意一個節點,而後向該節點發送一條get/set命令,接收的節點首先會依據該key計算對應槽位,而後再找到槽位所在的節點,判斷找到的節點是不是自身,若是是則在當前節點執行該命令,不然回覆客戶端moved異常,異常中包含真正執行命令的節點的信息,客戶端須要使用獲取到節點信息,從新鏈接獲取到的節點併發送命令,可是該行爲不是自動的,須要主動操做(以下圖中第4步,該步驟須要在客戶端經過專門編寫邏輯代碼執行);客戶端依據返回的moved異常中的節點信息,進行的轉移鏈接操做就是moved重定向java
2. moved異常演示:首先啓動集羣,而後以普通模式的客戶端鏈接到任意一個節點上,進行set/get操做,linux中普通模式的客戶端對應Java中的Jedis客戶端node
2. 可自動進行moved重定向的客戶端:linux
redis-cli -h host -p port -c:linux系統中redis自帶的客戶端,該客戶端能夠自動進行moved重定向操做,主要在於-c命令參數,該參數表示以集羣模式啓動客戶端並鏈接到到集羣中的某個節點上,以下圖所示,客戶端會自動進行鏈接轉移並執行命令redis
3. ask重定向:相似於moved重定向,但該轉移一般與集羣伸縮有關,ask重定向發生在兩個節點間進行槽位遷移時,當兩個節點正在進行槽位轉移時(轉移未結束),若是此時客戶端向源節點發送一條get/set命令,若是key對應的槽位還在源節點,但槽位中的key已經轉移到新節點中,此時就會返回ask轉向,而後客戶端須要依據返回的ask中的信息執行一個asking命令,而後發送要執行的get/set命令,新節點會返回執行結果。緩存
4. moved和ask重定向帶來的問題:以集羣模式客戶端(redis-cli -h host -p port -c)鏈接任意一個節點後,進行大量的get/set命令,若是執行這些命令時發生了不少的moved和ask重定向,那麼就會極大影響系統性能網絡
1. smart客戶端:該客戶端就是爲了改善集羣模式客戶端(redis-cli -h host -p port -c)可能會由於頻繁的moved和ask重定向而致使的性能浪費多線程
2. smart客戶端JedisCluster簡單原理介紹:併發
2. 命令執行時的問題:若是說服務端手動進行了一些改動,好比節點遷移,而JedisCluster卻未刷新緩存中的每一個節點和其負責的槽位的關係映射,那麼當再次執行命令時,就有可能出現鏈接錯誤,就會發生並返回ask或moved(通常都是moved)異常,此時JedisCluster客戶端就會自動的刷新緩存中的節點和其負責的槽位的關係映射,而後從新發送命令執行,但命令發送次數有限制,屢次失敗以後就會拋出錯誤性能
1. 基本使用:建議使用單例模式管理使用JedisCluster對象優化
import redis.clients.jedis.HostAndPort; import redis.clients.jedis.JedisCluster; public class TestJedisCluster { public static void main(String[] args) { //保存集羣中每一個節點中的IP地址和端口 HashSet<HostAndPort> set=new HashSet<HostAndPort>(); set.add(new HostAndPort("192.168.10.128", 6380)); set.add(new HostAndPort("192.168.10.128", 6381)); set.add(new HostAndPort("192.168.10.128", 6382)); set.add(new HostAndPort("192.168.10.128", 6383)); set.add(new HostAndPort("192.168.10.128", 6384)); set.add(new HostAndPort("192.168.10.128", 6385)); //建立JedisCluster對象,該對象的構造方法有多個重載版本 JedisCluster cluster=new JedisCluster(set); //執行命令 cluster.set("hello","world"); //使用完後,若是肯定不在使用該對象,則關閉JedisCluster try { cluster.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
2. 多節點執行命令:有一個命令須要集羣中的每個節點都執行一遍,好比生成AOF文件,僞代碼過程以下
//獲取到集羣中的每個節點的鏈接池對象 Map<String, JedisPool> nodes=cluster.getClusterNodes(); //遍歷每個節點的鏈接池對象,獲取jedis客戶端對象,執行命令 for(Entry<String, JedisPool> set:nodes.entrySet()){ JedisPool pool=set.getValue(); Jedis j=pool.getResource(); j.bgrewriteaof();//生成AOF文件 j.close(); }
3. 批量執行命令:以相似mget或mset這類批量命令,mget或mset中全部的key值都必須在一個槽位中,執行這類命令有如下幾種優化
(1)串行化執行:也就是遍歷批量命令中操做的每個key值,遍歷執行這些命令便可,問題就是耗時長,由於每個命令都要在網絡中傳輸
(2)依據key值聚簇分類在串行執行:遍歷批量命令中操做的key值,計算出每個key值所在的槽位,而後依據槽位找到所在的節點,找到每一個命令執行所在的節點後就能夠對這些命令進行分組,同一個節點的命令分爲一組,而後使用每一個節點客戶端的Pipeline對象一次性執行每一個組中的全部命令(使用Jedis客戶端中的流水線執行功能),串行執行執行每個Pipeline對象,耗時比上面更少,取決於多個Pipeline對象執行時間之和
(3)並行化執行方法2:使用多線程,並行執行每一個節點客戶端的Pipeline對象,性能更好,耗時取決於執行時間最長的那個Pipeline對象
(4)hash_tag:能夠在key值前添加一個{tag},tag即爲一個標記值,用大括號括起來並寫在key值前面能夠保證批量命令只會在同一個節點中執行。該方法耗時最短,但也會引發數據傾斜