Redis系列:redis主从复制(七)

序言

  • 主从复制简单来说就是把redis_a服务器上的数据复制到redis_b,redis_c,redis_d等等服务器上,前者redis_a称为主节点(master),后 者redis_b,redis_c…等被称为从节点(slave)。master以写为主,slave以读为主。(注:数据的复制是单方向的只能从主节点到从节点,并且一个主节点可以有多个从节点,但是一个从节点只能有一个主节点)
  • 为什么要用主从复制:
    (1)数据冗余:主从复制实现了数据的热备份,是持久化之外的一种数据冗余方式
    (2)故障恢复:当主节点出现问题,可以由从节点提供服务,实现了故障的快速恢复
    (3)负载均衡:再主从复制的基础上,配合读写分离,可以由主节点提供写服务,由从节点提供读服务(在写服务的时候应用连接主节点,在读服务的时候应用连接从节点),平均服务器的负载。特别是在写少读多的时候,可以通过多个从节点进行分担读负载,可以大大提高redis服务器的并发量。
    (4)高可用(集群)的基石:主从复制还是哨兵和集群能够实施的基础。
  • 为什么在企业工程项目中不能仅仅使用一台redis服务器(宕机==gg)?
    (1)从结构上来看:单个redis服务器会发生单点故障,并且一台服务器需要处理所有请求负载,压力大
    (2)从容量上来看:单个redis服务器内存容量有限(一般单台服务器redis使用内存不应该超过20G)

主从复制环境搭建

由于服务器数量的限制,在这里我就在一台服务器上启动多个redis实例实现主从复制
搭建的环境==>主:127.0.0.1:6379 ,从(1): 10.1.210.69:6380 ,从(2):10.1.210.69:6381
搭建步骤(主要分为4步):
(1)将redis.conf文件拷贝三份,分别使用如下名字:redis6379.conf、 redis6380.conf、 redis6381.conf:

[root@izbp19p16mcz7awt4d8kahz twyconfig]# ls
redis.conf
[root@izbp19p16mcz7awt4d8kahz twyconfig]# cp redis.conf redis6379.conf
[root@izbp19p16mcz7awt4d8kahz twyconfig]# cp redis.conf redis6380.conf
[root@izbp19p16mcz7awt4d8kahz twyconfig]# cp redis.conf redis6381.conf
[root@izbp19p16mcz7awt4d8kahz twyconfig]# ls
redis6379.conf  redis6380.conf  redis6381.conf  redis.conf
[root@izbp19p16mcz7awt4d8kahz twyconfig]# rm -rf redis.conf
[root@izbp19p16mcz7awt4d8kahz twyconfig]# ls
redis6379.conf  redis6380.conf  redis6381.conf

(2)修改配置文件:ip(默认127.0.0.1可以不用修改)、端口(尽量和配置文件一致)、pid文件(pid名称),日志文件(log文件名字),持 久化数据目录(dir – dump.db)、后台运行(daemonize yes):
redis6379.conf:

daemonize yes   #修改redis为后台运行模式
pidfile "/var/run/redis_6379.pid"   #修改运行的redis实例的pid,不能重复
logfile "/usr/local/bin/log/redis_6379.log"   #指明日志文件
dbfilename "dump_6379.db"     #存放持久化数据文件
dir "/usr/local/bin"   #存放持久化数据的目录(看需求修改)
bind 127.0.0.1     #监听地址,如果是单机多个示例可以不用修改,如果是多个服务器修改为对应服务器地址ip
port 6379          #监听端口,保持和配置文件名称端口一致

redis6380.conf:

daemonize yes   #修改redis为后台运行模式
pidfile "/var/run/redis_6380.pid"   #修改运行的redis实例的pid,不能重复
logfile "/usr/local/bin/log/redis_6380.log"   #指明日志文件
dbfilename "dump_6380.db"     #存放持久化数据文件
dir "/usr/local/bin"   #存放持久化数据的目录(看需求修改)
bind 127.0.0.1     #监听地址,如果是单机多个示例可以不用修改,如果是多个服务器修改为对应服务器地址ip
port 6380       #监听端口,保持和配置文件名称端口一致

redis6381.conf:

