Redis的数据复制

介绍 Redis 的复制

Redis 的复制功能分为同步(sync)和命令传播(command propagate)这两个操作

  • 同步操作用于将备机的数据库状态更新为主服务器的数据库状态。
    [En]

    the synchronization operation is used to update the database state of the slave server to the database state of the master server.*

  • 命令传播操作用于在修改主服务器的数据库状态时,使主备服务器的数据库恢复到一致状态,从而导致主备服务器的数据库状态不一致。
    [En]

    the command propagation operation is used to make the database of the master and slave server return to a consistent state when the database state of the master server is modified, resulting in inconsistency between the database state of the master server and the slave server.*

如果主从服务器双方的数据库保存相同的数据,我们称主从服务器的数据库状态一致

当从服务器第一次连接主服务器时,Redis 使用全量复制进行数据同步。

当从服务器在断线后重新连接主服务器时,Redis 使用增量复制进行数据同步。

完整重同步

完全复制,也称为完全重新同步。

[En]

Full replication, also known as full resynchronization.

当客户端向从服务器发送 slaveof 命令,要求从服务器复制主服务器时,从服务器首先需要执行同步操作,将从服务器的数据库状态更新至主服务器当前所处的数据库状态。

从服务器对主服务器的完整重同步操作,需要通过向主服务器发送 psync 命令来完成。psync 的命令为:psync ? -1

psync 命令在完整重同步模式下的的执行步骤:让主服务器创建并发送 RDB 文件,以及主服务器向从服务器发送保存在缓冲区里面的写命令来进行同步。

  1. 从服务器向主服务器发送 psync 命令。
  2. 主服务器收到 psync 命令后,主服务器执行 bgsave 命令,在后台生成一个 RDB 文件,并使用一个缓冲区(replication buffer)记录从现在开始执行的所有写命令。
  3. 主服务器给从服务器同步数据:当主服务器的 bgsave 命令执行完毕时,主服务器会将 bgsave 命令生成的 RDB 文件发送给从服务器,从服务器接收并载入这个 RDB 文件,将自己的数据库状态更新至主服务器执行 bgsave 命令时的数据库状态。
  4. 主服务器给从服务器发送缓冲区里面的所有写命令:主服务器将记录在缓冲区里面的所有写命令发送给从服务器, 从服务器执行这些写命令,将自己的数据库状态更新至主服务器数据库当前所处的状态。

需要注意的是:

从库在开始和主库进行数据复制前,可能保存了其他数据。为了避免之前数据的影响,从库在收到主库发送的 RDB 文件后,会先把自己当前的数据库清空。

Redis的数据复制

介绍 偏移量 & 积压缓冲区 & 运行ID

部分再同步功能通过以下三个部分实现:

[En]

The partial resynchronization function is implemented through the following three parts:

  • 主服务器的复制偏移量 和 从服务器的复制偏移量(replication offset)
  • 主服务器的复制积压缓冲区(replication backlog buffer)
  • 服务器的运行 ID(run ID)

复制偏移量

主备服务器分别维护一个复制偏移量:

[En]

The master server and slave server maintain a replication offset respectively:

  • 主服务器每次向从服务器传播 N 个字节的数据时,就将自己的复制偏移量的值加上 N。
  • 从服务器每次收到主服务器传播来的 N 个字节的数据时,就将自己的复制偏移量的值加上 N。

通过比较主备服务器的复制偏移量,程序可以很容易地知道主备服务器是否处于一致状态:

[En]

By comparing the replication offset of the master-slave server, the program can easily know whether the master-slave server is in a consistent state:

  • 如果主备服务器之间的偏移量始终相同,则主备服务器处于一致状态。
    [En]

    if the offset between the master and slave servers is always the same, then the master and slave servers are in a consistent state.*

  • 如果主备服务器的偏移量不同,则表示主备服务器状态不一致。
    [En]

    if the offset between the master and slave servers is not the same, then the master and slave servers are not in a consistent state.*

复制积压缓冲区

复制积压缓冲区(repl_backlog_buffer)是由主服务器维护的一个固定长度的先进先出(FIFO)队列。

固定是指当排队元素的数量大于队列长度时,将弹出第一个排队元素,并将新元素放入队列中。或者理解复制积压缓冲区是循环缓冲区。

[En]

Fixed means that when the number of queuing elements is greater than the queue length, the first queuing element will be popped up and the new element will be placed in the queue. Or understand that the copy backlog buffer is a circular buffer.

当主服务器传播命令时,它不仅向所有从服务器发送写命令,而且还将写命令排队到复制积压缓冲区中。

[En]

