Redis 复制

Redis复制(replication)

通过持久化功能,保证Redis即使在重启的情况下也不会损失(或少量损失)数据。但是由于数据存储在一台服务器上,如果这台服务器出现磁盘故障等问题,也会导致数据丢失。 为了避免单点故障,Redis提供了复制(replication)功能,可以实现当一台数据库中的数据更新后,自动将数据同步到其他数据库上。

1、配置

在复制的概念中,数据库分为两类,一类是主数据库(master),另一类是从数据库(slave)。 主数据库可进行读写操作, 当读写操作导致数据变化是自动将数据同步给从数据库。而从数据库一般是可读的,只接受主数据库同步过来的数据。一个主数据库可以有多个从数据库,而一个从数据库只能有一个主数据库。
主从数据库

在Redis中使用复制功能很容易,只需要在从数据库配置文件中加入

slaveof 主数据库地址 主数据库端口

主数据库无需进行任何配置。

展示复制流程,下面实现一个最简化的复制系统。在一台服务器上启动两个redis实例,监听不同的端口,其中一个作为主数据库, 一个作为从数据库。
1、不加任何参数实现一个redis实例作为主数据库:默认监听6379端口

vagrant@homestead:~$ redis-server

2、加上slaveof参数启动另一个redis实例作为从数据库,并让监听6380端口,

vagrant@homestead:~$ redis-server --port 6380 --slaveof 127.0.0.1 6379
9457:C 14 Aug 2020 09:05:12.201 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
9457:C 14 Aug 2020 09:05:12.201 # Redis version=5.0.8, bits=64, commit=00000000, modified=0, pid=9457, just started
9457:C 14 Aug 2020 09:05:12.201 # Configuration loaded
9457:S 14 Aug 2020 09:05:12.203 * Increased maximum number of open files to 10032 (it was originally set to 1024).
...

3、打开redis-cli实例A并链接到主数据库,

vagrant@homestead:~$ redis-cli -p 6379

4、打开redis-cli实例B并链接到从数据库

vagrant@homestead:~$ redis-cli -p 6380
127.0.0.1:6380> info replication
# Replication
role:slave
master_host:127.0.0.1
master_port:6379
master_link_status:up
master_last_io_seconds_ago:7
master_sync_in_progress:0
slave_repl_offset:70
slave_priority:100
...

5、在实例A(主数据库)中,使用set命令设置一个值

127.0.0.1:6379> set foo bar
OK

此时在实例B(从数据库)中就可以获得该值了

127.0.0.1:6380> get foo
"bar"

6、默认情况下从数据是只读的

127.0.0.1:6380> set foo hi
(error) READONLY You can't write against a read only replica.

可以通过设置从数据库的配置文件的slave-read-onlyno 可以使从数据库可写,以免导致被忽略的潜在应用逻辑
7、除了通过配置文件或者命令行参数设置slaveof参数,还可以使用slaveof命令修改

127.0.0.1:6380> slaveof 127.0.0.1 6379

如果该数据已经是其他主数据库的从数据库,slaveof命令会停止和原来的数据库的同步,而和新主数据库同步。 此外对从数据库,还可以使用slaveof no one 命令来使当前数据库停止接受其他数据库的同步,并转换成主数据库。

2、复制的原理

1、当一个数据库启动后,会向主数据库发送sync命令
2、当主数据库接收到sync命令后,开始在后台保存快照(RDB持久化过程),并将快照期间接收到的命令缓存起来。 当快照完成后,Redis会将快照文件和所有的缓存命令发送给从数据库。
3、从数据库收到后,会载入快照文件并执行接受到的缓存命令。以上过程称为复制初始化
4、复制初始化后,主数据库每当收到写命令就会将命令同步给从数据库,从而保证主从数据库数据一致。
5、当主从数据库之间断开重连后, Redis2.6及以前的版本会重新进行复制初始化,即使从数据库仅有几条命令没有收到,主数据库也必须将数据库的所有数据重新传送给从数据库,这使得主从数据库断线重连后数据回复的效率很低。Redis2.8版重大改进是断线重连能够支持有条件的增量数据传输,当从数据重新连上主数据库后,主数据库只需要将断线期间的执行的命令发送给从数据库,大大提高了Redis复制的实用性。

3、从协议的角度介绍复制初始化的过程

1、从数据库发送ping命令确认主数据库是否可以链接
2、主数据库回复pong命令,如果没有收到主数据库的回复,则向用户提示错误。如果主数据需要密码才能链接, 我们还要发送auth命令进行验证
3、然后向主数据库发送replconf命令说明自己的端口号,这时可以开始同步过程了: 向主数据库发送sync命令开始同步,此时主数据库发送会快照文件和缓存命令
4、从数据库接收到的内容写入到磁盘的零时文件中,当写入完后,从数据库会用临时文件替换旧的RDB快照文件,之后的操作就和RDB持久化时启动恢复一样了。
5、需要注意的是,在同步的过程中从数据库并不会阻塞,可以继续处理客户端发来的命令。
6、复制初始化阶段结束后,主数据库执行的任何命令会导致数据变化的命令都会异步的传送给从数据库,这个过程称为复制同步阶段,复制同步阶段会贯穿整个主从同步的过程的始终,直到主从关系终止为主。

