为什么RedisCluster设计成16384个槽


Posted in Redis onSeptember 25, 2021

亲爱的同学们,你是否使用过Redis集群呢?那Redis集群的原理又是什么呢?记住下面两句话:

  • Redis Sentinal着眼于高可用,在master宕机时会自动将slave提升为master,继续提供服务。
  • Redis Cluster着眼于扩展性,在单个redis内存不足时,使用Cluster进行分片存储。

一、数据分片策略

布式数据存储方案中最为重要的一点就是数据分片,也就是所谓的 Sharding。为了使得集群能够水平扩展,首要解决的问题就是如何将整个数据集按照一定的规则分配到多个节点上,常用的数据分片的方法有:范围分片,哈希分片,一致性哈希算法和虚拟哈希槽等。

范围分片假设数据集是有序,将顺序相临近的数据放在一起,可以很好的支持遍历操作。范围分片的缺点是面对顺序写时,会存在热点。比如日志类型的写入,一般日志的顺序都是和时间相关的,时间是单调递增的,因此写入的热点永远在最后一个分片。对于关系型的数据库,因为经常性的需要表扫描或者索引扫描,基本上都会使用范围的分片策略。

我们为了将不同的 key 分散放置到不同的 redis 节点,通常的做法是获取 key 的哈希值,然后根据节点数来求模,但这种做法有其明显的弊端,当我们需要增加或减少一个节点时,会造成大量的 key 无法命中,这种比例是相当高的,所以就有人提出了一致性哈希的概念。

一致性哈希有四个重要特征:

  • 均衡性:也有人把它定义为平衡性,是指哈希的结果能够尽可能分布到所有的节点中去,这样可以有效的利用每个节点上的资源。
  • 单调性:当节点数量变化时哈希的结果应尽可能的保护已分配的内容不会被重新分派到新的节点。
  • 分散性和负载:这两个其实是差不多的意思,就是要求一致性哈希算法对 key 哈希应尽可能的避免重复。

二、Redis的分片机制

但是:Redis 集群没有使用一致性hash, 而是引入了哈希槽的概念。

Redis Cluster 采用虚拟哈希槽分区,所有的键根据哈希函数映射到 0 ~ 16383 整数槽内,每个key通过CRC16校验后对16384取模来决定放置哪个槽(Slot),每一个节点负责维护一部分槽以及槽所映射的键值数据。

计算公式:slot = CRC16(key) & 16383。

这种结构很容易添加或者删除节点,并且无论是添加删除或者修改某一个节点,都不会造成集群不可用的状态。使用哈希槽的好处就在于可以方便的添加或移除节点。

  • 当需要增加节点时,只需要把其他节点的某些哈希槽挪到新节点就可以了;
  • 当需要移除节点时,只需要把移除节点上的哈希槽挪到其他节点就行了。

三、Redis 虚拟槽分区的特点

  • 解耦数据和节点之间的关系,简化了节点扩容和收缩难度。
  • 节点自身维护槽的映射关系,不需要客户端或者代理服务维护槽分区元数据
  • 支持节点、槽和键之间的映射查询,用于数据路由,在线集群伸缩等场景。

为什么RedisCluster设计成16384个槽

 

四、 Redis 集群伸缩的原理

Redis 集群提供了灵活的节点扩容和收缩方案。在不影响集群对外服务的情况下,可以为集群添加节点进行扩容也可以下线部分节点进行缩容。可以说,槽是 Redis 集群管理数据的基本单位,集群伸缩就是槽和数据在节点之间的移动。

1.集群扩容

