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值前面能够保证批量命令只会在同一个节点中执行。该方法耗时最短,但也会引发数据倾斜