When the master server propagates commands, it not only sends write commands to all slave servers, but also queues write commands into the copy backlog buffer.

Redis的数据复制

因此,主服务器的复制积压缓冲区保存一些最近传播的写命令,并且复制积压缓冲区记录队列中每个字节的相应复制偏移量。

[En]

Therefore, the copy backlog buffer of the primary server holds some of the most recently propagated write commands, and the copy backlog buffer records the corresponding replication offset for each byte in the queue.

Redis的数据复制

当从服务器在断线后重新连接主服务器时,从服务器会通过 psync 命令将自己的复制偏移量 offset 发送给主服务器,主服务器会根据这个复制偏移量来决定对从服务器执行完整重同步还是部分重同步操作:

  • 如果 offset 偏移量之后的数据(也即是偏移量 offset+1 开始的数据)仍然存在于复制积压缓冲区里面,那么主服务器将对从服务器执行部分重同步操作。
  • 如果 offset 偏移量之后的数据已经不存在于复制积压缓冲区,那么主服务器将对从服务器执行完整重同步操作。

复制积压缓冲区的大小

Redis 为复制积压缓冲区设置的默认大小为 1MB,如果主服务器需要执行大量的写命令,又或者主从服务器断线后重连接所需的时间比较长,那么这个大小也许并不合适。我们可以通过 repl-backlog-size 选项修改复制积压缓冲区的大小。

如果复制积压缓冲区的大小设置得不恰当,那么 psync 命令的部分重同步复制就不能正常发挥作用。因此,正确估算和设置复制积压缓冲区的大小非常重要。

为了保证主从服务器断线并重连接后可以使用部分重同步功能,我们需要保证复制积压缓冲区的大小足够大。复制积压缓冲区的最小大小可以根据公式 second * write_size_per_second 来估算:

  • second 是从服务器断线后重新连接上主服务器所需的平均时间(以秒计算)。
  • write_size_per_second 是主服务器平均每秒产生的写命令数据量(协议格式的写命令的长度总和)。

例如,如果主服务器平均每秒产生1 MB的写数据,而从服务器断线之后平均要 5 秒才能重新连接上主服务器,那么复制积压缓冲区的大小就不能低于 5 MB。

为了安全起见,可以将复制积压缓冲区的大小设为: 2 * second * write_size_per_second,这样可以保证绝大部分断线情况都能用部分重同步来处理。

服务器运行 ID

每个 Redis 服务器,不论主服务器还是从服务,都会有自己的运行 ID。运行 ID 在服务器启动时自动生成,由 40 个随机的十六进制字符组成,例如:53b9b28df8042fdc9ab5e3fcbbbabff1d5dce2b3。

当从服务器对主服务器进行初次复制时,主服务器会将自己的运行 ID 发送给从服务器,而从服务器会将主服务器的这个运行 ID 保存起来。 当从服务器断线并重新连上一个主服务器时,从服务器将向当前连接的主服务器发送之前保存的主服务器的运行 ID:

  • 如果从服务器保存的主服务器的运行 ID 和当前连接的主服务器的运行 ID 相同,那么说明从服务器断线之前复制的就是当前连接的这个主服务器, 主服务器可以继续尝试执行部分重同步操作。
  • 如果从服务器保存的主服务器的运行 ID 和当前连接的主服务器的运行 ID 并不相同,那么说明从服务器断线之前复制的主服务器并不是当前连接的这个主服务器,主服务器将对从服务器执行完整重同步操作。

部分重同步

增量复制,也称为部分重新同步。

[En]

Incremental replication, also known as partial resynchronization.

在 Redis 中,从库对主库的复制可以分为以下两种情况:

  • 初始复制:之前没有从备库复制主库,或者需要从备库复制的主服务器与上次复制的主服务器不同。
    [En]

    initial replication: no master library has been copied from the slave library before, or the master server to be replicated from the slave library is different from the last master server.*

  • 断网重连后复制:命令传播阶段主备库因网络原因中断复制,但备库通过自动重连方式重新连接到主数据库,继续复制主服务器。
    [En]

    replication after network disconnection and reconnection: the master and slave library in the command propagation phase interrupted the replication due to network reasons, but the slave library reconnected to the master database through automatic reconnection, and continued to copy the master server.*

在 Redis 2.8 之前,如果主从库在命令传播时出现了网络中断,那么在断线重连后,从库会和主库重新进行一次全量复制,开销非常大。

从 2.8 版本开始,Redis 引入了部分重同步功能。部分重同步指的是,从服务器只同步主服务器的部分数据。当从服务器在断线后重新连接主服务器时,如果条件允许,主服务器可以将主从服务器连接断开期间执行的写命令发送给从服务器,从服务器只要接收并执行这些写命令,就可以将数据库更新至主服务器当前所处的状态。

