写点什么

Redis Cluster 宕机引发的事故

  • 2020-06-12
  • 本文字数:2160 字

    阅读完需:约 7 分钟

Redis Cluster 宕机引发的事故

导读:

Redis 官方号称支持并发 11 万读操作,并发 8 万写操作。由于优异的性能和方便的操作,相信很多人都在项目中都使用了 Redis,为了不让应用过分的依赖 Redis 服务,Redis 的作用只作为提升应用并发和降低应用响应时间存在,即使 Redis 出现异常,应用程序也不应该出现提供服务失败问题,对此拍拍信最近安排了一次全环境的 Redis Cluster 宕机演练。

本文作者系拍拍信架构负责人朱荣松和拍拍信架构开发工程师许彬,授权“技术锁话”进行发布。

一、演练过程

Redis 集群环境:


  1. 测试环境:

  2. Redis Cluster 配置 :Redis 3 主 3 从 一共 6 个节点。

  3. 预发环境:

  4. Redis Cluster 配置 :Redis 3 主 3 从 一共 6 个节点。


下面是我们操作的时间线:


  • 第一天

  • 程序运行中关闭任意一台从节点,测试一天均无异常。

  • 第二天

  • 程序运行中关闭任意一台从节点,程序未发现异常,测试一天未发现异常。

  • 第三天

  • 预发环境有应用发版,出现异常程序无法启动。


……

二、问题描述

首先说明几个前提:


  1. 测试与预发环境目前关闭的都是任意一台 Redis 从节点。

  2. 测试环境经过反复测试无问题才开始关闭预发环境节点。

  3. 预发环境重启被关闭的 Redis 节点后异常消失。

  4. 连接 Redis 客户端使用的是 Java 语言中使用范围较广的 Jedis。


那么为什么测试环境在经过反复测试没有问题,到预发环境会出现问题?

三、原理

分析问题前先简单解释下 Redis Cluster 实现原理。简单来说 Redis Cluster 中内置了 16384 个哈希槽,当需要在 Redis Cluster 中存取一个 key 或者 value 时,Redis 客户端先对 key 使用 crc16 算法算出一个结果,然后把结果对 16384 求余数( 算法为:crc16(key)mod 16384),这样每个 key 都会对应一个编号在 0-16383 之间的哈希槽,值得注意的是这个计算 key 是在哪个槽上的操作是 Redis 客户端做的操作,Java 中常用的客户端为 Jedis 这个也是被 Spring 推荐的一种客户端。


注: 如果有人好奇为什么 Redis Cluster 为什么会使用 16384 也就是 2^14 个槽。可以查看 Github https://github.com/antirez/redis/issues/2576作者对此进行了解释。

四、分析

首先是查看程序启动异常信息,下图 1 为程序异常信息。



图 1 异常很明显抛出的是连接异常


查看了 Jedis 的源码后发现初始化 Redis Cluster 的槽信息时,调用 initializeSlotsCache()方法时出现异常。图 2 为此方法的具体实现,分析代码发现此代码的目的应该是需要 cache Redis Cluster 槽信息,由于代码中有 break,所以是只需要连接 Redis 获取一次信息即可。细一看此代码应该是有 Bug,Try 的范围没有覆盖到 Jedis 连接的操作,如果 Jedis 连接失败直接抛出连接失败异常,此循环会直接退出,与代码实际预期不符合。



图 2


由此引发另一个思考,是不是我关闭的节点正好为循环的第一个节点导致此问题。尝试关闭另外一台从节点后程序正常启动。那么 Jedis 加载的节点顺序是什么,似乎 Jedis 对节点顺序进行了排序操作。在查看源码后发现 Jedis 重写了 Redis 节点配置类的 hashCode 方法。



图 3



图 4


下面简单测试下如果配置为:jedis-01.test.com、jedis-02.test.com、jedis-03.test.com、jedis-04.test.com、jedis-05.test.com、jedis-05.test.com 输出顺序是什么。



图 5


输出结果:


[redis-06.test.com:6379,redis-04.test.com:6379, redis-01.test.com:6379, redis-03.test.com:6379, redis-02.test.com:6379,redis-05.test.com:6379]


也就是说如果关闭 redis-06.test.com:6379 这台节点,程序就会出现启动失败问题。

五、解决

问题定位后首先去 Github 上的查看相关问题是否有人遇到,在查询后发现此问题有人在去年 11 月提了 PR 解决了此问题,链接如下:


https://github.com/xetorthio/jedis/pull/1633


官方目前释放出了 2.10.0-m1 和 3.0.0-m1 中解决了此问题,但是由于不是 Release 版本使用还得注意。解决的办法为图 6,和图 2 对比可以发现图 6 对 Jedis 的实例化也进行了 try catch。



图 6

六、思考

