Redis 主从复制

如何使用

通过持久化功能,Redis保证了即使在服务器重启的情况下也不会损失(或少量损失)数据。但是由于数据是存储在一台服务器上的,如果这台服务器出现硬盘故障等问题,也会导 致数据丢失。

为了避免单点故障,通常的做法是将数据库复制多个副本以部署在不同的服务器上,这样即使有一台服务器出现故障,其他服务器依然可以继续提供服务。

为此, Redis 提供了复制(replication)功能,可以实现当一台数据库中的数据更新后,自动将更新的数据同步到其他数据库上。

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

一个主数据库可以拥有多个从数据库,而一个从数据库只能拥有一个主数据库。

在 Redis 中使用复制功能非常容易,只需要在从数据库的配置文件中加入slaveof 主数 据库地址 主数据库端口 即可,主数据库无需进行任何配置。

例如,这里有两台服务器,主服务器A的IP为 192.168.0.106,从服务器B的IP为 192.168.7.477。

首先使用默认端口启动主服务器:

redis-server

指定端口号并加上 svlavefo 参数启动从服务器:

redis-server --port 6380 --slaveof 192.168.0.106 6379

此时在主数据库中的任何数据变化都会自动地同步到从数据库中,我们打开 redis-cli 实例A并连接到主数据库:

redis-cli -p 6379

再打开redis-cli实例B并连接到从数据库:

redis-cli -p 6380

这时我们使用INFO命令来分别在实例A和实例B中获取Replication节的相关信息:

127.0.0.1:6379> INFO replication
# Replication
role:master
connected_slaves:1
slave0:ip=192.168.7.144,port=6380,state=online,offset=85,lag=1
master_repl_offset:85
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:2
repl_backlog_histlen:84
127.0.0.1:6379>

可以看到,实例A的角色(上面输出中的role)是master,即主数据库,同时已连接的从数据库(上面输出中的connected_slaves)的个数为1。

同样在实例B中获取相应的信息为:

127.0.0.1:6380> INFO replication
# Replication
role:slave
master_host:192.168.0.106
master_port:6379
master_link_status:up
master_last_io_seconds_ago:3
master_sync_in_progress:0
slave_repl_offset:155
slave_priority:100
slave_read_only:1
replica_announced:1
connected_slaves:0
master_failover_state:no-failover
master_replid:47c2652dbfc83bc2e813a8c055b9232e64cd229b
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:155
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:2
repl_backlog_histlen:154

这里可以看到,实例 B 的 role 是 slave,即从数据库,同时其主数据库的地址为 192.168.0.106,端口为 6379。

这时,在实例B中使用SET命令设置一个键的值,会报错:

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

因为默认情况下,从数据库是只读的。

可以通过设置从数据库的配置文件中的 slave-read-only 为 no 以使从数据库可写,但是因为对从数据库的任何更改都不会同步给任何其他数据库,并且一旦主数据库中更新了对应的数据就会覆盖从数据库中的改动,所以通常的场景下不应该设置从数据库可写,以免导致易被忽略的潜在应用逻辑错误。

配置多台从数据库的方法也一样,在所有的从数据库的配置文件中都加上 slaveof参数指向同一个主数据库即可。

除了通过配置文件或命令行参数设置 slaveof参数,还可以在运行时使用 SLAVEOF命令修改:

127.0.0.1:6380> SLAVEOF 192.168.0.106 6379

如果该数据库已经是其他主数据库的从数据库了,SLAVEOF 命令会停止和原来数据库的同步转而和新数据库同步。此外对于从数据库来说,还可以使用 SLAVEOF NO ONE命令来使当前数据库停止接收其他数据库的同步并转换成为主数据库。

原理

当一个从数据库启动后,会向主数据库发送 SYNC 命令。同时主数据库接收到 SYNC命 令后会开始在后台保存快照(即RDB持久化的过程),并将保存快照期间接收到的命令缓存起来。当快照完成后,Redis会将快照文件和所有缓存的命令发送给从数据库。从数据库收到后,会载入快照文件并执行收到的缓存的命令。以上过程称为复制初始化。复制初始化结束后,主数据库每当收到写命令时就会将命令同步给从数据库,从而保证主从数据库数据一致。

读写分离与一致性

通过复制可以实现读写分离,以提高服务器的负载能力。在常见的场景中(如电子商务 网站),读的频率大于写,当单机的Redis无法应付大量的读请求时(尤其是较耗资源的请求,如 SORT 命令等)可以通过复制功能建立多个从数据库节点,主数据库只进行写操作,而从数据库负责读操作。这种一主多从的结构很适合读多写少的场景,而当单个的主数据库不能够满足需求时,就需要使用Redis 3.0 推出的集群功能。

从数据库持久化

另一个相对耗时的操作是持久化,为了提高性能,可以通过复制功能建立一个(或若干个)从数据库,并在从数据库中启用持久化,同时在主数据库禁用持久化。当从数据库崩溃重启后主数据库会自动将数据同步过来,所以无需担心数据丢失。

然而当主数据库崩溃时,情况就稍显复杂了。手工通过从数据库数据恢复主数据库数据时,需要严格按照以下两步进行。

  1. 在从数据库中使用 SLAVEOF NO ONE命令将从数据库提升成主数据库继续服务。
  2. 启动之前崩溃的主数据库,然后使用SLAVEOF命令将其设置成新的主数据库的从 数据库,即可将数据同步回来。

【注意】

当开启复制且主数据库关闭持久化功能时,一定不要使用 Supervisor 以及类似的进程管理工具令主数据库崩溃后自动重启。同样当主数据库所在的服务器因故关闭时,也要避免直接重新启动

这是因为当主数据库重新启动后,因为没有开启持久化功能,所以数据库中所有数据都被清空,这时从数据库依然会从主数据库中接收数据,使得所有从数据库也被清空,导致从数据库的持久化失去意义。

无论哪种情况,手工维护从数据库或主数据库的重启以及数据恢复都相对麻烦,好在Redis提供了一种自动化方案哨兵来实现这一过程,避免了手工维护的麻烦和容易出错的问题。

无硬盘复制

前面说过 Redis 复制的工作原理时介绍了复制是基于RDB方式的持久化实现的,即主数据库端在后台保存 RDB 快照,从数据库端则接收并载入快照文件。

这样的实现优点是可以显著地简化逻辑,复用已有的代码,但是缺点也很明显。

  1. 当主数据库禁用RDB快照时(即删除了所有的配置文件中的save语句),如果执行了复制初始化操作,Redis依然会生成RDB快照,所以下次启动后主数据库会以该快照恢复数据。因为复制发生的时间不能确定,这使得恢复的数据可能是任何时间点的。
  2. 因为复制初始化时需要在硬盘中创建RDB快照文件,所以如果硬盘性能很慢(如网络硬盘)时这一过程会对性能产生影响。举例来说,当使用 Redis 做缓存系统时,因为不需要持久化,所以服务器的硬盘读写速度可能较差。但是当该缓存系统使用一主多从的集群架构时,每次和从数据库同步,Redis都会执行一次快照,同时对硬盘进行读写,导致性能降低。

因此从2.8.18版本开始,Redis引入了无硬盘复制选项,开启该选项时,Redis在与从数据库进行复制初始化时将不会将快照内容存储到硬盘上,而是直接通过网络发送给从数据库,避免了硬盘的性能瓶颈。

可以在配置文件中使用如下配置来开启该功能:

repl-diskless-sync yes
原文地址:https://www.cnblogs.com/luler/p/15043057.html