redis cluster知识

2023-11-19

一 架构

Redis Cluster使用 Slot 的概念:作为一个KV系统,它把每个key的值hash成0 ~ 16383之间的一个数。这个hash值被用来确定对应的数据存储在哪个节点中。集群中的每个节点都存储了一份类似路由表的东西,描述每个节点所拥有的 Slots;当用户请求一个不在本机的key的时候,它可以根据这个路由表找到正确的服务节点,然后回复给用户一个moved,告知用户正确的服务节点。

  • slot = CRC16(key) % 16383;

  • 是集群内数据管理和迁移的最小单位,保证数据管理的粒度易于管理;

  • 每个节点都知道slot在集群中的分布,并能把对应信息回复给无法服务的请求。

  • 节点之间保持Gossip通信
    在这里插入图片描述
    在这里插入图片描述
    gossip 协议包含多种消息,包括ping,pong,meet,fail等等。

  • meet:某个节点发送meet给新加入的节点,让新节点加入集群中,然后新节点就会开始与其他节点进行通信。

  • ping:每个节点都会频繁给其他节点发送ping,其中包含自己的状态还有自己维护的集群元数据,互相通过ping交换元数据(类似自己感知到的集群节点增加和移除,hash slot信息等)。

  • pong: 对ping和meet消息的返回,包含自己的状态和其他信息,也可以用于信息广播和更新。

  • fail: 某个节点判断另一个节点fail之后,就发送fail给其他节点,通知其他节点,指定的节点宕机了。

  • gossip协议的优点在于元数据的更新比较分散,不是集中在一个地方,更新请求会陆陆续续,打到所有节点上去更新,有一定的延时,降低了压力;缺点在于元数据更新有延时可能导致集群的一些操作会有一些滞后。

二 连接与部署

2.1客户端查找数据

在这里插入图片描述

比如:MOVED 10086 127.0.0.1:7002 表示,客户端请求的键值对所在的哈希槽 10086,实际是在 127.0.0.1:7002 这个实例上。

通过返回的 MOVED 命令,就相当于把哈希槽所在的新实例的信息告诉给客户端了。

这样一来,客户端就可以直接和 7002 连接,并发送操作请求了。

同时,客户端还会更新本地缓存,将该槽与 Redis 实例对应关系更新正确

MOVED 错误表示客户端请求的 key 不在当前实例的槽位上,客户端需要重定向到 MOVED 错误指向的实例

而 ASK 错误只是两个节点迁移槽过程中的一种临时措施,客户端请求的 key 正在迁移而且迁移到新实例上去时,就会返回一个 ASK 错误,客户端就会将请求的 key 重定向到 ASK 错误指向的新实例上

如果客户端再次请求相同的 key,它还是会向原来负责该槽位的实例发送请求

ASK 命令只是让客户端给新实例发送一起请求,不像 MOVED命令一样会更改本地缓存的哈希槽分配信息,让后续所有请求都发往新实例
在这里插入图片描述

单个集群节点不建议设置过多,过多的节点间的通信会带来较大的网络开销。按照服务来区分部署集群
1.集群能在挂一台机器的情况下不影响服务,要满足这个要求,主从节点就不能在一台机器上,也不能有过半数(包括半数)主节点在同一台机器上。
2.集群在挂一台机器的情况下,压力应尽可能平均分流到其他机器。
3.主节点和从节点在各台机器上的分布应当平均。
4.一个分片只配置一个slave
在这里插入图片描述
为了简化管理, 我们规定了集群的规格. 具体做法是每个主节点有且只有一个从节点. 并且以4个节点为最小的管理单位, 我们称为chunk. 一个chunk有两主两从, 分布在两台机器上面, 每台机器两个节点, 且4个节点内互相组成主从关系, 要求负责一个分片的主从分布在不同的机器上面.

一个chunk:
machine A machine B
master 1 / master 2
slave 2 /\ slave 1

三 故障检测与切换

  • slave发现自己的master变为FAIL

  • 将自己记录的集群currentEpoch加1,并广播Failover Request信息

  • 其他节点收到该信息,只有master响应,判断请求者的合法性,并发送FAILOVER_AUTH_ACK,对每一个epoch只发送一次ack

  • 尝试failover的slave收集FAILOVER_AUTH_ACK

  • 超过半数后变成新Master

  • 广播Pong通知其他集群节点

