目录
Redis分布式存储的常见方案有:
1.「主从 (Master-Slave) 模式」主从复制,读写分离,数据备份。
2.「哨兵 (Sentinel) 模式」一主多从,哨兵监控,自动处理故障,容错和恢复。
3.「 集群 (Cluster) 模式」多主多从,数据分片,在线扩容。
Redis集群切片的方式有:
1.「客户端分片」分片逻辑在Redis客户端实现,采用一致性哈希来决定Redis节点。
2.「中间件分片」借助中间件实现Redis节点的路由分派,根据路由规则将请求发送到正确的Redis节点上。
3. 「客户端服务端分片」Redis Cluster模式,客户端采用一致性哈希,服务端提供错误节点的重定向服务。
本文将详细介绍「Redis分布式存储」的3种方案和「Redis集群切片」的3种常见方式。
什么是Redis集群
Redis集群是由多个redis实例组成的集合,将缓存的数据分散到多个redis节点上存储,减轻单个缓存节点的访问压力,达到分流效果,提高了存储容量和吞吐量,提供高可用性和容错恢复能力,实现了水平扩容。
Redis 集群哈希槽分区算法详解_redis 哈希槽-CSDN博客
一:Redis集群的作用和优势
1. 处理大规模数据:将数据分散在多个节点上,从而突破单节点内存限制,有效应对数据量的增长和高并发的访问请求。
2. 高可用性:当主节点发生故障时,集群会自动选举新的主节点,从而实现故障转移,保证高可用性。
3. 可扩展性:Redis集群可以根据业务需求和系统负载,动态添加或删除节点,实现水平扩展,以适应数据规模的变化。
4. 高性能:Redis集群中的各个节点可以并行处理不同的请求,从而提高系统整体吞吐量和响应速度,适合于高并发访问的场景。
5. 自动故障转移:Redis集群通过哨兵节点监控主节点的健康状态,在主节点故障时,自动选举新的主节点,减少服务中断和数据丢失的风险。
1 主从(Master - Slave)模式
「主从模式的原理」
主从模式是指在Redis集群中有一个主节点(Master)和多个从节点(Slave)。
Master节点负责数据的写操作,Slave节点负责数据的读操作。
通过主从复制,将Master节点的数据变更同步到Slave节点。
「主从模式的优点」
1. 数据备份:通过主从复制,从节点备份主节点的数据,提高数据可靠性。
2. 读写分离:将读操作和写操作分配给不同的节点处理,提高系统稳定性。
3. 提升读取性能:通过多个从节点来分担读操作的压力,提高数据的查询效率。
「主从模式的缺点」
1. 没有容错和恢复的能力,存在单点故障:一旦Master节点故障,不会自动选出新的主节点,导致后续所有的写操作直接失败。
2. 可用性低:主节点故障时不能自动实现故障转移,需要人工干预手动切换,故障恢复时间长。
3. 没有实现数据分片,无法在线扩容,并发压力受限于单节点服务器的资源配置。
4. 集群中所有节点保存的都是全量数据,浪费内存空间,且数据量过大时严重影响主从同步的速度。
5. 单个主节点承担所有写操作,写操作受单节点性能瓶颈影响。
「主从模式的应用场景」
适用于数据备份、读写分离、对可用性要求不高,小规模数据处理的场景。
2 哨兵(Sentinel)模式
「哨兵模式的原理」
哨兵模式是在Redis主从集群的基础上加入了哨兵节点,监控主节点和从节点的状态。当Master节点出现故障时,哨兵节点会自动从剩余的Slave节点中选举出一个新的Master节点,并通知其他Slave节点和客户端。
「哨兵模式的优点」
1. 高可用性:提供故障的自动处理能力。
2. 容灾恢复:在主从模式数据备份的基础上,提供容错和自动恢复机制。
3. 读写分离:通过多个从节点来分担读操作的压力,提升读取性能。
4. 解决了单点故障:监测到master节点故障,会自动从slave节点中选出一个节点将其切换成master节点,在选举结束之前,redis会开启保护机制,禁止写操作,直到新的master节点产生。每个哨兵节点都是一个独立的进程,哨兵节点之间也会互相监控。
「哨兵模式的缺点」
1. 依然没有实现数据分片,无法在线扩容,并发压力受限于单节点服务器的资源配置。
2. 每个从节点和主节点的耦合度高,在主节点宕机到重新选举Master的期间,服务不可用。
3. 集群中所有节点保存的都是全量数据,浪费内存空间,且数据量过大时严重影响主从同步的速度。
4. 单个主节点承担所有写操作,写操作受单节点性能瓶颈影响。
「哨兵模式的应用场景」
适用于数据备份、容灾恢复,高可用性、小规模数据处理的场景。
3 集群(Cluster)模式
「Cluster的原理」
在Redis Cluster中,引入slot槽来实现数据分片,slot的整体取值范围是0~16383,每个节点会分配一个slot区间,数据分散在多个节点上。每个分片都是由一个Redis主机和多个从机组成,分片和分片之间是相互平行的。每个master节点负责维护一部分槽,以及槽所映射的键值数据。
在Redis Cluster中,数据被分为16384个槽,每个槽存储一部分数据。
数据分片是用CRC16算法对key进行hash操作,哈希计算的结果是16位的哈希值,将哈希值对16384取模,得到槽位编号。当存取Key的时候,根据槽位编号,找到对应的节点进行数据的读写。
「Cluster的优点」
1. 数据分片:基于slot槽的数据分片,突破单节点的内存限制,实现更大规模的数据分布式存储。
2. 横向扩展:实现在线动态横向扩缩容。
3. 高可用性:每个分片都是一个主节点对应多个从节点,主从复制,当Master出现故障,会从Slave节点中选举一个新的Master继续提供服务,实现自动故障转移。
「Cluster的缺点」
1. slave节点只是一个冷备节点,用于在主节点故障时接替其角色,不提供分担读操作的压力。
2. 对Redis里面的批量操作指令会有限制。
3. 客户端的实现会更加复杂。
「Cluster的应用场景」
适用于海量数据+高并发+高可用的场景。
总结
1.「主从(Master-Slave)模式」适用于数据备份、读写分离、对可用性要求不高,小规模数据处理的场景。不提供容错和恢复功能,主节点故障时需要手动切换。
2.「哨兵(Sentinel)模式」在主从复制的基础上,提供了故障自动处理的能力,适用于数据备份、容灾恢复、高可用性的场景。没有实现数据分片,无法在线扩容,并发压力受限于单节点服务器的资源配置。
3.「集群(Cluster)模式」基于slot槽的数据分片,突破单节点的内存限制,实现更大规模的数据分布式存储,提供了高可用性和在线动态横向扩容的能力,提升数据的读写性能,主要针对于海量数据+高并发+高可用的场景。
从集群架构上来说:
哨兵(Sentinel)模式是中心化的集群实现方案,一主多从。
集群(Cluster)模式是去中心化的集群实现方案,多主多从。
主从模式和哨兵模式每个节点存储的都是全量的数据,当存储的数据量过大时,就需要对数据进行分片后存储到多个redis实例上。此时就要用到Redis Sharding技术。
如果数据量很大,那么建议就用cluster。数据量不是很大时,使用sentinel就够了。
二:Redis集群分片的方式
1.「客户端分片」分片逻辑在Redis客户端实现,采用一致性哈希来决定Redis节点。
优点:不依赖于第三方中间件,服务端的Redis实例彼此独立,相互无关联,分片逻辑和路由规则可控。
缺点:这是一种静态的分片方案,增删Redis节点时需要手工调整分片程序,服务端Redis集群拓扑结构有变化时,每个客户端都需要更新调整,运维成本较高。
2.「中间件分片」Redis客户端把请求发送到中间件(例如Twemproxy、Codis),借助中间件实现Redis节点的路由分派,根据路由规则将请求发送到正确的Redis节点上。
优点:支持平滑增减Redis实例而无需修改任何的代码。
缺点:每个请求都经过代理才能到达Redis服务器,过程中产生性能损失。
3. 「客户端服务端分片」Redis Cluster模式,客户端采用一致性哈希,服务端提供错误节点的重定向服务。
三:一致性哈希算法和哈希槽区别
一致性哈希是在哈希取余算法基础之上优化的,分布式缓存数据变动和映射问题,某个机器宕机了,分母数量改变了,自然取余数不对了。用于解决分布式缓存系统中的数据选择节点存储问题和数据选择节点读取问题以及在增删节点后减少数据缓存的消失范畴,防止雪崩的发生。
哈希槽是在redis cluster集群方案中采用的,redis cluster集群没有采用一致性哈希方案,而是采用数据分片中的哈希槽来进行数据存储与读取的。
1 一致性哈希
一致性hash是指将 “存储节点” 和 “数据” 都映射到一个首尾相连的hash环上。如果增删节点,仅影响该节点在hash环上顺时针相邻的后继节点,其他数据不会受到影响。
三步:
1:算法构建一致性哈希环。
对存储节点进行哈希计算,也就是对存储节点做哈希映射,比如根据节点的 IP 地址进行哈希(用ip地址对环上的节点个数进行hash,比如ip地址为1.2.3.4,节点个数N,那么可映射到第1234%N的位置上)
2:服务器IP节点映射。
当对数据进行存储或访问时,对数据进行哈希映射; 将存储的key进行hash(key),然后将其值要分布在这个闭合圆上。
一致性Hash算法使用 “取模法”,且是对 2^32 次方取模,其可表示的范围为:0 ~ 2^32-1。
3:key落到服务器的落键规则。
将数据key使用Hash函数计算出哈希值,并确定此数据在环上的位置,从此位置沿环顺时针“行走”,第一台遇到的服务器就是其应该定位到的服务器!
比如:a、b、c三个key,经过哈希计算后,在环空间上的位置如下:key-a存储在node1,key-b存储在node2,key-c存储在node3。
与普通的hash算法有何不同?
普通的hash算法是对节点数进行hash,而一致性hash是对固定值2^32进行取模。
优点:
将数据key使用Hash函数计算出哈希值,并确定此数据在环上的位置,从此位置沿环顺时针“行走”,第一台遇到的服务器就是其应该定位到的服务器!
比如:a、b、c三个key,经过哈希计算后,在环空间上的位置如下:key-a存储在node1,key-b存储在node2,key-c存储在node3。一致性Hash算法对于节点的增减都只需重定位环空间中的一小部分数据,具有较好的容错性和可扩展性。
新增服务器节点/删除服务器节点:
在hash环中新增服务器,也是通过hash算法确认分布,只需要移动一小部分数据即可;移除也是同理(比如下图中想删除4节点,只需把原本1~4节点之间的数据移动到2节点上即可)。
缺点:(数据倾斜)
但是一致性哈希算法不能够均匀的分布节点,会出现大量请求都集中在一个节点的情况,在这种情况下进行容灾与扩容时,容易出现雪崩的连锁反应。
当在服务器节点数量太少的时候,容易出现分布不均而导致数据倾斜。
例如:系统中只有两台服务器,此时必然造成大量数据集中到Node 2上,而只有极少量会定位到Node 1上。所以用一致性哈希算法时,节点太少容易出现数据倾斜,就达不到了分布式效果。其环分布如下
总结:一致性Hash算法,主要是考虑到分布式系统每个节点都有可能失效,并且新的节点很可能动态的增加进来的情况。如何保证当系统的节点数目发生变化的时候(新增/删减),我们的系统仍然能够对外提供良好的服务(不用停掉所有redis服务),这是值得考虑的!
2 哈希槽
哈希槽解决了数据均匀分布的问题,在数据和节点之间又加入了一层,把这层称之为哈希槽,用于管理数据和节点之间的关系,现在就相当于节点上放的是槽,槽里放数据。
槽解决的是粒度问题,相当于把粒度变大了,这样便于数据移动。
原理
- 只是进行不重复的基数统计,不是集合也不保存数据,只记录数量而不是具体内容。
- 有误差,非精准统计,牺牲准确率来换取空间,误差仅仅只是0.81%左右。误差来自Redis之父回答。
redis cluster采用数据分片的哈希槽来进行数据存储和数据的读取。redis cluster一共有2^14(16384)个槽,所有的master节点都会有一个槽区比如0~1000,槽数是可以迁移的。master节点的slave节点不分配槽,只拥有读权限。但是注意在代码中redis cluster执行读写操作的都是master节点,并不是读是从节点,写是主节点。
为什么是16384个槽?
hash方式:
一个redis集群包含 16384 个哈希槽,编号0-16383(0-2*14-1)。这些槽会分配给集群中的所有主节点,分配策略没有要求,可以指定哪些编号的槽分配给哪个主节点。集群会记录节点槽的对应关系,解决了节点和槽的关系后,接下来就需要对key求哈希值,然后对16384取余,余数是及,那么Key就落入对应的槽里。集群使用公式 CRC16(key) % 16384 来计算键 key 属于哪个槽。
一个redis节点包含N个槽,数据通过hash算法哈希到固定的槽里,所以槽只是决定了数据的存放位置,当多个数据hash出来的结果相同时,他们就被分配到相同的槽里,即也会映射到相同的服务节点上。
在握手成功后,两个节点之间会定期发送ping/pong消息,交换数据信息,在redis节点发送心跳包时需要把所有的槽信息放到这个心跳包里,以便让节点知道当前集群信息,在发送心跳包时使用char进行bitmap压缩后是2k(16384÷8÷1024=2kb),也就是说使用2k的空间创建了16k的槽数。
虽然使用CRC16算法最多可以分配65535(2^16-1)个槽位,65535=65k,压缩后就是8k(8 * 8 (8 bit) * 1024(1k) = 8K),也就是说需要需要8k的心跳包,作者认为这样做不太值得;并且一般情况下一个redis集群不会有超过1000个master节点,所以16k的槽位是个比较合适的选择。
再来一个图
①如果槽位为65536,发送心跳信息的消息头达8k,发送的心跳包过于庞大。
②redis的集群主节点数量基本不可能超过1000个。集群节点越多,心跳包的消息体内携带的数据越多。如果节点过1000个,也会导致网络拥堵。因此redis作者,不建议redis cluster节点数量超过1000个。
③槽位越小,节点少的情况下,压缩率高
Redis主节点的配置信息中,它所负责的哈希槽是通过一张bitmap的形式来保存的,在传输过程中,会对bitmap进行压缩,但是如果bitmap的填充率slots / N很高的话(N表示节点数),bitmap的压缩率就很低。
如果节点数很少,而哈希槽数量很多的话,bitmap的压缩率就很低。
哈希槽和一致性哈希相比
并不是闭合的,key的定位规则是根据CRC-16(key)%16384的值来判断属于哪个槽区,从而判断该key属于哪个节点,而一致性哈希是根据hash(key)的值来顺时针找第一个hash(ip)的节点,从而确定key存储在哪个节点。
一致性哈希是创建虚拟节点来实现节点宕机后的数据转移并保证数据的安全性和集群的可用性的。redis cluster是采用master节点有多个slave节点机制来保证数据的完整性的,master节点写入数据,slave节点同步数据。当master节点挂机后,slave节点会通过选举机制选举出一个节点变成master节点,实现高可用。但是这里有一点需要考虑,如果master节点存在热点缓存,某一个时刻某个key的访问急剧增高,这时该mater节点可能操劳过度而死,随后从节点选举为主节点后,同样宕机,进入fail状态。
扩容和缩容
一致性哈希算法在新增和删除节点后,数据会按照顺时针来重新分布节点。而redis cluster的新增和删除节点都需要手动来分配槽区。
为什么redis集群不采用一致性哈希算法?
一致性哈希的节点分布基于圆环,无法很好的手动控制数据分布,比如有些节点的硬件差,希望少存一点数据,这种很难操作(还得通过虚拟节点映射,总之较繁琐)。
而redis集群的槽位空间是可以用户手动自定义分配的,类似于 windows 盘分区的概念,可以手动控制大小。
其实,无论是一致性哈希还是哈希槽的方式,在增减节点的时候,都会对一部分数据产生影响,都需要我们迁移数据,当然,redis集群也提供了相关手动迁移槽数据的命令。