Redis7_08 基础篇 第八章 Redis集群(cluster)

本文详细介绍了Redis集群的工作原理,包括什么是Redis集群、功能特性、槽位与数据分片的重要性,以及不同映射算法的比较。重点讲解了CRC16算法和16384槽位的选择,以及如何通过一致性哈希确保高可用性和在集群扩展和故障时的数据迁移策略。
摘要由CSDN通过智能技术生成

集群的引入

上一章我们学习了哨兵机制,哨兵有一个缺陷,就是不能保证数据不丢失。

如何解决?

答:使用Redis的集群

1 Redis集群(cluster)简介

1.1 什么是Redis 集群

        数据量过大时单个Master复制集难以承担,因此需要对多个复制集进行集群,形成水平扩展,每个复制集只负责存储整个数据集的一部分,这就是Redis的集群,其作用是提供在多个Redis节点间共享数据的程序集

       通俗的解释就是 多个redis主从复制服务的组合(集合),每个服务都只负责整个数据集的一部分,并且互相都能共享数据。

1.2 Redis集群的功能

  • Redis集群支持多个Master每个Master又可以挂载多个slave
    • 支持数据的高可用
    • 读写分离
    • 支持海量数据的读写存储操作
  • 由于cluster自带Sentinel的故障转移机制,内置了高可用的支持,无需再去使用哨兵功能
  • 客户端与Redis的节点连接,不再需要连接集群中所有的节点,客户端只需要任意连接集群中的一个可用节点即可
  • 槽位slot负责分配到各个物理服务节点,由对应的集群来负责维护节点、插槽和数据之间的关系

2 Redis集群的算法——分片与槽位slot

2.1 集群中的槽位

       槽位就是redis集群中用于存放key的一段空间。Redis集群中存放key的空间被分成了2^14=16384个槽位。这个槽位的序号就对应着每个key对应的CRC16算法值再取模16384的值。就是说,每个key的槽位怎么算呢?使用如下公式计算即可。

hash_slot =  CRC16(key)mod 16384

        CRC16 算法源码

cluster.c的KeyHashSlot方法

2.2 Redis集群的数据分片 

就是每个redis主从服务,都只负责16384的一部分(一般都是平均分配),这称为分片。

2.3 使用槽位和分片 有何优势?

便于分派查询数据集群扩容集群缩容。

2.4 slot槽位映射算法方案对比

        其实在槽位介绍时我们已经讲了本章集群使用的槽位映射算法,但实际上还有其余两种映射算法方案,我们有必要了解一下。看看到底哪一个更适合集群,更有优势。换句话说,我们为什么就使用了CRC16算法 取模 16384 外加 数据分片来映射槽位呢?

方案一 哈希取余分区

如何映射的?

        计算完哈希值,直接对主机个数取余,结果为几就放在第几个主机上。

        hash(key)mod 主机个数

        

        有何劣势?

        扩容缩容时,时间成本太高,要重新映射所有数据。

        这种算法,在扩容或者缩容时,会将全部数据都重新洗牌一遍,比较耗时,耗费成本。

        比如 原来是对3取余,现在要扩容,加一个主机,就要对4取余,那现在就要对所有数据重新洗牌,都算一算对4取余是多少,在存入对应的主机。比较耗时。同样地,缩容也有类似的缺陷和问题。

方案二 一致性哈希算法分区

如何映射的?
槽位的计算

        方案一计算时,分母可能会变动,使得我们扩容缩容时花销太大,而该方案就解决了分母变动问题。它使用了一致性哈希环的设计。

        一致性哈希算法必然有个hash函数并按照算法产生hash值,这个算法的所有可能哈希值会构成一个全量集,这个集合可以成为一个hash空间[0,2^32-1],这个是一个线性空间,但是在算法中,我们通过适当的逻辑控制将它首尾相连(0 = 2^32),这样让它逻辑上形成了一个环形空间。

        该环上对应着2^32个槽位,槽位计算公式。

        hash(key)mod 2^32

数据分区

        槽位能够定下来了,那么如何确定主机们都管理哪些槽位呢?

        该算法是这样设计的。他让各个redis服务节点,映射到该环的不同位置上。

        

        落键规则是,该key沿着顺时针方向遇到的第一个节点,就是管理该key的节点。

        言外之意,从节点(redis服务)本身,逆时针走,直到遇到下一个节点停止。走过的这部分槽位,都归该节点管理。

        

有何优势劣势?