在这里插入图片描述

1.SlaveMigration

/* Step 1: Don't migrate if the cluster state is not ok. */
    if (server.cluster->state != CLUSTER_OK) return;

    /* Step 2: Don't migrate if my master will not be left with at least
     *         'migration-barrier' slaves after my migration. */
    if (mymaster == NULL) return;
    for (j = 0; j < mymaster->numslaves; j++)
        if (!nodeFailed(mymaster->slaves[j]) &&
            !nodeTimedOut(mymaster->slaves[j])) okslaves++;
    if (okslaves <= server.cluster_migration_barrier) return;
candidate = myself;
    di = dictGetSafeIterator(server.cluster->nodes);
    while((de = dictNext(di)) != NULL) {
        clusterNode *node = dictGetVal(de);
        int okslaves = 0, is_orphaned = 1;

        /* We want to migrate only if this master is working, orphaned, and
         * used to have slaves or if failed over a master that had slaves
         * (MIGRATE_TO flag). This way we only migrate to instances that were
         * supposed to have replicas. */
        if (nodeIsSlave(node) || nodeFailed(node)) is_orphaned = 0;
        if (!(node->flags & CLUSTER_NODE_MIGRATE_TO)) is_orphaned = 0;

        /* Check number of working slaves. */
        if (nodeIsMaster(node)) okslaves = clusterCountNonFailingSlaves(node);
        if (okslaves > 0) is_orphaned = 0;

        if (is_orphaned) {
            if (!target && node->numslots > 0) target = node;

            /* Track the starting time of the orphaned condition for this
             * master. */
            if (!node->orphaned_time) node->orphaned_time = mstime();
        } else {
            node->orphaned_time = 0;
        }

        /* Check if I'm the slave candidate for the migration: attached
         * to a master with the maximum number of slaves and with the smallest
         * node ID. */
        if (okslaves == max_slaves) {
            for (j = 0; j < node->numslaves; j++) {
                if (memcmp(node->slaves[j]->name,
                           candidate->name,
                           CLUSTER_NAMELEN) < 0)
                {
                    candidate = node->slaves[j];
                }
            }
        }
    }
    dictReleaseIterator(di);

/* Step 4: perform the migration if there is a target, and if I'm the
     * candidate, but only if the master is continuously orphaned for a
     * couple of seconds, so that during failovers, we give some time to
     * the natural slaves of this instance to advertise their switch from
     * the old master to the new one. */
    if (target && candidate == myself &&
        (mstime()-target->orphaned_time) > CLUSTER_SLAVE_MIGRATION_DELAY &&
       !(server.cluster_module_flags & CLUSTER_MODULE_FLAG_NO_FAILOVER))
    {
        serverLog(LL_WARNING,"Migrating to orphaned master %.40s",
            target->name);
        clusterSetMaster(target);
    }

四 需要注意的坑

1.压力分布不均:

宕机后slave会选举为master 这台机器上会出现较多的master节点,在创建集群时需要预估该机器挂了后机器能否承受上面对应的服务产生的压力
保存初始主从节点的分布情况并及时更新,宕机后可以尽快的给集群补充机器

Redis Cluster 不建议使用 pipeline 和 multi-keys 操作(如 mset/mget. multi-key 操作),减少 max redirect 的产生;

五 常见问题排查

1.OOM

redis内存分布:
在这里插入图片描述

  • 客户端内存 :不受maxmemory限制
  • 对象内存: 主要占用内存的部分
  • 复制积压缓冲: 所有从库客户端共享、保存固定大小的写入命令用于从库失连后数据补偿
  • redis自身内存

oom导致:无法写入;大量key过期 影响读取

检查内存使用情况:

redis-cli -p 6383 memory stats|egrep -A 1 '(total.allocated|overhead.total|dataset.bytes|clients.normal)'

