Redis主从复制
为什么使用主从复制
数据冗余
上一篇中,Redis数据的持久化保证了Redis数据不至于因为意外关闭而导致数据丢失,但是假如Redis服务器因为硬盘损坏而导致的数据丢失。使用上面的备份可能依然会出现数据因为损坏而无法恢复的情况,所以这个时候我们需要为Redis在其他服务器上提供备份。
服务冗余
当主服务出现问题无法启动的时候可以启动从服务,实现快速故障恢复
读写分离
随着数据访问的增加,单一节点负责数据的读写会给单一服务带来较大的压力。所以这个时候我们可以主节点负责写服务,从服务负责读服务,分担服务器负载。
高可用
主从复制,是实现哨兵和集群的基础。
Redis集群原理
一般来说我们针对一个主服务(master)配置两个从服务(slave),当然这个数量不是固定了,你可以做一主一从、一主多从。这样在主服务宕机的时候其他两个从服务依旧可以维护数据的完整性,并且尝试继续提供服务。
主从配置
主从复制提供三种配置的方式
配置文件
在从服务器的配置文件redis.conf
中加入:slaveof
# 指定主服务为本机的 6379端口
slaveof 127.0.0.1 6379
启动命令
从服务器redis-server启动命令后加入 --slaveof
redis-server --port 6380 --slaveof 127.0.0.1 6379
客户端命令
Redis服务器启动后,直接通过客户端执行命令:slaveof ,则该Redis实例成为从服务。
redis> SLAVEOF 127.0.0.1 6379
上述3种方式是等效的。
主从复制过程
主从复制的建立过程主要分为:建立连接、数据同步、复制命令。
建立连接
加载主节点
获得从服务内部维持的两个数据:masterhost:主服务IPmasterport:主服务端口
建立socket连接
从服务每秒1此调用定时函数replicationCron(),如果主服务可用,则创建socket连接
复制准备
从服务,建立一个处理复制工作(接收RDB,接收命令)的事件处理器,复制处理工作。
主服务,接收到socket连接后,为该socket创建客户端状态,并将从服务看做是连接到主节点的一个客户端。
发送命令
主服务完成连接后,从服务发送ping命令检查连接是否可用。
- 返回pong,说明socket连接正常
- 一定时间后从服务仍未收到主服务的回复,说明socket连接不可用,进行重连
- 其他内容说明主服务当前无法处理命令,则从服务断开socket连接,并重连
验证身份
从服务进行身份验证是通过向主节点发送auth命令进行的,auth命令的参数即为配置文件中的masterauth的值。验证通过则继续,不通过则断开连接重连。
保存从服务信息
完成身份验证后,从服务发送其监听信息至主节点,主节点将信息保存对应客户端的slave_listening_port字段下。
数据同步阶段
连接完成后,主从服务开始进行数据同步。从服务向主服务发送psync命令,开始进行同步。此时根据主从服务当前状态不同,复制被分为全量复制和部分复制
全量复制
用于初次复制或其他无法进行部分复制的情况,将主节点中的所有数据都发送给从服务。
全量复制的流程:
- 主从服务建立连接后,主从服务之间无法完成部分复制的认证的时候,从服务向主节点发起全量复制请求
- 主节点收到全量复制的命令后开始备份数据生成RDB文件,并将此后的命令写入缓存区中
- 主服务将RDB文件传递给从服务,从服务清除旧数据。开始恢复数据
- 主服务把缓存的命令发送给从服务,从服务执行命令,数据更新至最新状态
- 后续主服务收到的写命令都会发送给slave
复制流程
部分复制
针对新连接的从服务,执行全量复制没有什么问题,但是针对意外宕机仅仅部分数据不同的从服务使用全量复制就显得很是笨重了。
由于全量复制在主节点数据量较大时效率太低,因此Redis2.8开始提供部分复制,用于处理网络中断时的数据同步。来确定一个从服务回复数据的位置需要三个关键数据:主服务ID、数据偏移量、复制缓冲区
- 数据偏移量
主服务会维护一个数据偏移量(offset)标识的为主服务向从服务传递的字节数;同时从服务也维护一个数据偏移量标识从主服务接收到的字节数。进行数据复制的时候,主节点可以根据这个偏移量判断数据库是否一致,如果主从服务偏移量不一致,可以根据两个offset找出从服务缺少的那部分数据。那么只需要复制部分内容就可以了。
- 复制缓冲区
主服务维护了一个复制缓冲区,其主要备份了最近从主节点发送给从服务的数据。此区域固定长度,且实现了先进先出的策略,时间较早的命令会被挤出缓冲区。主从服务在比对偏移量之后,假如缺失的数据都在缓冲区中,则执行部分复制。如果缺失的数据已经不再缓冲区,则进行全量复制。
- 主服务ID
每个Redis节点,启动的时候会生成一个随机ID(runid),通过这个ID来标识一个Redis节点。进行初次复制的时候,主服务会将自己的runid发送给从服务,从服务会保存此runid。当断线重连的时候,从服务将此runid发送给主服务,主服务判断和自己的runid是否一致。假如不一致则进行全量复制。假如一致主服务会进行其他部分复制条件的判断。
复制流程
命令复制
完成数据同步之后,主从服务进行命令复制节点,这个阶段主服务主要将自己执行的写命令发送给从服务,从服务接收命令后执行进行执行,保证数据一致性。
数据延迟
命令复制阶段是异步操作的,当主服务发送命令之后并不会等从服务的回复,因此主从服务也不是完全的数据一致。数据不一致的程度,取决于主从服务之间的网络、写命令的执行频率,以及repl-disable-tcp-nodelay的配置。Redis提供了一个参数repl-disable-tcp-nodelay此参数用来配置主服务和从服务的数据同步方式,默认为no。将此配置设置为yes,则TCP会对包进行合并从而减少带宽,这样会降低发送频率,导致数据同步延迟增加。大多数情况使用的都是默认值,此时TCP会立马将主服务的数据发送给从服务,带宽增加但延迟变小。
单机搭建主从服务
现在我们在一台电脑上,通过监听不同的端口来搭建一主一从的配置。
修改绑定端口
将两个Redis监听不同的端口,然后启动。其中主服务不需要修改,从服务端口修改为6380
从服务的redis.conf文件中修改port值
# Accept connections on the specified port, default is 6379 (IANA #815344).
# If port 0 is specified Redis will not listen on a TCP socket.
port 6380
在配置中指定主服务
从服务的redis.conf文件中添加slaveof
# 指定主服务为本机的 6379端口
slaveof 127.0.0.1 6379
注意假如主服务设置了密码,需要打开masterauth注释,配置主服务的密码
# If the master is password protected (using the "requirepass" configuration
# directive below) it is possible to tell the replica to authenticate before
# starting the replication synchronization process, otherwise the master will
# refuse the replica request.
#
masterauth ******
插入数据并验证从服务
分别启动主服务(6379)和从服务(6380)。此时在主服务上输入CLIENT LIST
命令可以看到连接的服务。
而在身份验证步骤之后,从服务器将执行命令REPLCONF,所以通过cmd=replconf
可以确定其中一个为从服务。
127.0.0.1:6379> CLIENT LIST
id=131 addr=127.0.0.1:33722 fd=7 name= age=1437 idle=0 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=26 qbuf-free=32742 obl=0 oll=0 omem=0 events=r cmd=client
id=1232 addr=127.0.0.1:38186 fd=8 name= age=315 idle=0 flags=S db=0 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=0 obl=0 oll=0 omem=0 events=r cmd=replconf
127.0.0.1:6379>
目前两个数据都是空的,此时向主服务添加key,然后查询从服务会获取此内容
127.0.0.1:6379> keys *
(empty list or set)
127.0.0.1:6379> set a 1
OK
127.0.0.1:6379>
127.0.0.1:6380> keys *
(empty list or set)
127.0.0.1:6380> keys *
1) "a"
127.0.0.1:6380>
此时作为从服务是不接受外部的写数据任务的
(error) READONLY You can't write against a read only replica.
此时一个一主一从的主从复制集群搭建完成。
来源:https://blog.csdn.net/qq330983778/article/details/100165754