主从复制
什么是主从复制
主从复制,是指将一台 Redis 服务器的数据,复制到其他的 Redis 服务器。前者称为 主节点(master) ,后者称为 从节点(slave)**。Master以写为主,Slave以读为主。一般用来实现读写分离和容灾恢复且数据的复制是 **单向 的,只能由主节点到从节点。Redis 主从复制支持 主从同步 和 从从同步 两种,后者是 Redis 后续版本新增的功能,以减轻主节点的同步负担。
主从复制主要的作用
- 故障恢复: 当主节点出现问题时,可以由从节点提供服务,实现快速的故障恢复 (实际上是一种服务的冗余)。
- 负载均衡: 在主从复制的基础上,配合读写分离,可以由主节点提供写服务,由从节点提供读服务 (即写 Redis 数据时应用连接主节点,读 Redis 数据时应用连接从节点),分担服务器负载。尤其是在写少读多的场景下,通过多个从节点分担读负载,可以大大提高 Redis 服务器的并发量。
- 防止数据丢失:当主服务器磁盘坏掉之后,其他从服务器还保留着相关的数据,不至于数据全部丢失。
- 高可用基石: 除了上述作用以外,主从复制还是哨兵和集群能够实施的 基础,因此说主从复制是 Redis 高可用的基石。
如何开启&关闭
开启主从复制
原则上就是配从(库)不配主(库),所以只需要在从库输入如下命令即可。
1 | 配置从库 |
如果主服务设置了密码,需要在从服务器输入主服务器的密码,使用 config set masterauth 主服务密码
命令的方式,例如:
1 | 127.0.0.1:6377> config set masterauth 123456 |
注:默认情况下,每次与master断开后,都需要自行重新连接,除非你配置进redis.conf文件
配置只要在从库的redis.conf文件加上下面的语句即可:
1 | replicaof <masterip> <masterport> |
Tips:在执行完 replicaof 命令之后,从服务器的数据会被清空,主服务会把它的数据副本同步给从服务器。
关闭主从复制
我们可以使用 replicaof no one
命令来停止从服务器的复制,操作命令如下:
1 | 127.0.0.1:6379> role #查询当前角色 |
常用的主从方式
一主多仆
含义:就是一个Master节点多个Slave节点
通过info replication
查看主从信息
1 | Replication |
1 | Replication |
注意:
第一次slave1 和slave2切入点,是全量复制,之后是增量复制
主机可以写,但是从机不可以写,从机只能读
主机shutdowm后,从机进入待机状态,等主机恢复后,主机新增记录从机可以顺利复制。
从机shutdowm后,每次与master断开之后,都需要重新连接,除非你配置进redis.conf文件
从机复制到的数据,会被本机持久化。就算shutdown断开连接依然会有数据。
重新连接或者变更master,会清除之前的数据,重新建立拷贝最新的数据
薪火相传
含义:就是上一个Slave可以是下一个slave的Master,Slave同样可以接收其他slaves的连接和同步请求,那么该slave作为了链条中下一个的master,可以有效减轻master的写压力。
注意事项和一主多仆差不多,但注意虽然有slave是相对master,但是依然是slave
反客为主
1 | SLAVEOF no one |
使当前数据库停止与其他数据库的同步,转成主数据库
数据同步方式总结
完整数据同步
当有新的从服务器连接时,为了保障多个数据库的一致性,主服务器会执行一次 bgsave 命令生成一个 RDB 文件,然后再以 Socket 的方式发送给从服务器,从服务器收到 RDB 文件之后再把所有的数据加载到自己的程序中,就完成了一次全量的数据同步。
部分数据同步
在 Redis 2.8 之前每次从服务器离线再重新上线之前,主服务器会进行一次完整的数据同步,然后这种情况如果发生在离线时间比较短的情况下,只有少量的数据不同步却要同步所有的数据是非常笨拙和不划算的,在 Redis 2.8 这个功能得到了优化。
Redis 2.8 的优化方法是当从服务离线之后,主服务器会把离线之后的写入命令,存储在一个特定大小的队列中,队列是可以保证先进先出的执行顺序的,当从服务器重写恢复上线之后,主服务会判断离线这段时间内的命令是否还在队列中,如果在就直接把队列中的数据发送给从服务器,这样就避免了完整同步的资源浪费。
Tips:存储离线命令的队列大小默认是 1MB,使用者可以自行修改队列大小的配置项 repl-backlog-size。
无盘数据同步
从前面的内容我们可以得知,在第一次主从连接的时候,会先产生一个 RDB 文件,再把 RDB 文件发送给从服务器,如果主服务器是非固态硬盘的时候,系统的 I/O 操作是非常高的,为了缓解这个问题,Redis 2.8.18 新增了无盘复制功能,无盘复制功能不会在本地创建 RDB 文件,而是会派生出一个子进程,然后由子进程通过 Socket 的方式,直接将 RDB 文件写入到从服务器,这样主服务器就可以在不创建RDB文件的情况下,完成与从服务器的数据同步。
要使用无须复制功能,只需把配置项 repl-diskless-sync 的值设置为 yes 即可,它默认配置值为 no。
注意事项
主从同步有一些需要注意的点,我们来看一下。
数据一致性问题
当从服务器已经完成和主服务的数据同步之后,再新增的命令会以异步的方式发送至从服务器,在这个过程中主从同步会有短暂的数据不一致,如在这个异步同步发生之前主服务器宕机了,会造成数据不一致。
从服务器只读性
默认在情况下,处于复制模式的主服务器既可以执行写操作也可以执行读操作,而从服务器则只能执行读操作。
可以在从服务器上执行 config set replica-read-only no
命令,使从服务器开启写模式,但需要注意以下几点:
- 在从服务器上写的数据不会同步到主服务器;
- 当键值相同时主服务器上的数据可以覆盖从服务器;
- 在进行完整数据同步时,从服务器数据会被清空。
复制命令的变化
Redis 5.0 之前使用的复制命令是 slaveof,在 Redis 5.0 之后复制命令才被改为 replicaof,在高版本(Redis 5+)中我们应该尽量使用 replicaof,因为 slaveof 命令可能会被随时废弃掉。
哨兵模式(sentinel)
反客为主的自动版,能够后台监控Master库是否故障,如果故障了根据投票数自动将slave库转换为主库。一组sentinel能同时监控多个Master。
使用步骤:
在Master对应redis.conf同目录下新建sentinel.conf文件,名字绝对不能错;
配置哨兵,在sentinel.conf文件中填入内容(可以配置多个):
1
2说明:最后一个数字1,表示主机挂掉后slave投票看让谁接替成为主机,得票数多少后成为主机。
sentinel monitor 被监控数据库名字(自己起名字) ip port 1启动哨兵模式(路径按照自己的需求进行配置):
1
redis-sentinel /myredis/sentinel.conf
注意:
当master挂掉后,会通过选票进行选出下一个master。而且只有使用了sentinel.conf启动的才能开启选票
当原来的master后来后,会变成了slave。
复制原理
Slave启动成功连接到master后会发送一个sync命令;
Master接到命令启动后的存盘进程,同时收集所有接收到的用于修改数据集命令,在后台进程执行完毕之后,master将传送整个数据文件到slave,以完成一次完全同步;
全量复制
:而slave服务在数据库文件数据后,将其存盘并加载到内存中;增量复制
:Master继续将新的所有收集到的修改命令依次传给slave,完成同步;但是只要是重新连接master,一次完全同步(全量复制)将被自动执行。
复制的缺点
延时,由于所有的写操作都是在Master上操作,然后同步更新到Slave上,所以从Master同步到Slave机器有一定的延迟,当系统很繁忙的时候,延迟问题会更加严重,Slave机器数量的增加也会使得这个问题更加严重。
命令
命令 | 作用 |
---|---|
slaveof 主库ip 主库端口 | 配置从库 |
info replication | 查看redis主从复制的情况 |
slaveof no one | 使当前数据库停止与其他数据库的同步,转成主数据库 |
sentinel monitor 被监控数据库名字(自己起名字) 127.0.0.1 6379 1 | 配置哨兵,监视master |
redis-sentinel /myredis/sentinel.conf | 以哨兵模式启动redis |
SpringBoot整合哨兵模式
配置主从服务
为了方便测试,本用例只在一台机器上执行,实际情况不建议这么做,根据自己的情况调整。
在自己的redis安装目录下新建conf文件夹,并拷贝几份redis.conf
文件
1 | cp redis.conf conf/redis6379.conf |
修改reids.conf配置文件,以6379配置为例
1 | vim redis6379.conf |
参考上面的步骤,修改6380、6381节点配置文件,但是要新增一行内容,如下:
1 | replicaof 127.0.0.1 6379 |
注:在很多地方都说这里不能配置成127.0.0.1
或localhost
,但是我本地试了,这个ip可以配成127.0.0.1
,而配置成具体的本地ip(例如192.168.50.96
)不行,感觉这个和主Redis中bind的ip有关
打开三个标签页,依次启动master及两个slave
1 | redis-server conf/redis-6379.conf |
连接master查看情况
1 | master |
验证:
1 | 读写分离验证 |
配置哨兵
拷贝三份sentinel.conf
文件到conf
目录下
1 | cp sentinel.conf conf/sentinel26379.conf |
注:看情况,也可以只拷贝一份,但是建议单数。
修改sentinel26379.conf
1 | port 26379 |
同样的方法,修改sentinel26380.conf
及sentinel26381.conf
开启三个标签页,启动哨兵
1 | redis-sentinel conf/sentinel26379.conf |
代码
pom文件
1 | <dependencies> |
application.yml
1 | server: |
controller代码
1 | package com.redis.sentinel.controller; |
此时运行项目,访问接口,即可进行redis的设置及获取。
测试重连
手动关闭master(截图中对应的是6380),可以看到系统会自动重连上最新选举出来的master(新选举出来的为6379)
当重连上以后,无需重启项目,服务即可恢复正常。
如果不使用默认的lettuce,使用jedis的话,可以参考下面的文章:
https://www.cnblogs.com/spec-dog/p/12572120.html
https://blog.csdn.net/weixin_30409927/article/details/105793877