目录
Redis集群
一、为什么用Redis集群?
二、Redis集群能干什么
三、Redis集群槽位
四、Redis集群的分片
1、哈希取余分区算法
3、哈希槽分区算法
五、集群搭建
1、3主3从redis集群配置
2、3主3从redis集群读写
3、主从容错切换迁移
4、主从扩容
5、主从缩容
由于数据量过大,单个Master复制集难以承担,因此需要对多个复制集进行集群,形成水平扩展每个复制集只负责存储整个数据集的一部分,这就是Redis的集群,其作用是提供在多个Redis节点间共享数据的程序集。
1、Redis集群支持多个Master,每个Master又可以挂载多个Slave。
2、由于Cluster自带Sentinel的故障转移机制,内置了高可用的支持,无需再去使用哨兵功能。
3、客户端与Redis的节点连接,不再需要连接集群中所有的节点,只需要任意连接集群中的一个可用节点即可。
4、槽位slot负责分配到各个物理服务节点,由对应的集群来负责维护节点、插槽和数据之间的关系。
Redis集群使用槽位(slot)来分片和存储数据。每个Redis集群共有16384个槽位,每个槽位可以存储一个键值对。
在Redis集群中,每个节点都负责一部分槽位。当一个节点加入集群时,它会被分配一些槽位。通过这种分片方式,集群中的每个节点都负责处理一部分数据,从而实现数据的分布式存储和处理。
具体来说,Redis集群将16384个槽位均匀地分配给所有的节点。例如,如果集群有3个节点,那么每个节点将被分配约5461个槽位。当一个节点加入或离开集群时,集群会重新计算和重新分配槽位,以保持数据的均衡分布。
通过使用槽位,Redis集群可以提供以下功能:
1、数据分片:将数据划分到不同的节点上,实现数据的分布式存储和处理,提高系统的吞吐量和并发性能。
2、故障转移:当一个节点发生故障时,集群会将该节点负责的槽位自动迁移到其他正常节点上,确保数据的可用性和高可靠性。
3、扩缩容:当需要扩展或缩小集群规模时,可以通过增加或减少节点来重新分配槽位,实现集群的动态扩缩容。
使用Redis集群时我们会将存储的数据分散到多台redis机器上,这称为分片。简言之,集鹏中的每个Redis实例都被认为是整个数据的一个分片。
如何找到给定key的分片?
为了找到给定key的分片,我们对key进行CRC16(key)算法处理并通过对总分片数量取模。然后,使用确定性哈希函数,可以推断将来读取特定key的位置。
通过将数据分片到多个节点,Redis集群可以获得以下好处:
1、增加数据容量:每个节点只负责存储部分数据,可以利用多个节点的存储容量。
2、提高读写性能:由于数据被分散存储在多个节点上,可以同时并行地处理多个读写请求,提高系统的整体吞吐量。
3、实现高可用性:当一个节点发生故障时,集群可以自动将该节点上的哈希槽迁移到其他健康的节点上,确保数据的可用性和系统的高可用性。
该算法的基本思想是,将数据通过哈希函数映射到一个固定数量的分区上,然后通过对分区数取模计算得到对应的分区编号。
优点:只需要预估好数据规划好节点,例如3台、8台、10台,就能保证一段时间的数据支撑
使用Hash算法让固定的一部分请求落到同一台服务器上,这样每台服务器固定处理一部分请求(并维护这些请求的信息),起到负载均衡的作用。
缺点:原来规划好的节点,进行扩容或者缩容就比较麻烦了额,不管扩缩,每次数据变动导致节点有变动,映射关系需要重新进行计算,在服务器个数固定不变时没有问题,如果需要弹性扩容或故障停机的情况下,原来的取模公式就会发生变化:Hash(key)/3会变成Hash(key)/?。此时地址经过取余运算的结果将发生很大变化,根据公式获取的服务器也会变得不可控。
2、一致性哈希算法分区
提出一致性Hash解决方案。目的是当服务器个数发生变动时,尽量减少影响客户端到服务器的映射关系。
步骤:1、算法构建一致性哈希环 2、服务器IP节点映射 3、key落到服务器的落键规则
算法构建一致性哈希环
一致性哈希算法必然有个hash函数并按照算法产生hash值,这个算法的所有可能哈希值会构成一个全量集,这个集合可以成为一个hash空间[0,2^32-1],这个是一个线性空间,但是在算法中,我们通过适当的逻辑控制将它首尾相连(0= 2个32),这样让它逻辑上形成了一个环形空间。
服务器IP节点映射
将集群中各个IP节点映射到环上的某一个位置。将各个服务器使用Hash进行一个哈希,具体可以选择服务器的IP或主机名作为关键字进行哈希,这样每台机器就能确定其在哈希环上的位置。假如4个节点NodeA、B、C、D,经过IP地址的哈希函数计算(hash(ip)),使用IP地址哈希后在环空间的位置如下:
key落到服务器的落键规则
当我们需要存储一个kv键值对时,首先计算key的hash值,hash(key),将这个key使用相同的函数Hash计算出哈希值并确定此数据在环上的位置,从此位置沿环顺时针“行走”,第一台遇到的服务器就是其应该定位到的服务器,并将该键值对存储在该节点上。
如我们有Object A、Object B、object C. object D四个数据对象,经过哈希计算后,
在环空间上的位置如下:根据一致性Hash算法,数据A会被定为到Node A上,B被定为到Node B上,C被定为到Node C上,D被定为到Node D上。
优点:
缺点:
为了解决均匀分配的问题,在数据和节点之间又加入了一层,把这层称为哈希槽(slot),用于管理数据和节点之间的关系,现在就相当于节点上放的槽,槽里放的是数据。
槽解决的是粒度问题,相当于把粒度变大了,这样便于数据移动。哈希解决的是映射问题,使用key的哈希值来计算所在的槽,便于数据分配。
一个集群只能有16384个槽,编号0-16383(0-2^14-1)。这些槽会分配给集群中的所有主节点,分配策略没有要求。
集群会记录节点和槽的对应关系,解决了节点和槽的关系后,接下来就需要对key求哈希值,然后对16384取模,余数是几key就落入对应的槽里。HASH_SLOT = CRC16(key) mod 16384。以槽为单位移动数据,因为槽的数目是固定的,处理起来比较容易,这样数据移动问题就解决了。
面试题:为什么redis集群的最大槽数是16384个(2^14)而不是65536(2^16)个?
(1)如果槽位为65536,发送心跳信息的消息头达8k,发送的心跳包过于庞大。
在消息头中最占空间的是myslots[CLUSTER_SLOTS/8]。当槽位为65536时,这块的大小是:65536:8÷1024=8kb
在消息头中最占空间的是myslots[CLUSTER_SLOTS/8]。当槽位为16384时,这块的大小是:16384:8÷1024=2kb
因为每秒钟,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的压缩率就很低。
Redis集群不能保证其强一致性
Redis集群不能保证其强一致性,可能会出现数据丢失或者不一致的情况。这是因为,Redis集群采用异步复制机制来进行数据同步,而异步复制存在一定的延迟和不确定性,从而导致新的写入请求不能立即被同步和复制到所有节点上。
集群搭建设计图
配置3台虚拟机,每台配置一主一从
修改redis主机配置文件:/myredis/cluster/redisCluster6381.conf
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
bind:指定Redis服务绑定的IP地址。在本例中,使用0.0.0.0表示服务将绑定到所有可用的IPv4地址上。
daemonize:决定Redis是否以守护进程方式运行。当设置为yes时,Redis将在后台运行,不会向终端输出日志信息。
protected-mode:控制Redis是否启用保护模式。当设置为no时,Redis将不会检查访问请求是否来自本地主机。
port:指定Redis服务监听的端口号。在本例中,服务将监听6381端口。
logfile:指定Redis日志文件的路径和文件名。在本例中,日志将写入/myredis/cluster/cluster6381.log文件。
pidfile:指定Redis进程ID文件的路径和文件名。在本例中,文件名为cluster6381.pid。
dir:指定Redis数据库文件和AOF文件所在的目录。在本例中,它们将存储在/myredis/cluster目录下。
dbfilename:指定Redis数据库文件的文件名。在本例中,文件名为dump6381.rdb。
appendonly:决定Redis是否启用AOF持久化。当设置为yes时,Redis将记录所有修改操作,并将其写入AOF文件中。
appendfilename:指定AOF文件的文件名。在本例中,文件名为appendonly6381.aof。
requirepass:设置Redis的访问密码。在本例中,密码为111111。
masterauth:设置Redis主节点的访问密码。在本例中,主节点密码也为111111。
cluster-enabled:决定Redis是否启用集群模式。当设置为yes时,Redis将启用集群模式。
cluster-config-file:指定Redis集群配置文件的路径和文件名。在本例中,文件名为nodes-6381.conf。
cluster-node-timeout:指定Redis集群节点之间的超时时间。在本例中,超时时间为5000毫秒
修改redis从机配置文件:/myredis/cluster/redisCluster6382.conf 添加一下配置
bind 0.0.0.0
daemonize yes
protected-mode no
port 6382
logfile "/myredis/cluster/cluster6382.log"
pidfile /myredis/cluster6382.pid
dir /myredis/cluster
dbfilename dump6382.rdb
appendonly yes
appendfilename "appendonly6382.aof"
requirepass 111111
masterauth 111111
cluster-enabled yes
cluster-config-file nodes-6382.conf
cluster-node-timeout 5000
分别启动6台服务
查看进程看到是以集群的方式开启的
构建主从关系命令
redis-cli -a 111111 --cluster create --cluster-replicas 1 192.168.111.185:6381 192.168.111.185:6382 192.168.111.172:6383 192.168.111.172:6384 192.168.111.184:6385
192.168.111.184:6386--cluster-replicas 1表示为每个master创建一个slave节点
集群配置号后会产生nodes-conf文件
以6381为切入点查看节点状态
查看集群节点关系:CLUSTER NODES
当写入数据时会报这个错误,说明k1要到6385服务器去存储,redis集群需要路由到所在的槽位
解决方法:连接redis时带 -c 参数打开路由
redis-cli -a 111111 -p 6381 -c
从定向到6385这台redis
关闭6381查看6384是否上位
进入6382查看主从关系,看到6381主机掉线,6384从机上位
6831宕机重启后变成了从机,如果还想6381变成主机使用一下命令
CLUSTER FAILOVER
重新配置一主一从
查看是否启动成功
将新增的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.184:6387 192.168.111.185:6381
从新分配槽位
命令:redis-cli -a密码--cluster reshard IP地址:端口号
redis-cli -a 111111 --cluster reshard 192.168.111.175:6381l
分配多少槽位,分配主机ID是多少
检查是否正确分配完成
redis- cli -a 111111 --cluster check 192.168.111.185:6381
为什么6387是3个新的区间,以前的还是连续?
重新分配成本太高,所以前3家各自匀出来一部分,从6381/6382/6383三个旧节点分别匀出1364个坑位给新节点6387
给新主机添加从机
redis-cli -a密码--cluster add-node ip:新slave端口 ip:新master端口--cluster-slave --cluster-master-id新主机节点ID
redis-cli - a 111111 --cluster add-node 192.168.111.184:6388 192.168.111.184.6387
--cluster-slave --cluster-master-id 307a5f6617a6eeb4949f3cb9124edo4c6962c348
重新查看主从关系
检查集群情况获取从节点6388的节点ID
redis- cli -a 111111 --cluster check 192.168.111.184:6385
从集群中将节点6388删除
redis-cli -a密码--cluster del-node ip:从机端口从机6388节点ID
将6387的槽位清空,重新分配槽位
redis- cli -a 111111 -- cluster reshard 192.168.111.185:6381
4096个槽位都指给6381,它变成了8192个槽位,相当于全部都给6381了,不然要输入3次
检查集群情况,此时6381有两个从节点
将6387节点删除
redis-cli -a密码--cluster del-node ip:端口6387节点ID
再检查集群情况、6387和6388被删除
注意:不在同一个slot槽位下的键值无法使用mset.mget等多键操作
可以通过{}来定义同一个组的概念,使key中内相同内容的键值对放到一个slot槽位去
查看槽位是否被占用,1被占用,0未被占用
CLUSTER COUNTKEYSINSLOT 12706