Redis主从复制
1、概述
主从复制,一个Master有多个slave,将一台redis服务器数据,复制到其他的redis服务器,前者称为主节点(master/leader),后者称为从节点(slave、follower),是一种主机数据更新后根据配置和策略,自动同步到备机【从机】的master/slaver机制 。数据的复制是单向的,只能从主节点复制到从节点,Master以写为主,Slave以读为主,主从复制,读写分离!!事实情况下80%都在读,减缓服务器的压力,最低配都是1主2从,后面会讲到哨兵模式,会选举,所以要至少2个从机
默认情况下,每台Redis服务器都是主节点,一个主节点可以有0个或者多个从节点,但每个从节点只能由一个主节点。
架构图 :
- 主节点:写操作:
set key value收集数据 - 从节点:读操作:
get key读取数据 - 主节点和从节点之间要保持数据的同步:异步复制方式
主从复制作用包括:
- 数据冗余:实现了数据的热备份,是持久化之外的一种数据冗余方式
- 故障恢复:主节点出现问题,从节点可以代替提供服务,实现快速的故障恢复,实际上是一种服务的冗余
- 负载均衡:在主从复制的基础上,配合读写分离,主节点提供写服务,从节点提供读服务,写redis数据时连接主节点,读redis数据连接从节点,分担服务器负载,尤其在写少读多的场景下通过,多个从节点分担负载,可以提高redis性能
- 高可用(集群)基石:主从复制是哨兵、集群,能够实施的基础,主从复制是高可用的基础
为什么使用集群
一般来说,要将Redis运用于工程项目中,只使用一台Redis是万万不能的(宕机),原因如下:
- 从结构上来说,单台Redis服务器会发生单点故障,处理所有请求负载,压力较大
- 从容量上来说,单台服务器内存容量有限(就算256G)。一般来说,单台Redis最大使用内存不应该超过20G
通常的电商网站都是一次上传,无数次浏览,也就是读多写少
因此就必须进行集群的布置,即使用如上所示的主从复制架构
2、环境配置(单机多集群)
只配置从库,不用配置主库!
1、启动一个Redis服务,查看当前库的信息(测试)
127.0.0.1:6379> info replication //查看当前库的信息
# Replication
role:master //默认角色 master
connected_slaves:0 //没有从机
master_failover_state:no-failover
master_replid:4d7723377ff8793e81a41cd6a57a6d7928ca6581
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:0
second_repl_offset:-1
repl_backlog_active:0
repl_backlog_size:1048576
repl_backlog_first_byte_offset:0
repl_backlog_histlen
2、启动四个Linux 终端模拟4个Redis客户端(1 主、2 从、1 测试),即在默认的测试客户端上复制3个配置文件,然后修改对应的信息
[root@lemon bin]# cd /etc
[root@lemon bin]# ls
redis.conf
[root@lemon bin]# cp redis.conf redis79.conf
[root@lemon bin]# cp redis.conf redis80.conf
[root@lemon bin]# cp redis.conf redis81.conf
[root@lemon bin]# ls
redis79.conf redis80.conf redis81.conf redis.conf
修改 redis79.conf (主机),redis80.conf(从机1号),redis81.conf(从机2号),模板如下:
1.端口号:port port 6381
2.后台运行:daemoniza yes
3.pid 名字 pidfile /var/run/redis_6381.pid
4.log 文件名字 logfile "6381.log"
5.dump.rdb名字 dbfilename dump6381.rdb
6.关闭aof,改为no
3、分别启动这三个服务,并在测试机器上查看进程
查看进程信息
[root@lemon bin]# ps -ef|grep redis
root 14516 1 0 15:58 ? 00:00:00 redis-server 127.0.0.1:6379
root 14551 1 0 15:59 ? 00:00:00 redis-server 127.0.0.1:6380
root 14591 1 0 15:59 ? 00:00:00 redis-server 127.0.0.1:6381
root 14659 13336 0 16:00 pts/6 00:00:00 grep --color=auto redis
[root@lemon bin]#
到此,集群环境搭建完毕
3、主从复制:一主两从
3.1、配置从机
默认情况下,每一台Redis服务器都是主节点,因此下面来开始配置从机
#一主(79)二从(80、81)
#配置从机
127.0.0.1:6380> slaveof 127.0.0.1 6379 #认老大
127.0.0.1:6381> slaveof 127.0.0.1 6379
127.0.0.1:6381> info replication
# Replication
role:slave #这时候就是从机
master_host:127.0.0.1 #从机可以查看到其主人--主机的信息
master_port:6379
主机中会显示从机的配置
#在主机79进行查看
127.0.0.1:6379> info replication
# Replication
role:master
connected_slaves:2 #多了两台从机
slave0:ip=127.0.0.1,port=6380,state=online,offset=602,lag=1
slave1:ip=127.0.0.1,port=6381,state=online,offset=602,lag=1
master_replid:3f9a8d15d0e7a2b3978ad8e6cfc1d8fc490b464e
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:602
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:602
127.0.0.1:6379>
同样,从机也会显示主机的信息
#在从机80进行查看
127.0.0.1:6380> info replication
#Replication
role:slave #现在是从机
#主机的信息
master_host:127.0.0.1
master_port:6379
................
.............
真实的主从配置,应该在每个从机的配置文件中配置(replicaion),这样的话是永久的,这里使用的命令,暂时的。
3.2、细节
- 主机可以读也可以写, 但是多用于写,即设置值
set key value,从机不能设置值,只能读get key,在从机上写数据会报错 - 主机的所有信息和数据,都会被自动被从机保存,防止主机故障。
- 如果使用命令行来配置主从,这个时候重启了,从机就会变回主机,但只要变为从机,立马就会从主机中获取值
- 当主机断电宕机后,默认情况下从机的角色不会发生变化,集群中只是失去了写操作,不影响slave的读,当主机恢复以后,又会连接上从机恢复原状。
- 当从机断电宕机后,不影响其他slave的读和master的读和写,但是若不是使用配置文件配置的从机,即使用命令行来配置主从,再次重新启动后从机就会变回主机,作为主机是无法获取之前主机的数据的,若此时重新配置成为从机,又可以获取到主机的所有数据。这里就要提到一个同步原理。
3.3、主从复制原理
slave启动成功连接到master后会发送一个sync同步命令
master接到sync命令,启动后台的存盘进程,同时收集所接收到的用于修改数据集命令(RDB快照持久化),后台执行完毕之后,
master将传送整个数据文件到slave,并完成一次完全同步
- 全量复制:slave服务在接受到数据库文件数据后,将其存盘并加载到内存中
- 增量复制:master继续将新的所有收集到的修改命令依次传给slave,完成同步,叫增量复制,即后来增加的
但是只要重新连接master,一次完全同步(全量复制)将被自动执行,数据一定能在从机中看到
4、主机宕机后手动配置主机
4.1、一主两从方式
从机故障:
比如将从机6380挂掉(shutdown),在6379主机通过info replication查看,发现它的从服务器只有1台,即6381。从机6380挂掉后,如果在主机进行写操作
set k1 v1
set k2 v2
set k3 v3
此时6380从机如果启动起来,会出现什么情况,即切入点问题:从机是从头开始复制还是从切入点开始复制?比如从k3进来,那之前的k1,k2是否也可以复制?答案就是重启之后,首先它会重新变成主机,不再是从机,必须通过命令slaveof 127.0.0.1 6379,才能重新变为主机6379的从机。通过keys *查看,内容是:
v2
v3
v1
所以是从头复制,即主机里面有啥,就全部复制出来。不会只单独复制一个v3
主机故障:
这就是上面我们配置的方式,默认情况下,上面这种一主两从的方式,若主机故障后:
- 首先主机挂掉,从机依然是主机,不会上位变成主机
- 然后将主机恢复重启,主机又回来了后,主机新增记录,从机依然可以顺利复制,进行数据的读取操作
- 但是这种方式若主机故障后,不能出现新的主机
有两种方式在主机宕机以后可以产生新的主机:
- slaveof no one 将从机变为主机
- 哨兵模式自动选举主机
4.2、薪火相传:层层链路结构
上一个Slave可以是下一个Slave的Master,Slave同样可以接收其他slaves的连接和同步请求,那么该slave作为了链条中下一个的master,可以有效减轻master的写压力,去中心化降低风险。
但是这种模式有缺点:
- 一旦中间某个slave从机宕机,后面的slave都没法备份工作了。
- 如果中途变更转向:会清除之前的数据,重新建立拷贝最新的
- 主机挂了,从机还是从机,但是无法写数据了
演示:让6380这台从服务器作为6381的主服务器:
6380:
//slaveof <ip><port>
slaveof 127.0.0.1 6381
这样配置以后,我们的6380既是主机也是从机,在主机6379没有挂掉之前,查看6380的信息,依然还是从机,但是当6379挂掉以后,它就变成主机了
4.3、反客为主
当一个master宕机【shutdown】后,后面的slave可以立刻升为master,其后面的slave不用做任何修改。用 slaveof no one 将从机变为主机。
如果主机断开了连接,从机手动执行命令slaveof no one,这样执行以后使自己成为主机,然后其他从机就可以手动连接到新主机,就算老大主节点重新修复了,也是光杆司令,要重新配置。如图:这种方式是我们手动完成的,不能自动完成,假如我们服务器凌晨挂掉,手动就不可能了啊
所以就用到下面的哨兵模式的自动选举模式
5、哨兵(Sentinel)模式
参考:www.jianshu.com/p/06ab9daf9…
主从模式的弊端就是不具备高可用性,当master挂掉以后,Redis将不能再对外提供写入操作,因此sentinel应运而生。sentinel中文含义为哨兵,顾名思义,它的作用就是监控redis集群的运行状况,特点如下:
sentinel模式是建立在主从模式的基础上,如果只有一个Redis节点,sentinel就没有任何意义- 当
master挂了以后,sentinel会在slave中选择一个做为master,并修改它们的配置文件,其他slave的配置文件也会被修改,比如slaveof属性会指向新的master - 当
master重新启动后,它将不再是master而是做为slave接收新的master的同步数据 sentinel因为也是一个进程有挂掉的可能,所以sentinel也会启动多个形成一个sentinel集群- 多
sentinel配置的时候,sentinel之间也会自动监控 - 当主从模式配置密码时,
sentinel也会同步将配置信息修改到配置文件中,不需要担心 - 一个
sentinel或sentinel集群可以管理多个主从Redis,多个sentinel也可以监控同一个redis sentinel最好不要和Redis部署在同一台机器,不然Redis的服务器挂了以后,sentinel也挂了
5.1、概述
主从切换的方法是,当master服务器宕机后,需要人工切换,费事,更多时候选择优先考虑是哨兵模式,redis2.8 开始正确提供sentinel(哨兵)谋朝篡位自动版来解决这个问题
哨兵能够监控后台的主机是否故障,如果出现故障,根据投票数自动将从库转换为主库,从而自动进行故障恢复
哨兵模式是一种特殊模式,首先Redis提供了哨兵命令。哨兵是一个独立的进程,作为进程,它会独立运行,原理是哨兵通过发请求命令,等待redis服务器响应,从而监控多个redis实例
基本模型
单机单个哨兵模式
这里的哨兵有两个作用
- 通过发送命令给Redis服务器,让Redis服务器返回监控其运行状态,包括主服务器和从服务器。
- 当哨兵监测到master宕机,会自动根据投票机制将选举出的slave切换成master,然后通过发布订阅模式通知其他的从服务器,修改配置文件,让它们切换主机。
然而一个哨兵进程对Redis服务器进行监控,可能会出现问题,为此,我们可以使用多个哨兵进行监控。各个哨兵之间还会进行监控,这样就形成了多哨兵模式
多哨兵模式
用文字描述一下故障切换(failover)的过程。假设主服务器宕机,哨兵1先检测到这个结果,系统并不会马上进行failover(选举)过程,仅仅是哨兵1主观的认为主服务器不可用,这个现象成为主观下线。当后面的哨兵也检测到主服务器不可用,并且数量达到一定值时,那么哨兵之间就会进行一次投票,投票的结果由一个哨兵发起,进行failover操作。切换成功后,就会通过发布订阅模式,让各个哨兵把自己监控的从服务器实现切换主机,这个过程称为客观下线。这样对于客户端而言,一切都是透明的。
5.2、Redis配置哨兵模式
配置3个哨兵和1主2从的Redis服务器来演示这个过程。
1、首先配置Redis的主从服务器,修改redis.conf文件如下,和以前主从复制一样配置,下面只是一个参考
# 使得Redis服务器可以跨网络访问
bind 0.0.0.0
# 设置密码
requirepass "123456"
# 指定主服务器,注意:有关slaveof的配置只是配置从服务器,主服务器不需要配置
slaveof 192.168.11.128 6379
# 主服务器密码,注意:有关slaveof的配置只是配置从服务器,主服务器不需要配置
masterauth 123456
上述内容主要是配置Redis服务器,从服务器比主服务器多一个slaveof的配置和密码。
2、在测试机器上配置3个哨兵,每个哨兵的配置都是一样的。 去 redis 配置文件所在目录,创建一个哨兵配置文件sentinel.conf, 名字绝不能错,内容如下:
# 哨兵 监视 【名称】 监视的主机地址 端口 ;
# 最后一位 1 : 当一个哨兵主观认为主机断开,就可以客观认为主机故障,然后开始选举failover新的主机
sentinel monitor mysentinel 127.0.0.1 6379 1 #这里只监控主机
这只是哨兵模式最基本的配置,还有其他很多,如下:
3、在测试机器上启动哨兵:在 Redis 启动目录有一个哨兵文件 sentinel.sentinel
启动命令:
redis-sentinel sentinel.conf
可以看到,这个时候监控的主机6379有一票,所以此时他是主机
5.3、宕掉主机,观察情况
shutdown主机6379,观察两台从机的INFO,显示依然是从机,等待大概10秒左右可以在测试终端上看到哨兵窗口日志
这就说明原主机shutdwn后作为从机,并且切换(switch)了6381作为新的主机,并且再次观察两台从机的INFO,显示6381已经变成了新主机,并且主机重启后只能归并到新的主机下,还是作为从机,这就是哨兵模式的选举机制,即如果主节点断开,就会从从机中根据投票算法(采用加权轮询算法)选举出一个主机,
那么哪个从机会被选举为主机呢?根据优先级别:slave-priority
#设置从机的优先级,值越小,优先级越高,用于选举主机时使用。默认100
slave-priority 10
5.4、哨兵模式的优缺点
优点:
- 基于集群、基于主从复制,所有主从配置的优点都有
- 主从可以切换,故障可以切换,系统的可用性提高
- 哨兵模式就是主从模式的升级,手动到自动,更加健壮
缺点:
- Redis不好在线扩容,集群容量一旦达到上限,在线扩容就十分麻烦
- 实现哨兵模式的配置其实是很麻烦的,里面有很多配置项
- 多哨兵,多端口配置复杂,一般也由运维来配置
5.5、完整的哨兵模式配置文件 sentinel.conf
# Example sentinel.conf
# 哨兵sentinel实例运行的端口 默认26379
port 26379
# 哨兵sentinel的工作目录
dir /tmp
# 哨兵sentinel监控的redis主节点的 ip port
# master-name 可以自己命名的主节点名字 只能由字母A-z、数字0-9 、这三个字符".-_"组成。
# quorum 当这些quorum个数sentinel哨兵认为master主节点失联 那么这时 客观上认为主节点失联了
# sentinel monitor <master-name> <ip> <redis-port> <quorum>
sentinel monitor mymaster 127.0.0.1 6379 1
# 当在Redis实例中开启了requirepass foobared 授权密码 这样所有连接Redis实例的客户端都要提供密码
# 设置哨兵sentinel 连接主从的密码 注意必须为主从设置一样的验证密码
# sentinel auth-pass <master-name> <password>
sentinel auth-pass mymaster MySUPER--secret-0123passw0rd
# 指定多少毫秒之后 主节点没有应答哨兵sentinel 此时 哨兵主观上认为主节点下线 默认30秒
# sentinel down-after-milliseconds <master-name> <milliseconds>
sentinel down-after-milliseconds mymaster 30000
# 这个配置项指定了在发生failover主备切换时最多可以有多少个slave同时对新的master进行 同步,
这个数字越小,完成failover所需的时间就越长,
但是如果这个数字越大,就意味着越 多的slave因为replication而不可用。
可以通过将这个值设为 1 来保证每次只有一个slave 处于不能处理命令请求的状态。
# sentinel parallel-syncs <master-name> <numslaves>
sentinel parallel-syncs mymaster 1
# 故障转移的超时时间 failover-timeout 可以用在以下这些方面:
#1. 同一个sentinel对同一个master两次failover之间的间隔时间。
#2. 当一个slave从一个错误的master那里同步数据开始计算时间。直到slave被纠正为向正确的master那里同步数据时。
#3.当想要取消一个正在进行的failover所需要的时间。
#4.当进行failover时,配置所有slaves指向新的master所需的最大时间。不过,即使过了这个超时,slaves依然会被正确配置为指向master,但是就不按parallel-syncs所配置的规则来了
# 默认三分钟
# sentinel failover-timeout <master-name> <milliseconds>
sentinel failover-timeout mymaster 180000
# SCRIPTS EXECUTION
#配置当某一事件发生时所需要执行的脚本,可以通过脚本来通知管理员,例如当系统运行不正常时发邮件通知相关人员。
#对于脚本的运行结果有以下规则:
#若脚本执行后返回1,那么该脚本稍后将会被再次执行,重复次数目前默认为10
#若脚本执行后返回2,或者比2更高的一个返回值,脚本将不会重复执行。
#如果脚本在执行过程中由于收到系统中断信号被终止了,则同返回值为1时的行为相同。
#一个脚本的最大执行时间为60s,如果超过这个时间,脚本将会被一个SIGKILL信号终止,之后重新执行。
#通知型脚本:当sentinel有任何警告级别的事件发生时(比如说redis实例的主观失效和客观失效等等),将会去调用这个脚本,
#这时这个脚本应该通过邮件,SMS等方式去通知系统管理员关于系统不正常运行的信息。调用该脚本时,将传给脚本两个参数,
#一个是事件的类型,
#一个是事件的描述。
#如果sentinel.conf配置文件中配置了这个脚本路径,那么必须保证这个脚本存在于这个路径,并且是可执行的,否则sentinel无法正常启动成功。
#通知脚本
# sentinel notification-script <master-name> <script-path>
sentinel notification-script mymaster /var/redis/notify.sh
# 客户端重新配置主节点参数脚本
# 当一个master由于failover而发生改变时,这个脚本将会被调用,通知相关的客户端关于master地址已经发生改变的信息。
# 以下参数将会在调用脚本时传给脚本:
# <master-name> <role> <state> <from-ip> <from-port> <to-ip> <to-port>
# 目前<state>总是“failover”,
# <role>是“leader”或者“observer”中的一个。
# 参数 from-ip, from-port, to-ip, to-port是用来和旧的master和新的master(即旧的slave)通信的
# 这个脚本应该是通用的,能被多次调用,不是针对性的。
# sentinel client-reconfig-script <master-name> <script-path>
sentinel client-reconfig-script mymaster /var/redis/reconfig.sh
部分参数解释:
| 配置项 | 参数类型 | 作用 |
|---|---|---|
| port | 整数 | 启动哨兵进程端口 |
| dir | 文件夹目录 | 哨兵进程服务临时文件夹,默认为/tmp,要保证有可写入的权限 |
| sentinel down-after-milliseconds | <服务名称><毫秒数(整数)> | 指定哨兵在监控Redis服务时,当Redis服务在一个默认毫秒数内都无法回答时,单个哨兵认为的主观下线时间,默认为30000(30秒) |
| sentinel parallel-syncs | <服务名称><服务器数(整数)> | 指定可以有多少个Redis服务同步新的主机,一般而言,这个数字越小同步时间越长,而越大,则对网络资源要求越高 |
| sentinel failover-timeout | <服务名称><毫秒数(整数)> | 指定故障切换允许的毫秒数,超过这个时间,就认为故障切换失败,默认为3分钟 |
| sentinel notification-script | <服务名称><脚本路径> | 指定sentinel检测到该监控的redis实例指向的实例异常时,调用的报警脚本。该配置项可选,比较常用 |
sentinel down-after-milliseconds配置项只是一个哨兵在超过规定时间依旧没有得到响应后,会自己认为主机不可用。对于其他哨兵而言,并不是这样认为。哨兵会记录这个消息,当拥有认为主观下线的哨兵达到sentinel monitor所配置的数量时,就会发起一次投票,进行failover,此时哨兵会重写Redis的哨兵配置文件,以适应新场景的需要。
6、复制延时和故障恢复
复制延时
由于所有的写操作都是先在Master上操作,然后同步更新到Slave上,所以从Master同步到Slave机器有一定的延迟,当系统很繁忙的时候,延迟问题会更加严重,Slave机器数量的增加也会使这个问题更加严重。
故障恢复
新主登基:从下线的主服务的所有从服务里面挑选一个从服务,将其转成主服务,选择条件依次为:
- 选择优先级靠前的:优先级在
redis.conf中默认:slave-priority 100,值越小优先级越高 - 选择偏移量最大的:偏移量是指获得原主机数据最全的
- 选择runid最小的从服务:每个redis实例启动后都会随机生成一个40位的runid
群仆俯首
挑选出新的主服务之后sentinel向原主服务的从服务发送 slaveof新主服务的命令,复制新master
旧主俯首
当已下线的服务重新上线时sentinel会向其发送slaveof命令让其成为新主的从
7、java实现Redis的主从复制-哨兵模式
private static JedisSentinelPool jedisSentinelPool=null;
public static Jedis getJedisFromSentinel(){
if(jedisSentinelPool==null){
// 哨兵信息
Set<String> sentinelSet=new HashSet<>();
sentinelSet.add("192.168.11.103:26379");
JedisPoolConfig jedisPoolConfig =new JedisPoolConfig();
jedisPoolConfig.setMaxTotal(10); //最大可用连接数
jedisPoolConfig.setMaxIdle(5); //最大闲置连接数
jedisPoolConfig.setMinIdle(5); //最小闲置连接数
jedisPoolConfig.setBlockWhenExhausted(true); //连接耗尽是否等待
jedisPoolConfig.setMaxWaitMillis(2000); //等待时间
jedisPoolConfig.setTestOnBorrow(true); //取连接的时候进行一下测试 ping pong
// 创建连接池
jedisSentinelPool=new JedisSentinelPool("mymaster",sentinelSet,jedisPoolConfig);
return jedisSentinelPool.getResource();
}else{
return jedisSentinelPool.getResource();
}
}
补充:Redis集群详解