当一个 Redis 新节点运行并加入现有集群后,我们需要为其迁移槽和数据。首先要为新节点指定槽的迁移计划,确保迁移后每个节点负责相似数量的槽,从而保证这些节点的数据均匀。

  • 首先启动一个 Redis 节点,记为 M4。
  • 使用 cluster meet 命令,让新 Redis 节点加入到集群中。新节点刚开始都是主节点状态,由于没有负责的>槽,所以不能接受任何读写操作,后续我们就给他迁移槽和填充数据。
  • 对 M4 节点发送 cluster setslot { slot } importing { sourceNodeId } 命令,让目标节点准备导入槽的数据。 >4) 对源节点,也就是 M1,M2,M3 节点发送 cluster setslot { slot } migrating { targetNodeId } 命令,让源节>点准备迁出槽的数据。
  • 源节点执行 cluster getkeysinslot { slot } { count } 命令,获取 count 个属于槽 { slot } 的键,然后执行步骤>六的操作进行迁移键值数据。
  • 在源节点上执行 migrate { targetNodeIp} " " 0 { timeout } keys { key... } 命令,把获取的键通过 pipeline 机制>批量迁移到目标节点,批量迁移版本的 migrate 命令在 Redis 3.0.6 以上版本提供。
  • 重复执行步骤 5 和步骤 6 直到槽下所有的键值数据迁移到目标节点。
  • 向集群内所有主节点发送 cluster setslot { slot } node { targetNodeId } 命令,通知槽分配给目标节点。为了>保证槽节点映射变更及时传播,需要遍历发送给所有主节点更新被迁移的槽执行新节点。

为什么RedisCluster设计成16384个槽

2.集群收缩

收缩节点就是将 Redis 节点下线,整个流程需要如下操作流程。

  • 首先需要确认下线节点是否有负责的槽,如果是,需要把槽迁移到其他节点,保证节点下线后整个集群槽节点映射的完整性。
  • 当下线节点不再负责槽或者本身是从节点时,就可以通知集群内其他节点忘记下线节点,当所有的节点忘记改节点后可以正常关闭。

下线节点需要将节点自己负责的槽迁移到其他节点,原理与之前节点扩容的迁移槽过程一致。

为什么RedisCluster设计成16384个槽

迁移完槽后,还需要通知集群内所有节点忘记下线的节点,也就是说让其他节点不再与要下线的节点进行 Gossip 消息交换。

Redis 集群使用 cluster forget { downNodeId } 命令来讲指定的节点加入到禁用列表中,在禁用列表内的节点不再发送 Gossip 消息。

五、总结

Redis Cluster 是Redis的集群实现,内置数据自动分片机制,集群内部将所有的key映射到16384个Slot中,集群中的每个Redis Instance负责其中的一部分的Slot的读写。集群客户端连接集群中任一Redis Instance即可发送命令,当Redis Instance收到自己不负责的Slot的请求时,会将负责请求Key所在Slot的Redis Instance地址返回给客户端,客户端收到后自动将原请求重新发往这个地址,对外部透明。一个Key到底属于哪个Slot由crc16(key) % 16384 决定。

面试问题:为什么RedisCluster会设计成16384个槽呢?

这个问题,作者是给出了回答的!

地址如下:https://github.com/antirez/redis/issues/2576

作者原版回答如下: The reason is:

Normal heartbeat packets carry the full configuration of a node, that can be replaced in an idempotent way with the old in order to update an old config. This means they contain the slots configuration for a node, in raw form, that uses 2k of space with16k slots, but would use a prohibitive 8k of space using 65k slots.At the same time it is unlikely that Redis Cluster would scale to more than 1000 mater nodes because of other design tradeoffs.

So 16k was in the right range to ensure enough slots per master with a max of 1000 maters, but a small enough number to propagate the slot configuration as a raw bitmap easily. Note that in small clusters the bitmap would be hard to compress because when N is small the bitmap would have slots/N bits set that is a large percentage of bits set.

1.如果槽位为65536,发送心跳信息的消息头达8k,发送的心跳包过于庞大。

如上所述,在消息头中,最占空间的是myslots[CLUSTER_SLOTS/8]。 当槽位为65536时,这块的大小是:65536÷8÷1024=8kb因为每秒钟,redis节点需要发送一定数量的ping消息作为心跳包,如果槽位为65536,这个ping消息的消息头太大了,浪费带宽。

2.redis的集群主节点数量基本不可能超过1000个。

如上所述,集群节点越多,心跳包的消息体内携带的数据越多。如果节点过1000个,也会导致网络拥堵。因此redis作者,不建议redis cluster节点数量超过1000个。 那么,对于节点数在1000以内的redis cluster集群,16384个槽位够用了。没有必要拓展到65536个。

3.槽位越小,节点少的情况下,压缩率高

Redis主节点的配置信息中,它所负责的哈希槽是通过一张bitmap的形式来保存的,在传输过程中,会对bitmap进行压缩,但是如果bitmap的填充率slots / N很高的话(N表示节点数),bitmap的压缩率就很低。 如果节点数很少,而哈希槽数量很多的话,bitmap的压缩率就很低。

