Redis应用学习——Redis Cluster客户端

 

1. moved重定向

    1. 客户端读写(get/set)操作执行过程:如果是一个普通的客户端连接到redis cluster中的任意一个节点,然后向该节点发送一条get/set命令,接收的节点首先会依据该key计算对应槽位,然后再找到槽位所在的节点,判断找到的节点是否是自身,如果是则在当前节点执行该命令,否则回复客户端moved异常,异常中包含真正执行命令的节点的信息,客户端需要使用获取到节点信息,重新连接获取到的节点并发送命令,但是该行为不是自动的,需要主动操作(如下图中第4步,该步骤需要在客户端通过专门编写逻辑代码执行);客户端依据返回的moved异常中的节点信息,进行的转移连接操作就是moved重定向

6c787406e4017c571e0e1e09fedac0b8441.jpg

    2. moved异常演示:首先启动集群,然后以普通模式的客户端连接到任意一个节点上,进行set/get操作,linux中普通模式的客户端对应Java中的Jedis客户端

5bb0b5b3d90d7b949f04651a799005e246a.jpg

    2. 可自动进行moved重定向的客户端:

redis-cli  -h host  -p port  -c:linux系统中redis自带的客户端,该客户端可以自动进行moved重定向操作,主要在于-c命令参数,该参数表示以集群模式启动客户端并连接到到集群中的某个节点上,如下图所示,客户端会自动进行连接转移并执行命令785e627841b04fea52ad83e1cf5b7526e62.jpg

    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创建并初始化时,会自动进行该步骤,并且将每个节点和其负责的槽位的关系映射保存在缓存中9435c16b5bf04846aa3fb241676e03f998a.jpg
  • 每个节点和其负责的槽位的关系映射后,为每一个节点创建一个对应的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值前面可以保证批量命令只会在同一个节点中执行。该方法耗时最短,但也会引起数据倾斜

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值