在复制过程中,快照无论在主从数据库还是从数据库都起了很大作用,只要只从复制,就会进行快照,即使我们关闭了RDB方式的持久化(通过删除所有的save参数)。Redis2.8.18后支持无硬盘复制。

4、乐观复制

Redis采用乐观复制的复制策略,容忍在一定时间内主从数据的内容是不同的,但是两者的数据最终是同步的。
1、具体来说,Redis主从复制的过程是异步的,这意味着主数据库执行完客户端请求后会立即将命令在主数据库的执行结果返回给客户端,并且异步的将命令同步给从数据库,而不会等待从数据库接收到该命令后在返回给客户端,这一特性保证了启用复制后主数据库的性能不会收到影响,从另一方面也会产生一个主从数据不一致的时间窗口。
2、当主数据执行一条写命令后,主数据库的数据已经发生变动,然而在主数据库将命令传送给从数据库之前,如果两个数据库之间的网络断开了,此时二者之间的数据就会不一致。
3、从这个角度来说,主数据库是无法得知某个命令最终同步给了多少个从数据库, 但是Redis提供了两个配置选项来限制,只有当数据至少同步给指定数量的从数据库时,主数据库才是可写的:

min-slaves-to-write 3
min-slaves-max-lag 10

min-slaves-to-write命令表示只有当至少3个从数据库连接到主数据时,主数据库才是可写的
min-slaves-max-lag命令表示允许从数据库最长失去连接时间,如果从数据库与主数据库联系(即发送replconf ack命令)的时间小于这个值,则认为主数据库和从数据库还在保持连接。这一特性默认是关闭的,在分布式系统中,打开并合理的配置该选项后可以降低主从架构中因为网络分区导致的数据不一致问题。

5、读写分离的一致性

  • 从数据库的持久化
    为了提高性能,可以通过复制功能建立一个(或若干个)从数据库,并在从数据库中开启持久化,同时禁用主数据库的持久化,当从数据库崩溃重启后主数据库会自动的将数据同步过来,所以无需担心数据丢失。
    然而当主数据库崩溃时,手工把从数据库的数据复制到主数据库时,需严格按照一下步骤进行
    1)、在从数据中使用slaveof no one 命令将从数据库提升至主数据库继续服务
    2)、启动之前崩溃的主数据库,然后使用slaveof命令将其设置成新的主数据库的从数据,即可将数据同步过来
    无论哪种情况,手工维护从数据或主数据的重启及数据恢复都相对麻烦,Redis提供了自动化哨兵方案来实现这一过程。
  • 无硬盘复制
    Redis2.8.18版本开始,Redis引入了无硬盘复制,开启该选项后,Redis在与从数据库进行复制初始化时不会将快照内容存储到硬盘上,而是直接通过网络发送给从数据库,避免了硬盘性能的瓶颈。目前该功能还在试验阶段,可在配置文件中开启该功能:repl-diskless-sync yes

6、增量复制

增量复制是基于一下3点来实现的:
1、从数据库会存储主数据库的允许ID(run id),每个Redis运行实例都会有一个唯一的运行ID,每当实例重启后,会自动生成一个新的运行ID
2、在复制同步阶段,主数据库将每一个命令发送给从数据库,都会将该命令存放在一个积压队列(backlog)中,并记录下当前积压队列中存放命令的偏移量范围。
3、从数据库接受到主数据库传来的命令时,会记录下该命令的偏移量。
这三点是实现增量复制的基础。

增量复制的通信流程

当主从连接准备就绪后,从数据库会发送sync命令告诉主数据可以把所有数据同步过来了。在Redis2.8版本后,不在发送sync命令,取而代之的是psync,格式

psync 主数据库的运行ID 断开前的最新偏移量

主数据库收到psync命令后,会执行一下判断来决定此次重连是否可以进行增量复制
1)、主数据库的会判断从数据的运行ID是否和自己的运行ID是否相同。这一步骤是确保从数据库之前确实是和自己同步的,避免从数据库拿到错误的数据
2)、判断从数据库最后同步成功的命令偏移量是否在积压队列中,如果在执行增量复制,并将积压队列的相应命令发送给从数据库。
如果此次重连不满足增量复制的条件,主数据库会进行一次全部同步
积压队列本质上是一个固定长度的循环队列,默认情况下积压队列的大小是1MB,可以通过配置文件的repl-backlog-size选项来调整,积压队列越大,允许主从数据断开的时间就越长。与积压队列配置相关的另一个选项是repl-backlog-ttl,即当所有从数据库与主数据库断开连接后,经过多长时间释放积压队列的内存空间。默认时间是1个小时。

posted @ 2020-08-14 19:17  phper-liunian  阅读(124)  评论(0编辑  收藏  举报