优势:

        当需要扩容缩容时(或者某个redis服务宕机时),受影响的数据比较少。

        比如,a节点宕机了,那么受影响的数据就只是a管理的这一部分数据,这部分数据会交给a的顺时针方向的下一个节点来管理。同样地,要扩容时,也只是修改一部分数据即可。

劣势:

        当节点分配不合理,或者节点个数太少时,会出现节点之间管理的槽位数目不均衡的问题。

        也即数据倾斜问题。

        如图 这种情况下,A管理了大约80%的数据,造成节点间的不均衡。

        

方案三 哈希槽分区

其实就是开篇槽位讲解之处说过的映射算法,外加数据分片实现的。

hash_slot =  CRC16(key)mod 16384

        Redis 集群中内置了 16384 个哈希槽,redis 会根据节点数量大致均等的将哈希槽映射到不同的节点。当需要在 Redis 集群中放置一个 key-value时,redis先对key使用crc16算法算出一个结果然后用结果对16384求余数[ CRC16(key) % 16384],这样每个 key 都会对应一个编号在 0-16383 之间的哈希槽,也就是映射到某个节点上。

        

优势

        对比方案一方案二,该方案是最优解,是生产中用的映射算法。该算法在扩容缩容时,非常友好,且不存在数据倾斜问题。因此我们直接使用的映射算法就是该方案。

        扩容时,只需要把每个redis主机管理的槽位都匀出一部分给新加入的主机就可以了。缩容同理。

2.5 面试题

为什么Redis集群最大槽位要设置为16384?

        redis官方设计师,安特雷兹的回答。

        

答:

      1.正常的心跳数据包带有节点的完整配置,可以用幂等方式用旧的节点替换旧节点,以便更新旧的配置。这意味着它们包含原始节点的插槽配置。

        那么插槽配置越大,心跳数据包就会越大。

        若是2^16=65535个槽位 心跳包将达到65535/8/1024=8k

        若是2^14=16384个槽位 心跳包只有16384/8/1024=2k

        因为每秒钟,redis节点需要发送一定数量的ping消息作为心跳包,如果槽位为65536,这个ping消息的消息头太大了,浪费带宽。

        2.集群节点越多,心跳包的消息体内携带的数据越多。如果节点过1000个,也会导致网络拥堵。因此redis作者不建议redis cluster节点数量超过1000个。 那么,对于节点数在1000以内的redis cluster集群,16384个槽位够用了。没有必要拓展到65536个。
        3. 槽位越小,节点少的情况下,压缩比高,容易传输

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

Redis集群能否保证强一致性?

        Redis集群本身不能保证强一致性,因为它采用的是分片(sharding)的方式来水平扩展,数据被分布存储在不同的节点上。在Redis集群中,数据的写入是通过将数据分散存储在不同节点上来实现水平扩展的,每个节点只负责管理部分数据。这种设计使得Redis集群在某些情况下可能会出现数据不一致的情况,即可能存在数据写入一个节点成功,但由于某些节点未及时同步或者网络延迟等原因,导致其他节点上的数据并未更新的情况。

3 实战演示

本文采取的演示方式是,所有的redis服务都在同一虚拟机上,也就是同ip的。

3.1 三主三从集群搭建

3.1.1三主三从的配置

       创建六个文件

 vim /myredis/cluster/redisCluster6381.conf
 vim /myredis/cluster/redisCluster6382.conf
 vim /myredis/cluster/redisCluster6383.conf
 vim /myredis/cluster/redisCluster6384.conf 
 vim /myredis/cluster/redisCluster6385.conf
 vim /myredis/cluster/redisCluster6386.conf

         每个文件可以按照以下内容配置

        注意,port logfile pidfile dbfilename appendfilename  cluster-config-file 都要设置为对应的端口号才行  

bind 0.0.0.0
daemonize yes
protected-mode no
port 6381
logfile "/myredis/cluster/cluster6381.log"
pidfile /myredis/cluster6381.pid
dir /myredis/cluster
dbfilename dump6381.rdb
appendonly yes
appendfilename "appendonly6381.aof"
requirepass 111111
masterauth 111111
 
cluster-enabled yes
cluster-config-file nodes-6381.conf
cluster-node-timeout 5000

3.1.2 启动六台redis      

redis-cli -a 111111 /myredis/cluster/redisCluster6381.conf

        

       查看进程看看启动是否成功

        

3.1.3 配置主从关系

注意,ip地址写自己的,ip顺序无关,就按照给的命令执行也可以

redis-cli -a 111111 --cluster create --cluster-replicas 1 192.168.111.175:6381 192.168.111.175:6382 192.168.111.172:6383 192.168.111.172:6384 192.168.111.174:6385 192.168.111.174:6386