而16384÷8÷1024=2kb,怎么样,神奇不!

综上所述,作者决定取16384个槽,不多不少,刚刚好!

到此这篇关于为什么RedisCluster设计成16384个槽的文章就介绍到这了,更多相关RedisCluster 16384槽内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木!

Redis 相关文章推荐
redis三种高可用方式部署的实现
May 11 Redis
了解Redis常见应用场景
Jun 23 Redis
Redis主从配置和底层实现原理解析(实战记录)
Jun 30 Redis
linux下安装redis图文详细步骤
Dec 04 Redis
Redis 中使用 list,streams,pub/sub 几种方式实现消息队列的问题
Mar 16 Redis
一文搞懂Redis中String数据类型
Apr 03 Redis
 Redis 串行生成顺序编码的方法实现
Apr 03 Redis
Redis中key的过期删除策略和内存淘汰机制
Apr 12 Redis
Redis 限流器
May 15 Redis
Redis基本数据类型Zset有序集合常用操作
Jun 01 Redis
关于Redis的主从复制及哨兵问题
Jun 16 Redis
基于redis+lua进行限流的方法
Jul 23 Redis
使用redis生成唯一编号及原理示例详解
Sep 15 #Redis
Redis读写分离搭建的完整步骤
Sep 14 #Redis
在项目中使用redis做缓存的一些思路
Redis RDB技术底层原理详解
Sep 04 #Redis
使用redis实现延迟通知功能(Redis过期键通知)
Redis集群新增、删除节点以及动态增加内存的方法
Sep 04 #Redis
Redis字典实现、Hash键冲突及渐进式rehash详解
Sep 04 #Redis
You might like
Terran兵种对照表
2020/03/14 星际争霸
PHP 引用是个坏习惯
2010/03/12 PHP
php调用c接口无错版介绍
2014/03/11 PHP
Laravel构建即时应用的一种实现方法详解
2017/08/31 PHP
PHP+MySQL实现输入页码跳转到指定页面功能示例
2018/06/01 PHP
javascript操作html控件实例(javascript添加html)
2013/12/02 Javascript
21个值得收藏的Javascript技巧
2014/02/04 Javascript
基于javascript实现泡泡大冒险网页版小游戏
2016/03/23 Javascript
AngularJS实现树形结构(ztree)菜单示例代码
2016/09/18 Javascript
详解jQuery事件
2017/01/13 Javascript
Bootstrap table 定制提示语的加载过程
2017/02/20 Javascript
ExtJs的Ext.Ajax.request实现waitMsg等待提示效果
2017/06/14 Javascript
vue插件vue-resource的使用笔记(小结)
2017/08/04 Javascript
浅谈在vue项目中如何定义全局变量和全局函数
2017/10/24 Javascript
javaScript产生随机数的用法小结
2018/04/21 Javascript
Vue  webpack 项目自动打包压缩成zip文件的方法
2019/07/24 Javascript
JS基础之逻辑结构与循环操作示例
2020/01/19 Javascript
[06:49]2018DOTA2国际邀请赛寻真——VirtusPro傲视群雄
2018/08/12 DOTA
Python标准库之多进程(multiprocessing包)介绍
2014/11/25 Python
python装饰器-限制函数调用次数的方法(10s调用一次)
2018/04/21 Python
python版本的仿windows计划任务工具
2018/04/30 Python
python多线程调用exit无法退出的解决方法
2019/02/18 Python
Python编程中类与类的关系详解
2019/08/08 Python
如何基于python实现画不同品种的樱花树
2020/01/03 Python
使用TFRecord存取多个数据案例
2020/02/17 Python
Python selenium键盘鼠标事件实现过程详解
2020/07/28 Python
非常详细的C#面试题集
2016/07/13 面试题
建筑毕业生自我鉴定
2013/10/18 职场文书
《小池塘》教学反思
2014/02/28 职场文书
家长学校培训材料
2014/08/20 职场文书
个人培训总结
2015/03/05 职场文书
小学生安全保证书
2015/05/09 职场文书
信仰纪录片观后感
2015/06/08 职场文书
感恩的心主题班会
2015/08/12 职场文书
《走遍天下书为侣》教学反思
2016/02/22 职场文书
零基础学java之循环语句的使用
2022/04/10 Java/Android