daemonize yes   #修改redis为后台运行模式
pidfile "/var/run/redis_6381.pid"   #修改运行的redis实例的pid,不能重复
logfile "/usr/local/bin/log/redis_6381.log"   #指明日志文件
dbfilename "dump_6381.db"     #存放持久化数据文件
dir "/usr/local/bin"   #存放持久化数据的目录(看需求修改)
bind 127.0.0.1     #监听地址,如果是单机多个示例可以不用修改,如果是多个服务器修改为对应服务器地址ip
port 6381      #监听端口,保持和配置文件名称端口一致

(3)启动这三个redis实例:
Redis系列:redis主从复制(七)_第1张图片
(4)设置主从关系,当然你可以直接指明从库配置文件直接使用slaveof 指定,这里我在用客户端修改,分别使用客户端redis-cli命令连入端口为6380、6381的redis:

  • 连入6380数据库,使用redis-cli -h 127.0.0.1 -p 6380,其中-h代表ip地址,-p代表端口,并执行slaveof 127.0.0.1 6379,并写入配置文件config rewrite,如下:
    Redis系列:redis主从复制(七)_第2张图片
  • 连入6381数据库,同上操作:
    Redis系列:redis主从复制(七)_第3张图片
  • 测试主从复制(在主redis种存数据,在从读取数据):
    [root@izbp19p16mcz7awt4d8kahz bin]# redis-cli -p 6379
    127.0.0.1:6379> set name savein6379
    OK
    127.0.0.1:6379> 
    [root@izbp19p16mcz7awt4d8kahz bin]# redis-cli -p 6381
    127.0.0.1:6381> get name
    "savein6379"
    

原理实现

主从模式是最简单的实现高可用的方案,核心就是主从同步。主从同步的原理如下:
Redis系列:redis主从复制(七)_第4张图片

1)slave发送psync命令到master;
(redis2.8版本之后已经使用psync来替代sync了,原因是sync命令生成全量的RDB文件非常消耗系统资源,而psync的效率更高。在redis4.0后优化了psync,并实现即使redis实例重启的情况下也能实现部分同步)2)master收到psync之后,执行bgsave,生成RDB全量文件
(3)master把slave的写命令记录到缓存
(4)bgsave执行完毕之后,发送RDB文件到slave,slave执行
(5)master发送缓存中的写命令到slave,slave执行

关于sync --> psync1(2.8版本后)--> psync2(4.0版本后)

此部分内容关于redis主从复制流程的优化
sync:在redis2.6以及以前的版本,复制采用sync命令,从库向主库发送sync命令,主库收到sync命令后执行bgsave后台保存RDB文件,同时master把slave的写命令记录到缓存。当快照完成以后,主库将快照文件已经缓存的所有命令发送给从库,从库接受到快照文件并载入,再将执行主库发送的命令,也就是上面我们介绍的复制初始化阶段和数据同步阶段,其后就是命令增量同步,最终主库与从库保持数据一致。
当从库在某些情况断线重连(如从库重启、由于网络原因主从连接超时),重复上述过程,进行数据同步。由此可见,redis2.6版本以及2.6以前复制过程全部采用全量复制。sync虽然解决了数据同步问题,但是在数据量比较大情况下,从库断线从来依然采用全量复制机制,无论是从数据恢复、宽带占用来说,sync所带来的问题还是很多的。于是redis从2.8开始,引入新的命令psync。
psync1:在redis2.8版本,redis引入psync命令来进行主从的数据同步,这里我们称该命令为psync1。psync1实现依赖以下三个关键点:
(1)offset(复制偏移量):
Redis系列:redis主从复制(七)_第5张图片Redis系列:redis主从复制(七)_第6张图片
上面两张图我们可以知道,主库和从库分别各自维护一个复制偏移量(可以使用info replication查看),用于标识自己复制的情况,在主库中代表主节点向从节点传递的字节数,在从库中代表从库同步的字节数。每当主库向从节点发送N个字节数据时,主节点的offset增加N,从库每收到主节点传来的N个字节数据时,从库的offset增加N。因此offset总是不断增大,这也是判断主从数据是否同步的标志,若主从的offset相同则表示数据同步量,不通则表示数据不同步。
(2)replication backlog buffer(复制积压缓冲区):
复制积压缓冲区是一个固定长度的FIFO队列,大小由配置参数repl-backlog-size指定,默认大小1MB。需要注意的是该缓冲区由master维护并且有且只有一个,所有slave共享此缓冲区,其作用在于备份最近主库发送给从库的数据。在主从命令传播阶段,主节点除了将写命令发送给从节点外,还会发送一份到复制积压缓冲区,作为写命令的备份。除了存储最近的写命令,复制积压缓冲区中还存储了每个字节相应的复制偏移量,由于复制积压缓冲区固定大小先进先出的队列,所以它总是保存的是最近redis执行的命令。
(3)run_id(服务器运行的唯一ID) :
每个redis实例在启动时候,都会随机生成一个长度为40的唯一字符串来标识当前运行的redis节点,查看此id可通过命令info server查看。当主从复制在初次复制时,主节点将自己的runid发送给从节点,从节点将这个runid保存起来,当断线重连时,从节点会将这个runid发送给主节点。主节点根据runid判断能否进行部分复制:

[1]如果从节点保存的runid与主节点现在的runid相同,说明主从节点之前同步过,主节点会更具offset偏移量之后的数据判断是否执行部分复制,如果offset偏移量之后的数据仍然都在复制积压缓冲区里,则执行部分复制,否则执行全量复制;
[2]如果从节点保存的runid与主节点现在的runid不同,说明从节点在断线前同步的redis节点并不是当前的主节点,只能进行全量复制;

psync2:redis4.0新版本除了增加混合持久化,还优化了psync(以下称psync2)并实现即使redis实例重启的情况下也能实现部分同步,下面主要介绍psync2实现过程。psync2在psync1基础上新增两个复制id(可使用info replication 查看如下图):
Redis系列:redis主从复制(七)_第7张图片
(1)master_replid: 复制id1,一个长度为41个字节(40个随机串+’0’)的字符串,每个redis实例都有,和runid没有直接关联,但和runid生成规则相同。当实例变为从实例后,自己的replid1会被主实例的replid1覆盖
(2)master_replid2:复制id2,默认初始化为全0,用于存储上次主实例的replid1

主从配置基本参数

#主库
repl-disable-tcp-nodelay no
#在slave和master同步后(发送psync/sync),后续的同步是否设置成TCP_NODELAY假如设置成yes,则redis会合并小的TCP包从而节省带宽,但会增加同步延迟(40ms),造成master与slave数据不一致假如设置成no,则redis master会立即发送同步数据,没有延迟
#前者关注性能,后者关注一致性

repl-ping-slave-period 10
#从库会按照一个时间间隔向主库发送PING命令来判断主服务器是否在线,默认是10秒

repl-backlog-size 1mb
#复制积压缓冲区大小设置

repl-backlog-ttl 3600
#master没有slave一段时间会释放复制缓冲区的内存,repl-backlog-ttl用来设置该时间长度。单位为秒。

min-slaves-to-write 3
min-slaves-max-lag 10
#设置某个时间断内,如果从库数量小于该某个值则不允许主机进行写操作,以上参数表示10秒内如果主库的从节点小于3个,则主库不接受写请求,min-slaves-to-write 0代表关闭此功能

#从库
slaveof <masterip> <masterport> 
#设置该数据库为其他数据库的从数据库

masterauth <master-password>
#主从复制中,设置连接master服务器的密码(前提master启用了认证)

slave-serve-stale-data yes
# 当从库同主库失去连接或者复制正在进行,从库有两种运行方式:
# 1) 如果slave-serve-stale-data设置为yes(默认设置),从库会继续相应客户端的请求
# 2) 如果slave-serve-stale-data设置为no,除了INFO和SLAVOF命令之外的任何请求都会返回一个错误"SYNC with master in progress"

slave-priority 100
#当主库发生宕机时候,哨兵会选择优先级最高的一个称为主库,从库优先级配置默认100,数值越小优先级越高

slave-read-only yes
#从节点是否只读;默认yes只读,为了保持数据一致性,应保持默认

你可能感兴趣的:(redis,redis)