使用命令查看集群信息 ip写自己的ip 写哪一个机器随意 只要是急集群中的redis服务就行

redis-cli -a 111111 --cluster check 127.0.0.1:6381

能看到以下返回 就说明集群配置成功了

3.1.4 集群信息查看

info replication        查看主从复制的相关信息
cluster info              查看集群所有节点的信息  
cluster nodes          查看集群整体信息

3.2 三主三从集群读写

        新增key 发现会出现 错误 提示 请移动到某某端口

        如何解决上述错误,需要开启路由,在用客户端连接服务时最后跟上-c就可以了

3.3 主从容错切换演示

        关闭某个主机

        

查看 nodes信息发现 还是有三个主机,说明实现了从机上位的功能

   可以正常使用

恢复6383的连接

6383的主机指向了6386

3.4 手动容错切换演示

3.3中我们交换了6383 和6386的身份,使得6386成为了主机,6383反而成为了从机。

那我们能不能手动的切换主从机?模拟集群自带的failover的操作?

可以的

在6383的客户端执行

cluster failover

3.5 主从扩容演示

为了扩容,我们新配置两个配置文件(内容回看本文即可找到) 

开启 6387 6388的redis服务

将新增的6387作为master节点加入原有集群

redis-cli -a 密码 --cluster add-node 自己实际IP地址:6387 自己实际IP地址:6381
6387 就是将要作为master新增节点
6381 就是原来集群节点里面的领路人,相当于6387拜拜6381的码头从而找到组织加入集群
redis-cli -a 111111  --cluster add-node 192.168.111.174:6387 192.168.111.175:6381

检查集群情况  6387 目前没有任何槽位

给6387分配4096个槽位

redis-cli -a 111111 --cluster reshard 127.0.0.1:6381

此时6387还没有从机,指定6388为其从机即可

需要6387主机的id  可以使用前面的集群信息查看命令 cluter nodes 来查看

redis-cli -a 111111 --cluster add-node 6388的ip:6388 6387的ip:6387 --cluster-slave --cluster-master-id 6387主机的id

redis-cli -a 111111 --cluster add-node 192.168.111.174:6388 192.168.111.174:6387 --cluster-slave --cluster-master-id 4feb6a7ee0ed2b39ff86474cf4189ab2a554a40f

检验一下 6388 确实成为了6387的从机

查看集群信息,发现扩容成功了!四主四从

3.6 主从缩容演示

加上6387 6388后,现在来实现以下缩容 删除6387 6388

要缩容,先删去从机节点

redis-cli -a 111111 --cluster del-node 127.0.0.1:6383 6383的id

查看集群信息

开始删除主节点,删除前要把6387节点的槽位移走,移到其他主节点上

redis-cli -a 111111 --cluster reshard 127.0.0.1:6381

槽位移动完毕,查看集群信息,发现6387没有槽位后,自动变味了从机,从属于6381

删去6387节点

三主三从,成功实现了缩容!!!

4 常用命令补充

带通识符的set get命令

mset k1{x} v1 k2{x} v2 ...
mget k1{x} k2{x}  ...

不在同一个slot槽位下的键值无法使用mset、mget等多键操作。

可以通过{}来定义同一个组的概念,使key中{}内相同内容的键值对放到一个slot槽位去,对照下图类似k1k2k3都映射为x,自然槽位一样。

这也就解释了为什么CRC16 算法源码 cluster.c的KeyHashSlot方法 中有对 “{” 和 “}” 的判断

集群是否完整才能对外提供服务的配置

默认YES,现在集群架构是3主3从的redis cluster由3个master平分16384个slot,每个master的小集群负责1/3的slot,对应一部分数据。

cluster-require-full-coverage: 默认值 yes , 即需要集群完整性,方可对外提供服务 通常情况,如果这3个小集群中,任何一个(1主1从)挂了,你这个集群对外可提供的数据只有2/3了, 整个集群是不完整的, redis 默认在这种情况下,是不会对外提供服务的。

如果你的诉求是,集群不完整的话也需要对外提供服务,需要将该参数设置为no ,这样的话你挂了的那个小集群是不行了,但是其他的小集群仍然可以对外提供服务。

CLUSTER COUNTKEYSINSLOT 槽位数字编号

例如 执行CLUSTER COUNTKEYSINSLOT 1234

若返回:

        1,该槽位被占用
        0,该槽位没占用

CLUSTER KEYSLOT 键名称 

例如 cluster keyslot k1 就会返回该key的槽位

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

孤尘Java

感谢认可!感谢您的打赏!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值