Redis 主从复制,哨兵,集群——(3)集群篇
目录一. 前篇回顾二. Redis 集群是什么三. Redis 集群的优点四. Redis 集群的槽位概念五. 什么是分片六. 给定一个数据 key如何确定应该存储在哪个分片七. 分片 槽位的设计有什么好处7.1 方便扩容7.2 方便缩容7.3 平滑迁移数据7.4 方便数据的分块查找八. key 映射到节点的三种解决方案8.1 哈希取余分区8.1.1 哈希取余分区的做法8.1.2 哈希取余分区的优点8.1.3 哈希取余分区的缺点8.2 一致性哈希算法分区8.2.1 一致性哈希算法的背景8.2.2 什么是哈希环8.2.3 redis 服务器IP节点映射8.2.4 数据 key 的映射规则8.2.5 一致性哈希算法的优点8.2.6 一致性哈希算法的缺点8.3 哈希槽分区8.3.1 哈希槽的实质8.3.2 用哈希槽的好处九. 经典面试题9.1 为什么redis集群的最大槽位是163849.2. 为什么集群数量最好不超过1000个十. Redis 不保证数据的强一致性十一. Redis 集群配置属性一. 前篇回顾前两篇主从复制篇和哨兵篇我们聊过了主从复制架构减轻了 master 的数据访问压力但 master 宕机会造成巨大损失Redis 主从复制哨兵集群——1主从复制篇-CSDN博客Redis 主从复制哨兵集群——2哨兵篇-CSDN博客所以为了解决 master 服务器宕机带来的巨大损失就有了主从复制 哨兵选举新 master 的设计模式解决了 master 宕机之后 slave 从机无法进行写数据的缺点尽管如此主从复制哨兵机制仍然无法100%保证数据不丢失比如所有从机都没有完全同步主机数据但是主机挂了就会造成缓存数据缺失因此看在哨兵模式还是有一定的缺陷的主从模式哨兵的设计模式似乎也没有那么优秀。那么有没有更好的解决方案呢当然有啦就是我们本篇要讲的也是大型企业项目开发几乎必备的极为常用的 redis 集群。二. Redis 集群是什么集群从字面意思理解即可就是一个群体对于 redis 来说一个 master 不行那我弄多台。就像我们的分布式系统一样注册中心配置中心都会配置多台多台服务器内部组成一个大的群体对外统一提供服务就称之为集群集群增强了系统的高可用性并且集群之间通常可以共享数据master1 的数据对于 master2 可见master2 的数据对于 master1也可见。用户客户端与 Redis 链接的时候只需要连接任意一个节点即可不需要与所有节点相连。三. Redis 集群的优点1这里要先重申一点使用Redis 通常可以主从复制哨兵也可以直接选择集群同学们可以把集群理解为主从复制哨兵这种模式的PLUS版所以主从复制和哨兵的优点 Redis 集群全都有2除此之外Redis 集群支持多个 master 每一个 master 都可以对外提供读写操作并且每个 master 之后还可以挂载多个 slave相比单台 master 极大的提高了系统数据吞吐能力和高并发处理能力3由于 Redis 集群内部自带了 Sentinel 哨兵故障转移机制已经有了高可用的特性所以在使用 redis 集群的时候不需要再重复配置哨兵它内部就有哨兵机制四. Redis 集群的槽位概念下面是我从 Redis 官网上关于 redis 集群截图过来的一部分说明我把红线圈出来的部分百度翻译一下Redis Cluster主要组件概述密钥分发模型集群的密钥空间被划分为16384个插槽有效地设置了16384个主节点的集群大小上限然而建议的最大节点大小约为1000个节点。集群中的每个主节点处理16384个散列槽的子集。当没有正在进行的集群重新配置时即哈希槽从一个节点移动到另一个节点集群是稳定的。当集群稳定时单个哈希槽将由单个节点提供服务但是服务节点可以有一个或多个副本在网络分裂或故障的情况下这些副本将替换它并且可以用于扩展读取过时数据的读取操作。官网的意思就是说Redis 集群采用的是密钥分发模型不管你准备用几台 Redis 服务器组成集群集群整体固定的会划分出来16384个槽位在集群内部编号就是0~16383你可以简单地把每一个槽位理解为一个储物柜每个储物柜都可以存放很多数据然后这些槽位通常会分散给 redis 集群中的各台服务器也就是说集群中每台 Redis 服务器只负责一部分槽位上的数据存取。并且官方建议 redis 集群的服务器数量不要超过1000台通常情况下也不会弄这么多服务器。五. 什么是分片一句话使用 Redis 集群时我们会将存储的数据分散到多台 Redis 服务器上这就称之为分片。简言之集群中的每个 Redis 服务器都只存储一部分数据不会全量存储。如下图举例刚才也提到过了16384个槽位编号分别为0~16383现在有三台 Redis 服务器将16384个槽位均匀分散到三台 Redis 服务器上第一台存储0~5460号数据第二台存储5461~10922台数据第三台存储10923~16282号数据这就是分片。六. 给定一个数据 key如何确定应该存储在哪个分片经过上面槽位和分片的概念了解现在已经基本清楚每一个分片负责管理不同的槽位。我们往 Redis 集群中存储一条数据实际上是存储到了整个 Redis 集群中某一个分片 Redis 服务器上负责的一个槽位中。那么如何确定一个给定的数据存储到哪台分片的哪个槽位中的我们可以在官网上找到答案英文看不懂没关系小编简单概括一下它的基本原理给定一个确定的数据 keyRedis 是基于CRC16 算法来计算出这个 key 应该存储到哪个槽位中的公式为 CRC16(key)/16384即对算法后得出一个值然后对 16384 插槽数量进行取模计算计算出了这个数据应该存储在哪个槽位中。下次在取数据的时候也是利用相同的算法得出取模数据就可以得知当前数据存放在哪个槽位中然后直接去对应的槽位取数据有数据则直接返回如果没有说明当前缓存暂未存储此数据则走额外程序查询数据库返回数据并回写缓存。这个原理同学们有没有发现类似于 Java 中的 HashMap我们存储数据会先计算键的哈希值然后取模计算出放在那个索引处在取数据的时候同理通过哈希值找到当前数据存储的位置。七. 分片 槽位的设计有什么好处7.1方便扩容假如说现在我们设置了三台 master 三台 slave随着时间的推移我们的用户数量越来越庞大三台 master 不够用了我们就可以再多增加几台进行水平扩容再增加新的服务器之后我们可以将原来的16383个槽位重新打乱分片也可以让原来的三台 master 各自均出来一些数据给新的 master。7.2方便缩容这个和刚才的扩容一个道理可以重新打乱分片也可以让即将下线的 master 将数据分给其他的 master。7.3平滑迁移数据在使用了 Redis 集群之后如果我们要更换数据库服务器或添加减少数据库服务器Redis 集群可以做到不停止服务器的迁移数据这个功能是非常强大的因为正常情况下Redis 要停机修复就会对外停止服务但 Redis 集群就不需要直接平滑升级做到安稳迁移数据用户完全感知不到。7.4方便数据的分块查找因为我们是基于CRC16 算法取模算法得出的槽位所以在取的时候是精确查找再加上 Redis 是内存数据库速度非常非常快。八. key 映射到节点的三种解决方案Redis 集群有很多台服务器除了上面官方提供的一种槽位映射存储方式还有另外两种方式加上官方提供的方式一共有三种也是现在大多数企业内用三种下面对它们分别进行深度解析。8.1 哈希取余分区8.1.1 哈希取余分区的做法这个方式非常简单粗暴假如说我的系统部署了三台Redis 服务器那么就直接对要存储的数据 key做哈希运算对服务器数量3做哈希取模算出来是多少就存储到那台服务器。8.1.2 哈希取余分区的优点简单粗暴直接有效只需要预估数据规划节点例如3台5台7台就能保证一段时间内的数据支撑。使用哈希算法让一部分固定的请求落在同一台服务器上每台服务器固定处理同一批请求起到了负载均衡分而治之的目的。8.1.3 哈希取余分区的缺点进行扩容和缩容时比较麻烦每次节点变动都会导致数据发生变化映射关系都需要重新进行计算如果 master 出故障取模公式就变成了 Hash(key)/? 由于台数发生变化会导致哈希取余重新发生变化。8.2 一致性哈希算法分区8.2.1 一致性哈希算法的背景因为哈希取余分区算法有一定的缺点后来又推出了一致性哈希算法分区。它是由麻省理工学院在1997年提出的目的就是为了解决分布式缓存数据变动与映射问题某台服务器宕机了哈希算法的分母发生了变化取余就会出现问题对应了上面哈希取余算法的缺点。8.2.2 什么是哈希环一致性哈希算法的核心是哈希环。哈希算法必然会有一个哈希函数通过哈希函数产生哈希值是0~2^32-1 范围内的其中一个值我们将所有可能的哈希值构成一个集合那么这个集合就是[02^32-1]它本身是一个线性结构从小到大排列如下图所示但我们通过适当的逻辑运算让 02^32-1使其首尾相接这样这个线性结构在逻辑上就会形成一个环。如下图然后我们将哈希函数得到的值对2^32进行取模即Hash(key) mod 2^32计算后得到的值一定是在 [02^32-1] 这个集合之内的某个值。换言之经过计算得到的值一定会落在这个环上这样就避免了哈希取余分区算法中服务器宕机导致哈希函数分母不确定的问题。8.2.3 redis 服务器IP节点映射经过上面哈希环的构建之后然后就需要将 redis 服务器上的各个IP节点映射到环上的某一位置这里的映射方法有很多种可以选择IP进行哈希映射也可以选择主机名进行哈希映射。如下图假设现在有四台 redis 服务器经过映射之后如下图所示分别分布在哈希环上的四个点8.2.4 数据 key 的映射规则数据 key 的映射规则与节点服务器映射规则相似当我们的用户要存储一个 key 的时候会和节点一样先计算哈希值 Hash(key)然后对 2^32 取模运算即 Hash(key) mod 2^32得到数据之后它会落在换上的某个点只要没有落在某台 redis 节点上那么该 key 就要沿着环顺时针行走直到遇到第一个节点遇到的第一个节点就是它应该存储的 redis 服务器。如下图数据 ObjectA 经过计算落在了 NodeA和NodeD 之间那么它沿着环顺时针行走就会首先遇到 NodeA它就要存储在 NodeA 节点服务器中数据 ObjectB 经过计算落在了 NodeA和NodeB 之间那么它沿着环顺时针行走就会首先遇到NodeB它就要存储在NodeB节点服务器中数据 ObjectC和ObjectD 同理分别存储到C节点和D节点中8.2.5 一致性哈希算法的优点1解决了服务器宕机当值哈希计算出现错误的情况。如下图如果NodeC服务区出现了故障那么ObjectC会越过NodeC继续顺时针滑动寻找下一个节点服务器就能找到NodeD数据ObjectC就能顺利存储到NodeD服务器节点中。2增加服务器时受影响的数据量小扩展性强。如下图假如说我们在NodeA和NodeB之间在加一台新的服务器节点NodeX那么受到影响的数据只有NodeA~NodeX之间的数据数据量较小对业务产生影响也较小。8.2.6 一致性哈希算法的缺点1节点较少时会导致数据倾斜问题。如下图假如只设置了两台节点服务器而且经过IP映射之后两者相距较近。那么大部分数据都会经过滑动最终存储到NodeA只有少量数据存储到了NodeB数据分散不均匀产生数据倾斜问题。8.3 哈希槽分区8.3.1 哈希槽的实质哈希槽它的实质是一个数组大小是 2^14大家可以算一下2^10 10242^4 161024 * 16 16383。这16384个槽位的编号则是 0~16383。8.3.2 用哈希槽的好处1解决了均匀分配问题实际上这个哈希槽是等于在数据和redis节点服务器之间又加了一层如下图前面哈希取余算法和一致性哈希算法都是直接找节点服务器存储。而哈希槽则是在中间加了一层用户存储一个数据时先经过计算得出这个数据应该存储在哪个槽位然后通过槽位取寻找特定的节点服务器。2便于数据的分配和移动因为哈希槽本身并不是与redis节点服务器处于高度绑定的状态我们可以人为的随时更改每一个节点服务器上的哈希槽数量。这样的话我们再增加修改服务器数量时数据都不会受到较大的影响便于数据的分配和移动。九. 经典面试题9.1 为什么redis集群的最大槽位是16384官网上说明了在存放 key 之前会对 key 做CRC16(key)算法。但是CRC16算法可以产生16个bit 位那么最大值为 2^16 65536,槽位号为 0~65535。既然如此redis在设计的时候为什么不用 65536 而要用16384你探究过原因吗下图是在网上截取的一幅图片在GitHub中有人问 Redis 的作者为什么不用 65536 而用 16384我们一起看看 Redis 的作者是如何回答这个问题的。我给大家翻译一下作者的解释(1).正常的心跳包带有节点的完整配置可以用幂等的方式用旧的节点替换新的节点以便更新旧的配置。这意味着它们包含原始节点的插槽配置该节点使用2K的空间和16K(16384)的插槽但是如果使用65K(65536)个插槽则需要8K的空间。(2).同时由于其他设计初衷Redis集群不太可能扩展到1000个以上的主节点。(3).因此16K处于正确的范围内以确保每个主机具有足够的插槽最多可容纳1000个矩阵但数量足够少可以轻松地将插槽配置作为原始位图传播。请注意在小型集群中位图难以压缩因此当N较小时位图将设置的 slot(插槽数) / N位占设置位的很大百分比。作者的回答并不是很好理解其实我在看到这里的时候也是一脸懵逼这里涉及到了 Redis 的底层架构Redis 使用C语言编写的非常复杂。我结合网上的一些资料和视频给大家总结了一个比较好理解的解释结合下图。1如上图这是 Redis 底层关于集群的一些配置信息看红线圈出来的部分第一个说明集群内部服务器之间是通过网络传输 ping 心跳包来互相发送信息的2第二个是关于发送服务器负责的槽信息所发送的信息包的消息头大小是集群数量 / 8如果采用2^16 65536那么每次集群内部服务器与服务器之间传输信息的心跳包消息头就需要发送65536 / 8 8192 bit换算一下就是8KB但如果采用 16384每次传输的心跳包消息头大小为16384 / 8 2048换算一下为 2KB。在网络传输过程中2KB的数据包明显比8KB的数据包传输速率更快并且不容易出现丢包情况9.2. 为什么集群数量最好不超过1000个1如果我们集群内部服务器节点较多假设有10000台每台节点互相传递都需要发送 2KB 的心跳包非常容易网络拥堵。而且16384个槽位分配给1000台节点也够用了所以不需要把节点数量设置的过大。2此外Redis 主节点的配置信息中它所负责的哈希槽是通过一张 bitmap 来进行压缩传输的bitmap 的压缩率 slots槽位数量 / Redis节点数量如果节点数量过大压缩率就会降低如果槽位少节点还多的话压缩率更低不利于网络之间的传输相反如果槽位数量多节点数量少压缩率就会很大槽位数量已经固定死了(16384)所以只能降低节点数量来提高节点之间的传输速率。十. Redis 不保证数据的强一致性这个问题与微服务CAP定理有一定的相似性。我结合官网给出的回答总结成了下面的一段话。redis 集群中 master 主节点与 slave 从节点是采用的异步通信也就是说用户向 master 主节点下达的写数据操作无法第一时间同步给 slave 从节点而是 master 主节点通过发送复制文本传递给 slave 从节点让从节点同步数据。如果 master 主节点还没有来得及将客户端最新下达过来的写入数据命令复制到传输文本中就已经死亡而且短时间内 master 主节点还无法恢复正常那么新选举出来的 master 主节点就会丢失那一部分没来得及写入到复制传出文本中的最新客户段数据客户在与新选组出来的 master 主节点进行数据交互时是无法得到刚才用户已经在上一个 master 中存储的数据的因为新 master 里面根本就没有因此 Redis 集群无法保证数据的强一致性而是选择了CAP理论中的 可用性Availability与分区容错性Partition tolerance。十一. Redis 集群配置属性想要组建Redis 集群只需要在 redis.conf 配置文件中配置几个属性就可以了。如下图主要说最后三个这三个才是集群的主要配置项cluster-enable yes (yes 表示开启集群模式);cluster-config-file (表示集群的配置文件名称在另外的配置文件中再指定其它集群属性)cluster-node-timeout (集群之间的超时时间)