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

Stella981
• 阅读 801

1. moved重定向

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

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

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

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

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

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

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

点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
待兔 待兔
3个月前
手写Java HashMap源码
HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22
Jacquelyn38 Jacquelyn38
3年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
Wesley13 Wesley13
3年前
java中比较两个时间的差值
项目背景1.某篇文稿的发布时间是publishDate,例如:2020072118:00:41。2.现要求判断该篇文稿的发布时间是否在近30天之内。publicstaticlongdayDiff(DatecurrentDate,DatepublishDate){LongcurrentTimecurrentDat
Wesley13 Wesley13
3年前
GoJS API学习
varnode{};node"key""节点Key";node"loc""00";//节点坐标node"text""节点名称";//添加节点通过按钮点击,添加新的节点到画布myDiagram.model.addNodeData(nod
Stella981 Stella981
3年前
KVM调整cpu和内存
一.修改kvm虚拟机的配置1、virsheditcentos7找到“memory”和“vcpu”标签,将<namecentos7</name<uuid2220a6d1a36a4fbb8523e078b3dfe795</uuid
Easter79 Easter79
3年前
Twitter的分布式自增ID算法snowflake (Java版)
概述分布式系统中,有一些需要使用全局唯一ID的场景,这种时候为了防止ID冲突可以使用36位的UUID,但是UUID有一些缺点,首先他相对比较长,另外UUID一般是无序的。有些时候我们希望能使用一种简单一些的ID,并且希望ID能够按照时间有序生成。而twitter的snowflake解决了这种需求,最初Twitter把存储系统从MySQL迁移
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Python进阶者 Python进阶者
9个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这