执行部分重新同步是有前提条件的。

[En]

There are prerequisites for performing partial resynchronization.

  • offset 偏移量
  • 运行 ID

当从服务器对主服务器进行初次复制时,主服务器会将自己的运行 ID 发送给从服务器,而从服务器会将主服务器的这个运行 ID 保存起来。 当从服务器断线并重新连上一个主服务器时,从服务器会通过 psync 命令将自己的复制偏移量 offset 和 之前保存的主服务器的运行 ID 发送给主服务器。

主服务器会根据这个复制偏移量 和 运行ID 来决定对从服务器执行完整重同步还是部分重同步操作:

  • 如果从服务器保存的主服务器的运行 ID 和当前连接的主服务器的运行 ID 相同,那么说明从服务器断线之前复制的就是当前连接的这个主服务器, 主服务器可以继续尝试执行部分重同步操作。
  • 如果从服务器保存的主服务器的运行 ID 和当前连接的主服务器的运行 ID 并不相同,那么说明从服务器断线之前复制的主服务器并不是当前连接的这个主服务器,主服务器将对从服务器执行完整重同步操作。
  • 如果 offset 偏移量之后的数据(也即是偏移量 offset+1 开始的数据)仍然存在于复制积压缓冲区里面,那么主服务器将对从服务器执行部分重同步操作。
  • 如果 offset 偏移量之后的数据已经不存在于复制积压缓冲区,那么主服务器将对从服务器执行完整重同步操作。

从服务器对主服务器的部分重同步操作,需要通过向主服务器发送 psync 命令来完成。psync 命令为:psync < runID > < offset >

Redis的数据复制

psync 命令

从服务器对主服务器的同步操作,需要通过向主服务器发送 psync 命令来完成。

psync 命令具有完整重同步(full resynchronization)和部分重同步 (partial resynchronization)两种模式:

  • 使用完全重新同步来处理初始复制
    [En]

    full resynchronization is used to handle initial replication*

  • 部分重新同步用于处理断开连接后重复的情况:当从服务器断开连接后重新连接到主服务器时,主服务器可以在条件允许的情况下将主服务器断开连接时执行的写命令发送到从服务器。只要从服务器接收并执行这些写命令,它就可以将数据库更新到主服务器的当前状态。
    [En]

    partial resynchronization is used to deal with the situation of repetition after disconnection: when the slave server reconnects to the master server after being disconnected, the master server can send the write commands executed during the disconnection of the master server to the slave server if conditions permit. As long as the slave server receives and executes these write commands, it can update the database to the current state of the master server.*

psync 命令的调用方法有两种:

  • 如果从服务器以前没有复制过任何主服务器,或者之前执行过 slaveof no one 命令,那么从服务器在开始一次新的复制时将向主服务器发送 psync ? -1 命令,主动请求主服务器进行完整重同步。
  • 如果从服务器已经复制过某个主服务器,那么从服务器在开始一次新的复制时将向主服务器发送 psync 命令:其中 runid 是上一次复制的主服务器的运行 ID,而 offset 则是从服务器当前的复制偏移量,接收到这个命令的主服务器会通过这两个参数来判断应该对从服务器执行哪种同步操作。

根据情况,接收到 psync 命令的主服务器会向从服务器返回以下三种回复的其中一种:

  • 如果主服务器返回 +fullresync 回复,那么表示主服务器将与从服务器执行完整重同步操作:其中 runid 是这个主服务器的运行 ID,从服务器会将这个 ID 保存起来,在下一次发送 psync 命令时使用;而 offset 则是主服务器当前的复制偏移量,从服务器会将这个值作为自己的初始化偏移量。
  • 如果主服务器返回 +continue 回复,那么表示主服务器将与从服务器执行部分重同步操作,从服务器只要等着主服务器将自己缺少的那部分数据发送过来就可以了。
  • 如果主服务器返回 -err 回复,那么表示主服务器的版本低于 Redis2.8,它识别不了 psync 命令,从服务器将向主服务器发送 sync 命令,并与主服务器执行完整同步操作。

命令传播

主服务器通过向从服务器传播命令来更新从服务器的状态,以保持主服务器和从服务器的一致。

[En]

The master server updates the status of the slave server by propagating commands to the slave server to keep the master and slave server consistent.

当完成了同步之后, 主从服务器就会进入命令传播阶段, 这时主服务器只要一直将自己执行的写命令发送给从服务器, 而从服务器只要一直接收并执行主服务器发来的写命令, 就可以保证主从服务器一直保持一致了。