overhead.total:复制缓冲区、客户端输入输出缓冲区等,另外还包括⼀些元数据如 overhead.hashtable
dataset.bytes:数据对象使用内存
数据达到⼀定规模后,因需消耗额外的元数据、缓存内存,Redis 最终将超过 maxmemory 而 OOM
占用最多内存的连接数

# 1. 快速查看Redis内存是否够用
redis-cli -p 9999 info memory |egrep
'(used_memory_human|maxmemory_human|maxmemory_policy)'
# 2. 检查复制积压缓冲区使用情况
redis-cli -p 9999 memory stats|egrep -A 1
'(total.allocated|replication.backlog)'
# 3. 检查客户端输入缓冲区内存使用总量
redis-cli -p 9999 client list| awk 'BEGIN{sum=0}
{sum+=substr($12,6);sum+=substr($13,11)}END{print sum}'
# 4. 检查客户端输入缓冲区各客户端连接的内存情况
redis-cli -p 6383 client list|awk '{print substr($12,6),$1,$12,$18,$20}'|sort -nrk1,1|head -3| cut -f1 -d " " --complement
# 5. 检查客户端输出缓冲区内存使用总量
redis-cli -p 9999 client list| awk 'BEGIN{sum=0} {sum+=substr($16,6)}END{print
sum}'
# 6. 检查客户端输出缓冲区各客户端连接的内存使用排序
redis-cli -p 9999 client list|awk '{print substr($16,6),$1,$16,$18}'|sort -
nrk1,1 | cut -f1 -d" " --complement |head -n10
# 7. 检查数据对象使用内存总量
redis-cli -p 9999 memory stats|grep -A 1 'dataset.bytes'

如果定位到有连接异常,可以使用如下命令杀掉

CLIENT KILL ID

压测常用命令:

# 1. 持续给Redis灌数据
redis-benchmark -p 9999 -t set -r 100000000 -l
# 2. 模拟输入缓冲区过大
redis-benchmark -p 9999 -q -c 10 -d 102400000 -n 10000000 -r 50000 -t set
# 3. 模拟输出缓冲区过大
redis-benchmark -p 9999 -t get -r 5000000 -n 10000000 -d 100 -c 1000 -P 500 -l

2.常用运维命令

2.1慢日志

#获取5条慢日志
slowlog get 5
#设置阈值
config set slowlog-log-slower-than 2000

2.2 rename高危操作

#在配置文件中添加:
rename-command flushdb flushddbb
rename-command flushall flushallall
rename-command keys keysys

2.3 monitor

#输出keys命令的执行情况
redis-cli -p 6380 monitor | grep keys

2.4 config

#将config热修改的参数刷到redis配置文件中持久化
config rewrite

2.5 redis 查看cluster各节点ip

redis-cli -p 6380 cluster nodes |awk '{print $2}' |awk -F'@' '{print "- "$1}'

2.6 关闭&打开 rdb持久化

redis-cli --cluster call 127.0.0.1:6380 config set save ""
redis-cli --cluster call 127.0.0.1:6380 config rewrite
redis-cli --cluster call 127.0.0.1:6380 config  get save*

redis-cli --cluster call 127.0.0.1:6380 config set save "900 1 300 10 60 10000"
redis-cli --cluster call 127.0.0.1:6380 config rewrite

2.7 下线节点

redis-cli --cluster del-node host:port node_id 
#删除节点 需要通过reshard命令把slot分配到其他节点,然后再执行删除命令

#或者
cluster forget <node_id> :从集群中移除 node_id 指定的节点(需在所有节点执行)

