1.Redis主从
单节点Redis的并发能力是有上限的,要进一步提高Redis的并发能力,就需要搭建主从集群,实现读写分离。
1.1.单节点Redis的并发能力
单节点Redis的并发能力确实是有限的。主要原因包括以下几点:
-
单线程模型:Redis采用单线程模型来处理请求,这意味着它一次只能处理一个请求,虽然它的事件驱动机制可以快速切换上下文,但在高并发情况下,仍然会受到限制。
-
网络带宽:单节点的网络带宽也是一个瓶颈。在高并发情况下,网络的吞吐量可能会成为限制性能的因素。
-
内存限制:Redis是基于内存的数据库,内存的大小也会影响其并发处理能力。如果内存不足,可能会导致性能下降。
-
CPU资源:虽然Redis是单线程的,但在高并发情况下,CPU的资源也会成为一个限制因素,尤其是在处理复杂命令时。
为了提高并发能力,可以考虑以下几种方案:
- 主从复制:通过设置主从复制,将读请求分散到多个从节点上。
- 分片:将数据分片到多个Redis实例中,以实现负载均衡。
- 使用Redis Cluster:Redis Cluster可以自动分片和负载均衡,支持更高的并发能力。
1.2.主从集群结构
下图就是一个简单的Redis主从集群结构:
集群中有一个master节点、两个slave节点(现在叫replica)。当我们通过Redis的Java客户端访问主从集群时,应该做好路由:
-
如果是写操作,应该访问master节点,master会自动将数据同步给两个slave节点:
-
1.配置master节点的redis.conf文件,启用复制功能并设置密码(可选)
-
2.启动两个slave节点,并在它们的redis.conf文件中配置连接到master节点的信息
-
3.重启master和slave节点,使配置生效。
-
一旦配置完成并重启节点,master节点将会自动将数据同步给两个slave节点。您可以通过监控命令 INFO replication
来查看复制的状态和信息,确保数据同步正常进行。
-
如果是读操作,建议访问各个slave节点,从而分担并发压力
集群有临时和永久两种模式:
-
永久生效:在redis.conf文件中利用
slaveof
命令指定master
节点 -
临时生效:直接利用redis-cli控制台输入
slaveof
命令,指定master
节点
1.3.主从集群的原理
1.3.1全量同步
在Redis中,全量同步(Full Sync)是指Slave节点在刚刚连接到Master节点时,进行一次完整的数据同步过程。这个过程确保Slave节点和Master节点的数据一致性,以便Slave节点能够开始接收增量同步数据。如下图所示:
- Slave节点连接到Master节点,并发送SYNC命令请求数据同步命令,申请进行全量同步。
- Master节点接收到SYNC请求后,会先判断是否是第一次同步,如果是第一次同步,染回master的数据版本信息,Slave会保存版本信息。
- 接下来Master会在后台创建一个RDB文件,将当前数据库中的数据快照存储到RDB文件中。
- Master节点将生成的RDB文件发送给Slave节点。
- Slave节点接收到RDB文件后,会先清空本地数据,再将其加载到内存中,完成全量同步。
- 一旦全量同步完成,Master节点会继续将增量数据发送给Slave节点,以保持数据的一致性。
这里有一个问题,Masteer
如何得知Salve
是否是第一次来同步呢??
有几个概念,可以作为判断依据:
-
Replication Id
:简称replid
,是数据集的标记,replid一致则是同一数据集。每个master
都有唯一的replid
,slave
则会继承master
节点的replid
-
offset
:偏移量,随着记录在repl_baklog
中的数据增多而逐渐增大。slave
完成同步时也会记录当前同步的offset
。如果slave
的offset
小于master
的offset
,说明slave
数据落后于master
,需要更新。
Slave在做数据同步,必须向master
声明自己的replication id
和offset
,master
才可以判断到底需要同步哪些数据。
由于我么在执行slaveof(认主)命令时,所有的redis节点都是主节点,有自己的replid和offset,所以,我们建立关系后需要对这两个数据进行主从同步。而这两条数据也就成了我们判断主从节点之间,是否是第一次数据交互的凭证。当我们进行第一次数据交互时如果主从节点之间replid和offset这两个数据不一致,就是第一次交互,就要进行全量同步。
1.3.2.增量同步
在Redis中,增量同步(Partial Sync)是指Slave节点在完成全量同步后,接收并应用Master节点的增量数据更新。这个过程确保了Slave节点和Master节点之间的数据保持一致。
增量同步就是只更新slave与master存在差异的部分数据。如图:
增量同步的流程如下:
-
Master节点在完成全量同步后,将持续记录并保存所有的写操作命令(包括SET、DEL等)到内存中的复制缓冲区(replication buffer)中。
-
当有新的写操作发生时(就是不是第一次数据交互),Master节点将这些写操作命令发送给所有连接的Slave节点。
-
Slave节点接收到增量数据更新后,会将这些命令按顺序应用到自己的数据集中,以保持与Master节点的数据一致。
通过增量同步,Slave节点能够及时地接收并应用Master节点的更新,从而保持数据的一致性。需要注意的是,增量同步是异步的,因此在极端情况下可能会有数据延迟。
2.Redis哨兵
主从结构中master节点的作用非常重要,一旦主节点故障就会导致整个集群不可用。那么有什么办法能保证主从集群的高可用性呢?
2.1.哨兵工作原理
Redis提供了哨兵
(Sentinel
)机制来监控主从集群监控状态,确保集群的高可用性。
2.1.1.哨兵作用
哨兵集群作用原理图:
哨兵的作用如下:
-
状态监控:
Sentinel
会不断检查您的master
和slave
是否按预期工作 -
故障恢复(failover):如果
master
故障,Sentinel
会将一个slave
提升为master
。当故障实例恢复后会成为slave
-
状态通知:
Sentinel
充当Redis
客户端的服务发现来源,当集群发生failover
时,会将最新集群信息推送给Redis
的客户端
那么问题来了,Sentinel
怎么知道一个Redis节点是否宕机呢?
2.1.2.状态监控
Sentinel
基于心跳机制监测服务状态,每隔1秒向集群的每个节点发送ping命令,并通过实例的响应结果来做出判断:
-
主观下线(sdown):如果某sentinel节点发现某Redis节点未在规定时间响应(就是会一直ping,节点会响应pong),则认为该节点主观下线。
-
客观下线(odown):若超过指定数量(通过
quorum
设置)的sentinel都认为该节点主观下线,则该节点客观下线。quorum值最好超过Sentinel节点数量的一半,Sentinel节点数量至少3台。
如图:
一旦发现master故障,sentinel需要在salve中选择一个作为新的master,选择依据是这样的:
-
首先会判断slave节点与master节点断开时间长短,如果超过
down-after-milliseconds * 10
则会排除该slave节点 -
然后判断slave节点的
slave-priority
值,越小优先级越高,如果是0则永不参与选举(默认都是1)。 -
如果
slave-prority
一样,则判断slave节点的offset
值,越大说明数据越新,优先级越高 -
最后是判断slave节点的
run_id
大小,越小优先级越高(通过info server可以查看run_id
)。
对应的官方文档如下:
问题来了,当选出一个新的master后,该如何实现身份切换呢?
大概分为两步:
-
在多个
sentinel
中选举一个leader
-
由
leader
执行failover
2.1.3.选举leader
首先,Sentinel集群要选出一个执行failover
的Sentinel节点,可以成为leader
。要成为leader
要满足两个条件:
-
最先获得超过半数的投票
-
获得的投票数不小于
quorum
值
而sentinel投票的原则有两条:
-
优先投票给目前得票最多的
-
如果目前没有任何节点的票,就投给自己
比如有3个sentinel节点,s1
、s2
、s3
,假如s2
先投票:
-
此时发现没有任何人在投票,那就投给自己。
s2
得1票 -
接着
s1
和s3
开始投票,发现目前s2
票最多,于是也投给s2
,s2
得3票 -
s2
称为leader
,开始故障转移
不难看出,谁先投票,谁就会称为leader,那什么时候会触发投票呢?
答案是第一个确认master客观下线的人会立刻发起投票,一定会成为leader。
OK,sentinel
找到leader
以后,该如何完成failover
呢?
2.1.4.failover
我们举个例子,有一个集群,初始状态下7001为master
,7002和7003为slave
:
假如master发生故障,slave1当选。则故障转移的流程如下:
1)sentinel
给备选的slave1
节点发送slaveof no one
命令,让该节点成为master
2)sentinel
给所有其它slave
发送slaveof 192.168.150.101 7002
命令,让这些节点成为新master
,也就是7002
的slave
节点,开始从新的master
上同步数据。
3)最后,当故障节点恢复后会接收到哨兵信号,执行slaveof 192.168.150.101 7002
命令,成为slave
:
3.Redis分片集群
主从模式可以解决高可用、高并发读的问题。但依然有两个问题没有解决:
-
海量数据存储
-
高并发写
要解决这两个问题就需要用到分片集群了。分片的意思,就是把数据拆分存储到不同节点,这样整个集群的存储数据量就更大了。
Redis分片集群的结构如图:
分片集群特征:
-
集群中有多个master,每个master保存不同分片数据 ,解决海量数据存储问题
-
每个master都可以有多个slave节点 ,确保高可用
-
master之间通过ping监测彼此健康状态 ,类似哨兵作用
-
客户端请求可以访问集群任意节点,最终都会被转发到数据所在节点
-
3.1.散列插槽
数据要分片存储到不同的Redis节点,肯定需要有分片的依据,这样下次查询的时候才能知道去哪个节点查询。很多数据分片都会采用一致性hash算法。而Redis则是利用散列插槽(
hash slot
)的方式实现数据分片。详见官方文档:
在Redis集群中,共有16384个hash slots
,集群中的每一个master节点都会分配一定数量的hash slots
。
每一个数据都存储在插槽当中,而每个插槽都有属于自己的一个编号,而这个编号就是在这个集群当中的唯一标识,而集群也知道该插槽在那个master节点下面,这样就不会因为节点的增减而致使,用hash值求余时致使数据找不到。而我们也引入了以大括号里的数据求hash值,然后按照该hash值进行数据的分类,就相当于一个对象有多个属性,而我们如果是按照之前的存储方式去存储,可能致使不同的属性在不同的集群里,这样调用不同的集群也是相当的耗时,从而降低了读的效率。所以,我们才引入了得到相同的hash值的计算方法,这样我们就可以是这些属性存储在同一个集群当中。如下:
当我们读写数据时,Redis基于CRC16
算法对key
做hash
运算,得到的结果与16384
取余,就计算出了这个key
的slot
值。然后到slot
所在的Redis节点执行读写操作。
不过hash slot
的计算也分两种情况:
-
当
key
中包含{}
时,根据{}
之间的字符串计算hash slot
-
当
key
中不包含{}
时,则根据整个key
字符串计算hash slot
例如:
-
key是
user
,则根据user
来计算hash slot -
key是
user:{age}
,则根据age
来计算hash slot
3.3.故障转移
分片集群的节点之间会互相通过ping的方式做心跳检测,超时未回应的节点会被标记为下线状态。当发现master下线时,会将这个master的某个slave提升为master。
标签:slave,Redis,master,原理,数据,节点,集群,底层 From: https://blog.csdn.net/kwb18293575696/article/details/141267710