Redis應用學習——Redis Cluster客戶端

1. moved重定向

    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重定向,那麼就會極大影響系統性能網絡

2. smart客戶端——JedisCluster

    1. smart客戶端:該客戶端就是爲了改善集羣模式客戶端(redis-cli  -h host  -p port  -c)可能會由於頻繁的moved和ask重定向而致使的性能浪費多線程

    2. smart客戶端JedisCluster簡單原理介紹:併發

  • 從集羣中選取一個可執行的節點,使用cluster slots命令獲取到每一個節點和其負責的槽位的關係映射,好比下圖,JedisCluster也會經過相似方式,在JedisCluster建立並初始化時,會自動進行該步驟,而且將每一個節點和其負責的槽位的關係映射保存在緩存中
  • 每一個節點和其負責的槽位的關係映射後,爲每個節點建立一個對應的JedisPool,每當有命令傳來時就從該鏈接池中獲取一個Jedis對象,執行完命令後在將該Jedis對象還給JedisPool
  • 準備執行命令,JedisCluster中保存着節點和其負責的槽位的關係映射map,在執行命令時,經過key能夠獲取到其所在的槽位slot,而後依據map就能夠獲取到對應的節點,而後找到該節點的JedisPool獲取一個Jedis對象,發送並執行命令

    2. 命令執行時的問題:若是說服務端手動進行了一些改動,好比節點遷移,而JedisCluster卻未刷新緩存中的每一個節點和其負責的槽位的關係映射,那麼當再次執行命令時,就有可能出現鏈接錯誤,就會發生並返回ask或moved(通常都是moved)異常,此時JedisCluster客戶端就會自動的刷新緩存中的節點和其負責的槽位的關係映射,而後從新發送命令執行,但命令發送次數有限制,屢次失敗以後就會拋出錯誤性能

3. 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值前面能夠保證批量命令只會在同一個節點中執行。該方法耗時最短,但也會引發數據傾斜

相關文章
相關標籤/搜索