#echo "usage: host port"
nodes_addrs=$(redis-cli -h $1 -p $2 cluster nodes|grep -v handshake| awk '{print $2}')
echo $nodes_addrs
for addr in ${nodes_addrs[@]}; do
    host=${addr%:*}
    port=${addr#*:}
    del_nodeids=$(redis-cli -h $host -p $port cluster nodes|grep -E 'handshake|fail'| awk '{print $1}')
    for nodeid in ${del_nodeids[@]}; do
        echo $host $port $nodeid
        redis-cli -h $host -p $port cluster forget $nodeid
    done
done
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

redis cluster知识 的相关文章

  • 策略模式还是命令模式?

    假设我有一个金融交易列表 我需要针对这些交易执行一系列验证规则 一个例子是我有一笔购买产品的交易 但是首先我需要验证交易中的帐户是否有足够的可用资金 产品没有售完等 由于这些规则 交易将是标记为拒绝 并应指定错误代码 当然 我正在考虑用一个
  • 将构造函数作为参数传递给方法

    我是java新手 开始研究构造函数 我看到一些构造函数作为参数传递给方法的示例 请告诉我当构造函数作为参数传递给方法时会发生什么 或者建议我一些链接 我可以在其中获得有关使用构造函数的足够知识 根据您需要传递构造函数的目的 您可以考虑传递供
  • 在 jTextfield 中禁用“粘贴”

    我有一个用 Swing awt 编写的应用程序 我想阻止用户将值粘贴到文本字段中 有没有办法在不使用动作监听器的情况下做到这一点 您可以使用 null 参数调用 setTransferHandler 如下所示 textComponent s
  • 对象数组的数组(二维数组)JNI

    我正在努力创建自定义对象类型 ShareStruct 的二维数组 jobjectArray ret jobjectArray ins jobjectArray outs jclass myClass env gt FindClass env
  • 重写 getPreferredSize() 会破坏 LSP

    我总是在这个压倒一切的网站上看到建议getPreferredSize 而不是使用setPreferredSize 例如 如前面的线程所示 对于固定大小的组件 使用重写 getPreferredSize 而不是使用 setPreferredS
  • 本地开发的 Azure Functions 扩展包版本问题

    我有一个带有队列触发器的 Java 11 Azure 函数 该函数在部署到 Azure 时按预期工作 并正确从定义的服务总线主题中提取消息 但是 运行相同的功能locally除非我回滚版本 否则不起作用Azure Functions 绑定扩
  • 使用 Spring 控制器处理错误 404

    I use ExceptionHandler处理我的网络应用程序抛出的异常 在我的例子中我的应用程序返回JSON回应HTTP status用于对客户端的错误响应 但是 我正在尝试弄清楚如何处理error 404返回与处理的类似的 JSON
  • 我需要在 JFileChooser(打开模式)中显示不带扩展名的文件名。如何?

    我在打开模式下使用 JFileChooser 我需要显示不带扩展名的 文件名 字段 如何 我知道文件视图 它删除文件系统文件中的扩展名 但将所选文件中的扩展名保留在 文件名 字段中解释 http saveimg ru show image
  • 可访问数据的 Java 约定。 (公共访问器和 Getter/命名)

    通过 Java API 您会看到大量冲突的命名和实践 这让我感到非常困惑 例如 The String http grepcode com file repository grepcode com java root jdk openjdk
  • 确定序列化对象的类型

    我需要通过套接字发送消息 从用户到引擎的请求 以及从引擎到用户的响应 所以流程本质上是 serialized request Server lt network gt Client serialized response request r
  • java setFullScreenWindow 在 Mac 中隐藏登录对话框

    我使用的是全屏窗口 类似于屏幕保护程序 使用这里的方法 GraphicsEnvironment getLocalGraphicsEnvironment getDefaultScreenDevice setFullScreenWindow t
  • Struts 1 到 Spring 迁移 - 策略

    我有一个legacy银行应用程序编码为Struts 1 JSP现在的要求是迁移后端 目前为 MVC to Springboot MVC 后续UI JSP 将迁移到angular Caveats 1 后端不是无状态的 2 会话对象中存储了大量
  • 嵌套字段的 Comparator.comparing(...)

    假设我有一个这样的域模型 class Lecture Course course getters class Course Teacher teacher int studentSize getters class Teacher int
  • 如何在不反编译的情况下更改已编译的.class文件?

    我想更改 class 文件方法 我安装 JD Eclipse Decompiler 并打开 class 文件 我添加了一些代码并保存 class 文件 但是 class 文件没有改变 我不知道如何使用反编译器 如果可能的话 如何在不使用反编
  • 如何从intellij项目视图中隐藏不必要的文件?

    给定一个示例 gradle 项目 其项目结构如下所示 正如你所看到的 有很多东西你实际上不需要在想法中看到 但你需要它们存在 我知道下面被忽略的文件 文件夹类型Editor File Types但这些正在影响库和项目 idea 会在各处忽略
  • BadPaddingException:无效的密文

    我需要一些帮助 因为这是我第一次编写加密代码 加密代码似乎工作正常 但解密会引发错误 我得到的错误是 de flexiprovider api exceptions BadPaddingException 无效的密文 in the 解密函数
  • 如何使用Gson仅从Json反序列化某些特定字段?

    我有以下 JSON 字符串 channel bvmt initValues data value instrumentIds TN0007250012 TN0007500010 instruments mnemonic ADWYA marc
  • java.lang.IllegalStateException - 提交响应后无法创建会话

    我在我的项目中使用 JSF PrimeFaces 我为此准备了一个Maven项目 当我编译项目并加载主页后 我收到以下异常 java lang IllegalStateException Cannot create a session af
  • 让 Hibernate 和 SQL Server 与 VARCHAR 和 NVARCHAR 良好配合

    我目前正在大型数据库的某些表中启用 UTF 8 字符 这些表已经是 MS SQL 类型 NVARCHAR 此外 我还有几个使用 VARCHAR 的字段 Hibernate 与 JDBC 驱动程序的交互存在一个众所周知的问题 例如 参见在 h
  • Errors/BindingResult 参数应在模型属性、@RequestBody 或 @RequestPart 参数之后立即声明

    我通过剖析示例应用程序来自学 Spring 然后到处添加代码来测试我在剖析过程中开发的理论 在测试添加到 Spring 应用程序中的一些代码时 我收到以下错误消息 An Errors BindingResult argument is ex

随机推荐

  • Maven常见问题、异常、错误整理【持续更新】

    前言 之前也写过一篇与Maven相关的文章 这是第二篇对于Maven常见问题 常见异常的整理 开一篇新的文章一个原因是自己更换了新的IDE 之前使用myEclipse 现在换成IDEA 更重要的原因是新的文章对于处理Maven这类错误的方案
  • 如何调用同文件夹的py文件

    你可以使用 import 语句来调用同文件夹中的另一个 Python 文件 举个例子 假设你有一个文件夹 里面有两个 Python 文件 main py 和 helper py 你想在 main py 中调用 helper py 中的函数
  • 用pycharm+flask 建立项目以后运行出现ImportError: No module named flask-login问题

    出现此问题 一般情况下 打开CMD输入 pip install flask login 然后 在cmd中输入命令 pip list 查看目前已安装的的模板 在此时 如果你继续运行项目 有可能会发现问题已经解决 但是也有可能会发现问题依然存在
  • Rust中的iter(), into_iter(), iter_mut()

    在Rust中 iter into iter iter mut 都是用于在集合类型上创建迭代器的方法 这三个方法各有不同 下面一一进行介绍 iter iter 方法创建一个不可变的引用迭代器 当你只想读取集合中的元素 而不想改变它们或消耗集合
  • C++57个入门知识点_27 继承的概念(类的组合关系:将一个类的对象作为另外一个类的成员;类的组合关系的访问;继承的概念:A类是B的儿子;A被称为子类,B被称为父类或者A被称为派生类,B被称为基类)

    我们知道类有三大特性 封装 继承和多态 封装在前面已经介绍完 本篇开始将会介绍继承 继承是在封装基础上的扩展 也是多态的一个承接 总结 类的组合关系 将一个类的对象作为另外一个类的成员 Class CStudent public priva
  • maven常用命令大全(附详细解释)

    1 常用打包命令 mvn clean package Dmaven test skip true 跳过单测打包 mvn clean install Dmaven test skip true 跳过单测打包 并把打好的包上传到本地仓库 mvn
  • MCU震荡电路的晶振边的22pf电容的作用

    振荡电路用于实时时钟RTC 对于这种振荡电路只能用32 768KHZ 的晶体 晶体被连接在OSC3 与OSC4 之间而且为了获得稳定的频率必须外加两个带外部电阻的电容以构成振荡电路 32 768KHZ的时钟晶振产生的振荡信号经过石英钟内部分
  • (3)numpy数组的索引和切片操作

    在开始之前 先导入numpy第三方包 import numpy as np 索引 谈到索引 我们就应该想到 在python语言中 是如何对列表进行索引的 接下来将会对python列表索引和numpy数组索引进行比较 先定义一个numpy数组
  • 查看linux中的TCP连接数

    一 查看哪些IP连接本机 netstat an 二 查看TCP连接数 1 统计80端口连接数 netstat nat grep i 80 wc l 2 统计httpd协议连接数 ps ef grep httpd wc l 3 统计已连接上的
  • 一招秒开GitHub,永久解决!

    步骤 目录 步骤 1 打开电脑 进入如下的路径 2 将hosts 复制到桌面 3 把hosts文件移动到桌面后etc文件夹下的hosts文件不存在 已经被移动到桌面了 4 在移到桌面的hosts文件里面进行修改 5 再把修改好的hosts文
  • mysql tomcat 自动重连_Java开发网 - tomcat连接池支持重新连接数据库吗?

    Posted by pigengler Posted on 2006 04 20 15 23 我做了一个测试 程序使用tomcat的连接池 factory org apache commons dbcp BasicDataSourceFac
  • 微信api ----统一下单

    应用场景 除被扫支付场景以外 商户系统先调用该接口在微信支付服务后台生成预支付交易单 返回正确的预支付交易回话标识后再按扫码 JSAPI APP等不同场景生成交易串调起支付 状态机 支付状态转变如下 接口链接 URL地址 https api
  • pandas中的data.corr()函数方法说明及使用

    数据相关性分析中 经常用到data corr 函数 data corr 表示了data中的两个变量之间的相关性 取值范围为 1 1 取值接近 1 表示反相关 类似反比例函数 取值接近1 表正相关 DataFrame corr 函数使用说明如
  • 国际版阿里云/腾讯云:阿里云流量包是用来做什么

    阿里云流量包是用来做什么 阿里云同享流量包是一种通用流量套餐 同享流量包具有多地域和多产品流量抵扣的优势 同享流量包不仅能够抵扣云服务器ECS发生的流量 还能够抵扣弹性公网IP和负载均衡SLB发生的流量 同享流量包掩盖产品规模广 同享流量包
  • Qt 设置:两个窗口位置重合

    目录 Qt 设置 两个窗口位置重合 QT向界面中嵌套新的界面 QT向界面中嵌套新的界面 https www cnblogs com bob jianfeng p 11609012 html 第一步 先进入ui编辑界面 加入一个水平或者垂直的
  • 数据预处理与特征工程—10.图像切割与特征提取

    文章目录 引言 一 图像切割 二 特征提取 1 各阶颜色矩的计算公式 三 python实现 水质图像数据 百度网盘链接提取码 1234 引言 本文以水质图像为例 进行图像切割与特征提取 一 图像切割 一般情况下 采集到的水样图片包含盛水容器
  • 深入研究C++多态(虚函数和虚继承)

    文章目录 多态的引入 虚函数表 几种常见继承关系中的类内存分布 单继承 多继承 菱形继承 总结 作者 狗子孙 链接 https www jianshu com p 02183498a2c2 来源 简书 简书著作权归作者所有 任何形式的转载都
  • 如何彻底删除JetBrains系列软件教程

    mac安装JetBrains系列软件后 如果JetBrains系列软件出了问题需要重新安装 有时候软件删除重装后 JetBrains系列软件仍然会打不开的问题 很是困扰 接下来为您带来了mac上如何彻底删除IntelliJ IDEA等软件的
  • Char.IsDigit与Char.IsNumber的区别

    需要判断Char是否为数字 查看了下MSDN 发现有三种方法 Char IsDigit aChar 指示指定字符串中位于指定位置处的字符是否属于十进制数字类别 Char IsNumber aChar 指示指定字符串中位于指定位置的字符是否属
  • redis cluster知识

    一 架构 Redis Cluster使用 Slot 的概念 作为一个KV系统 它把每个key的值hash成0 16383之间的一个数 这个hash值被用来确定对应的数据存储在哪个节点中 集群中的每个节点都存储了一份类似路由表的东西 描述每个