Redis Cluster 由于使用去中心化思想 ,图 7 显示了 Redis Cluster 集群的状态,所以 Redis Cluster 中如果有部分节点异常就会导致整个集群异常。



图 7


那么问题来了多少节点异常会导致程序读写操作出现异常,下面我们也做了个简单的测试用于统计程序运行中,关闭 Redis 节点后程序的出错情况,以下测试表 1 仅供参考。


场景操作(多节点均同时操作)Redis写总量Redis读总量错误量总耗时(s)错误率
程序运行中关主(关任一主)10000010000030841000.031
关主(关任一主)10000010000014821020.015
关主(关任一主)100000100000305397.60.031
关从(关任一从)1000001000000109.20
关从(关任一从)100000100000090.10
关从(关任一从)100000100000088.90
主从一起关(关任一对)10000010000032613210.10.326
主从一起关(关任一对)10000010000029148169.80.291
主从一起关(关任一对)10000010000032410173.70.324
所有主全关100000100000100000353.41
所有从全关100000100000087.70
只留一台主100000100000100000357.11


表 1


从测试结果看,集群 Master 的选举过程是由 Master 参与选举的。


  1. 如果半数以上 Master 处于关闭状态那么整个集群处于不可用状态。

  2. 关闭任意一对主从节点会导致部分(大约为整个集群的 1/3)失败。

  3. 关闭任意一主,会导致部分写操作失败,是由于从节点不能执行写操作,在 Slave 升级为 Master 期间会有少量的失败。

  4. 关闭从节点对于整个集群没有影响。


2020-06-12 17:521450

评论

发布
暂无评论
发现更多内容

Kratos微服务框架实现权鉴 - OPA

golang 微服务 鉴权 OPA Kratos

零基础培训学习大数据课程怎么样

小谷哥

web前端程序员培训前景怎么样

小谷哥

玩转物联网平台规则引擎-设备数据与业务应用高效协同——实践类

阿里云AIoT

安全 物联网 物联网安全 技术标签

10月15日 Apache IoTDB & SeaTunnel 联合 Meetup | 讲座回顾

Apache IoTDB

Vue基础知识和案例展示

Geek_7ubdnf

Vue

Kratos微服务框架实现权鉴 - Casbin

golang 微服务 鉴权 rbac Kratos

SpringBoot实现多线程

Geek_7ubdnf

Java springboot

阿里巴巴正式开源云原生应用脚手架

阿里巴巴中间件

阿里云 开源 云原生

还在高投入自建物联网平台?教你如何节省三分之二成本——实践类

阿里云AIoT

安全 物联网 物联网安全 技术标签

如何选择适合自己的物联网平台?技巧在这里!——实践类

阿里云AIoT

网络安全 安全 物联网 物联网安全 技术标签

浪潮云分布式云ICP加速千行百业羽化创新

云计算

Redis的String类型,原来这么占内存

杨同学technotes

redis 底层原理

学习大数据开发技术后好找工作吗?

小谷哥

软件测试/测试开发 | Jenkins中,如何管理用户及其相对应权限?

测试人

软件测试 持续集成 jenkins 自动化测试 测试开发

容器在公有云上的落地姿势

亚马逊云科技 (Amazon Web Services)

容器 亚马逊云科技 Builder 专栏

pip安装报错:No such file or directory 'conda-forge' 没有那个文件或目录 'conda-forge'

Geek_7ubdnf

Python

大数据开发课程哪个机构好

小谷哥

数据治理:数据集成的关键技术

用友BIP

TensorBoard的使用

Geek_7ubdnf

tensorflow

一文搞懂Spring,堪称Spring源码终结者

小小怪下士

Java spring spring源码

北京零基础前端软件培训班学习怎么样

小谷哥

软件测试/测试开发 | 如何安装Jenkins 插件?

测试人

软件测试 持续集成 jenkins 自动化测试 测试开发

软件测试/测试开发 | Jenkins通过什么方式报警?

测试人

软件测试 持续集成 jenkins 自动化测试 测试开发

MarkDown文件插入图片并上传GitHub

Geek_7ubdnf

markdown

看这篇就够了丨基于Calcite框架的SQL语法扩展探索

袋鼠云数栈

MobaXterm链接linux虚拟机报错Network error: Connection refused

Geek_7ubdnf

mobaxterm

测试开发 | 跨平台设备管理方案 Selenium Grid

霍格沃兹测试开发学社

走好这三步,不再掉进云上安全的沟里!

亚马逊云科技 (Amazon Web Services)

亚马逊云科技 Builder 专栏

如何使用 rust 写内核模块

阿里巴巴中间件

阿里云 rust 云原生

教你3步完成阿里云物联网平台公共实例到企业实例的迁移——实践类

阿里云AIoT

安全 物联网 物联网安全 技术标签

Redis Cluster 宕机引发的事故_文化 & 方法_技术琐话_InfoQ精选文章