Redis主从集群和哨兵集群
为什么要搭建Redis主从集群?
单节点的Redis的并发能力是有上限的,如果想要提高Redis并发能力,需要搭建主从集群。
主从集群
主从集群结构图:
当客户端向Redis写数据时,会写到master,然后master同步数据到slave;
当客户端向Redis读数据时,会优先从slave读取数据。
搭建主从集群
1.启动多个redis容器
基于Linux环境,使用Docker Compose进行多容器启动。
Docker 是 Docker Compose 的前提,Docker Compose 是为了简化使用 Docker 时多容器应用的管理和部署而设计的。
容器名 | 角色 | IP | 映射端口 |
---|---|---|---|
r1 | master | 192.168.150.101 | 7001 |
r2 | slave | 192.168.150.101 | 7002 |
r3 | slave | 192.168.150.101 | 7003 |
docker-compose.yaml:
services:
r1:
image: redis
container_name: r1
network_mode: "host"
entrypoint: ["redis-server", "--port", "7001"]
r2:
image: redis
container_name: r2
network_mode: "host"
entrypoint: ["redis-server", "--port", "7002"]
r3:
image: redis
container_name: r3
network_mode: "host"
entrypoint: ["redis-server", "--port", "7003"]
安装docker compose可以到其他博主
Docker Compose - 安装和基本使用_docker-compose安装-CSDN博客
将docker-compose.yaml迁移到/root/redis
执行命令
docker-compose up -d
查看节点
可以看到,初始状态下,每个实例都是master。
搭建主从集群
# Redis5.0以前
slaveof <masterip> <masterport>
# Redis5.0以后
replicaof <masterip> <masterport>
有临时和永久两种模式:
-
永久生效:在redis.conf文件中利用
slaveof
命令指定master
节点 -
临时生效:直接利用redis-cli控制台输入
slaveof
命令,指定master
节点
临时生效:
测试读写
在master节点可以写和读,在slave节点只能读
主从同步原理
为什么搭建主从集群后,在master r1写入的信息,在slave r2 r3可以读到信息,说明主从之间完成了数据同步。
全量同步和增量同步
- 全量同步:在该节点第一次来同步,将master的全部数据通过bgsave命令(redis在内存中的所有数据持久化到硬盘中)生成RDB文件,发送到slave执行;
- 增量同步: 该节点不是第一次同步,因为某些原因宕机后又恢复了,此时只需要将slave宕机后master写入的数据同步即可;
全量同步
那master如何判断slave是不是第一次来同步?
replicationID:每一个master节点都有自己的id,在为搭建主从关系前,每一个节点都是master,所以replicationID不同,搭建主从集群后,有些master变成了slave,replicationID也变成了master的replicationID。所以可以根据replicationID是否相同来判断,不同则是第一次来。
增量同步
理论上来说,对重连需要同步新数据的slave执行全量同步也能同步数据,但是全量同步需要先做RDB,然后将RDB文件通过网络传输个slave,成本太高了。
master和slave虽然又主从关系,但是无法监听,说明master是不知道slave什么时候宕机的,那增量同步要从哪里开始?
offset:repl_backing会记录第一次建立主从同步的所有写操作命令,写得越多,offset越大,当主从的offset一致则代表数据一致。
offset记录了master和slave的同步情况,可以根据offset的差距去执行增量同步。
repl_backing文件,这个文件是一个固定大小的数组,只不过数组是环形,也就是说角标到达数组末尾后,会再次从0开始读写,这样数组头部的数据就会被覆盖。
会存在问题,假如长度为10(0-1-2-3-4-5-6--7-8-9-0),在slave宕机时,同步数据0-2,master更新数据绕了一圈又回到了2,这时候就刚好,但是超过2后,就会覆盖数据,slave就无法同步被覆盖的数据。
主从同步优化
Redis哨兵
前面说那么多,主从集群、提高并发和主从同步,那主没怎么办?
哨兵(sentinel)就是用来保证主从集群的高可用性的。
-
状态监控:
Sentinel
会不断检查您的master
和slave
是否按预期工作 -
故障恢复(failover):如果
master
故障,Sentinel
会将一个slave
提升为master
。当故障实例恢复后会成为slave
-
状态通知:
Sentinel
充当Redis
客户端的服务发现来源,当集群发生failover
时,会将最新集群信息推送给Redis
的客户端
1.状态监控
Sentinel
基于心跳机制监测Redis集群服务状态,每隔1秒向集群的每个节点发送ping命令,并通过响应结果来做出判断:
-
主观下线(sdown):如果某sentinel节点发现某Redis节点未在规定时间响应,则认为该节点主观下线。
-
客观下线(odown):若超过指定数量(通过
quorum
设置)的sentinel都认为该节点主观下线,则该节点客观下线。quorum值最好超过Sentinel节点数量的一半,Sentinel节点数量至少3台。
2.故障恢复
在当前master宕机后,Sentinel会根据规则选取一个slave作为新的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
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。
4.故障转移
5.搭建哨兵集群
1.修改docker-compose.yaml:
version: "3.2"
services:
r1:
image: redis
container_name: r1
network_mode: "host"
entrypoint: ["redis-server", "--port", "7001"]
r2:
image: redis
container_name: r2
network_mode: "host"
entrypoint: ["redis-server", "--port", "7002", "--slaveof", "虚拟机ip", "7001"]
r3:
image: redis
container_name: r3
network_mode: "host"
entrypoint: ["redis-server", "--port", "7003", "--slaveof", "虚拟机ip", "7001"]
s1:
image: redis
container_name: s1
volumes:
- /root/redis/s1:/etc/redis
network_mode: "host"
entrypoint: ["redis-sentinel", "/etc/redis/sentinel.conf", "--port", "27001"]
s2:
image: redis
container_name: s2
volumes:
- /root/redis/s2:/etc/redis
network_mode: "host"
entrypoint: ["redis-sentinel", "/etc/redis/sentinel.conf", "--port", "27002"]
s3:
image: redis
container_name: s3
volumes:
- /root/redis/s3:/etc/redis
network_mode: "host"
entrypoint: ["redis-sentinel", "/etc/redis/sentinel.conf", "--port", "27003"]
2.sentinel配置
sentinel announce-ip "虚拟机ip"
sentinel monitor hmaster 虚拟机ip 7001 2
sentinel down-after-milliseconds hmaster 5000
sentinel failover-timeout hmaster 60000
3.执行命令
docker compose down
docker-compose up -d
4.启动哨兵
停止r1(master),s3第一个发现r1客观下线,s3成为leader
s3给r3发送send slaveof no one命令,r3成为master
重启r1,s3发送convert to slave给r1