当主服务器传播命令时,它不仅向所有从服务器发送写命令,而且还将写命令排队到复制积压缓冲区中。

[En]

When the master server propagates commands, it not only sends write commands to all slave servers, but also queues write commands into the copy backlog buffer.

心跳检测

从服务器通过向主服务器发送命令来检测心跳和命令丢失。

[En]

The slave server detects heartbeat and command loss by sending commands to the master server.

在命令传播阶段,从服务器默认会以每秒一次的频率,向主服务器发送命令:replconf ack

发送 replconf ack 命令对于主从服务器有三个作用:

  • 检测主备服务器的网络连接状态。
    [En]

    detect the network connection status of the master-slave server.*

  • 辅助实现 min-slaves 选项。
  • 检测命令丢失。

检测主备服务器的网络连接状态。

[En]

Detect the network connection status of the master-slave server.

主从服务器可以通过发送和接收 replconf ack 命令来检查两者之间的网络连接是否正常:如果主服务器超过一秒钟没有收到从服务器发来的 replconf ack 命令,那么主服务器就知道主从服务器之间的连接出现问题了。

通过向主服务器发送 info replication 命令,在列出的从服务器列表的 lag 一栏中,我们可以看到相应从服务器最后一次向主服务器发送 replconf ack 命令距离现在过了多少秒。在一般情况下,lag 的值应该在 0 秒或者 1 秒之间跳动,如果超过 1 秒的话,那么说明主从服务器之间的连接出现了故障。

辅助实现 min-slaves 选项。

Redis 的 min-slaves-to-write 和 min-slaves-max-lag 两个选项可以防止主服务器在不安全的情况下执行写命令。

例如,如果我们向主服务器提供以下设置:

[En]

For example, if we provide the following settings to the primary server:

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

那么在从服务器的数量少于 3 个,或者 3 个从服务器的延迟(lag)值都 ≥ 10 秒时,主服务器将拒绝执行写命令,这里的延迟值就是上面提到的 info replication 命令的 lag 值。

检测命令丢失。

如果因为网络故障,主服务器传播给从服务器的写命令在半路丢失,那么当从服务器向主服务器发送 replconf ack 命令时,主服务器将发觉从服务器当前的复制偏移量少于自己的复制偏移量,然后主服务器就会根据从服务器提交的复制偏移量,在复制积压缓冲区里面找到从服务器缺少的数据,并将这些数据重新发送给从服务器。

参考资料

《Redis设计与实现》

Original: https://www.cnblogs.com/feiyu2/p/16996233.html
Author: 真正的飞鱼
Title: Redis的数据复制



相关阅读

Title: C++文件操作

1.创建文件流

创建文件流需要包含头文件

#include

创建文件输入流对象

ofstream file;

创建文件输出流对象

ifstream file;

2.写入文本文件

1.首先创建文件输出流对象
2.使用.open()打开文件 括号中可以填string类型变量。
不重写文件内容,内容追加到末尾:file.open(R”(D:\MySQL\test.txt)”,ios::app);
用file.is_open()函数判断文件是否打开成功
3.使用输出流对象写内容
4.使用.close()关闭输出流对象
文件路径的三种写法:

  1. string path=”D\data\test.txt”; ‘/’需要转义 //
  2. R”(D:\MySQL\test.txt) 原始字面量 c++11 标准
  3. D:/MySQL/test.txt 斜杆反着写
#include
#include
using namespace std;
int main(){
    ofstream file;
    string path="D\\data\\test.txt";

    file.open(path);
    file.open(path,ios::app);
    file<<"My name is Yang";
    file.close();
    return 0;
}

3.读取文本文件

1.首先创建文件输入流对象
2.使用.open()打开文件 路径写法同上
3.使用输入流对象读文件
4.使用.close()关闭输出流对象

#include
#include
#include
using namespace std;
int main(){
    ifstream file;
    file.open(R"(D:\MySQL\test.txt)",ios::app);
    if(file.is_open()==false){
        cout<<"文件打开失败"<<endl;
    }else{
        cout<<"文件打开成功"<<endl;
    }
    string str;

    while(getline(file,str)){
        cout<<str<<endl;
    }

    while(file>>str){
        cout<<str<<endl;
    }

    file.close();
    return 0;
}

Original: https://blog.csdn.net/weixin_51169222/article/details/128322861
Author: 萨达大
Title: C++文件操作

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/358283/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

最近整理资源【免费获取】:   👉 程序员最新必读书单  | 👏 互联网各方向面试题下载 | ✌️计算机核心资源汇总