AOF 命令同步数据备份

2024-05-11 18:48
文章标签 命令 同步 aof 数据备份

本文主要是介绍AOF 命令同步数据备份,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

AOF 命令同步

Redis 将所有对数据库进行过写入的命令(及其参数)记录到 AOF 文件, 以此达到记录数据库状态的目的, 为了方便起见, 我们称呼这种记录过程为同步。

举个例子, 如果执行以下命令:

redis> RPUSH list 1 2 3 4
(integer) 4redis> LRANGE list 0 -1
1) "1"
2) "2"
3) "3"
4) "4"redis> KEYS *
1) "list"redis> RPOP list
"4"redis> LPOP list
"1"redis> LPUSH list 1
(integer) 3redis> LRANGE list 0 -1
1) "1"
2) "2"
3) "3"

那么其中四条对数据库有修改的写入命令就会被同步到 AOF 文件中:

RPUSH list 1 2 3 4RPOP listLPOP listLPUSH list 1

为了处理的方便, AOF 文件使用网络通讯协议的格式来保存这些命令。

比如说, 上面列举的四个命令在 AOF 文件中就实际保存如下:

*2
$6
SELECT
$1
0
*6
$5
RPUSH
$4
list
$1
1
$1
2
$1
3
$1
4
*2
$4
RPOP
$4
list
*2
$4
LPOP
$4
list
*3
$5
LPUSH
$4
list
$1
1

除了 SELECT 命令是 AOF 程序自己加上去的之外, 其他命令都是之前我们在终端里执行的命令。

同步命令到 AOF 文件的整个过程可以分为三个阶段:

  1. 命令传播:Redis 将执行完的命令、命令的参数、命令的参数个数等信息发送到 AOF 程序中。
  2. 缓存追加:AOF 程序根据接收到的命令数据,将命令转换为网络通讯协议的格式,然后将协议内容追加到服务器的 AOF 缓存中。
  3. 文件写入和保存:AOF 缓存中的内容被写入到 AOF 文件末尾,如果设定的 AOF 保存条件被满足的话, fsync 函数或者 fdatasync函数会被调用,将写入的内容真正地保存到磁盘中。

以下几个小节将详细地介绍这三个步骤。

命令传播

当一个 Redis 客户端需要执行命令时, 它通过网络连接, 将协议文本发送给 Redis 服务器。

比如说, 要执行命令 SET KEY VALUE , 客户端将向服务器发送文本 "*3\r\n$3\r\nSET\r\n$3\r\nKEY\r\n$5\r\nVALUE\r\n" 。

服务器在接到客户端的请求之后, 它会根据协议文本的内容, 选择适当的命令函数, 并将各个参数从字符串文本转换为 Redis 字符串对象(StringObject)。

比如说, 针对上面的 SET 命令例子, Redis 将客户端的命令指针指向实现 SET 命令的 setCommand 函数, 并创建三个 Redis 字符串对象, 分别保存 SET 、 KEY 和 VALUE 三个参数(命令也算作参数)。

每当命令函数成功执行之后, 命令参数都会被传播到 AOF 程序, 以及 REPLICATION 程序(本节不讨论这个,列在这里只是为了完整性的考虑)。

这个执行并传播命令的过程可以用以下伪代码表示:

if (execRedisCommand(cmd, argv, argc) == EXEC_SUCCESS):if aof_is_turn_on():# 传播命令到 AOF 程序propagate_aof(cmd, argv, argc)if replication_is_turn_on():# 传播命令到 REPLICATION 程序propagate_replication(cmd, argv, argc)

以下是该过程的流程图:

digraph propagate {      node [shape = plaintext, style = filled];      edge [style = bold];      // node       exec [label = "命令执行成功", fillcolor = "#FADCAD"];      aof_choice [label = "AOF\n 功能已打开?", shape = diamond, fillcolor = "#95BBE3"];      propagate_aof [label = "传播命令到 AOF 程序", fillcolor = "#A8E270"];      replication_choice [label = "REPLICATION\n 功能已打开?", shape = diamond, fillcolor = "#95BBE3"];      propagate_replication [label = "传播命令到 REPLICATION 程序", fillcolor = "#A8E270"];      remaind_jobs [label = "处理后续步骤:\n清理资源、\n等等", fillcolor = "#FADCAD"];      // edge      exec -> aof_choice;      aof_choice -> propagate_aof [label = "是"];      propagate_aof -> replication_choice;      aof_choice -> replication_choice [label = "否"];      replication_choice -> remaind_jobs [label = "否"];      replication_choice -> propagate_replication [label = "是"];      propagate_replication -> remaind_jobs; }

缓存追加

当命令被传播到 AOF 程序之后, 程序会根据命令以及命令的参数, 将命令从字符串对象转换回原来的协议文本。

比如说, 如果 AOF 程序接受到的三个参数分别保存着 SET 、 KEY 和 VALUE 三个字符串, 那么它将生成协议文本 "*3\r\n$3\r\nSET\r\n$3\r\nKEY\r\n$5\r\nVALUE\r\n" 。

协议文本生成之后, 它会被追加到 redis.h/redisServer 结构的 aof_buf 末尾。

redisServer 结构维持着 Redis 服务器的状态, aof_buf 域则保存着所有等待写入到 AOF 文件的协议文本:

struct redisServer {// 其他域...sds aof_buf;// 其他域...
};

至此, 追加命令到缓存的步骤执行完毕。

综合起来,整个缓存追加过程可以分为以下三步:

  1. 接受命令、命令的参数、以及参数的个数、所使用的数据库等信息。
  2. 将命令还原成 Redis 网络通讯协议。
  3. 将协议文本追加到 aof_buf 末尾。

文件写入和保存

每当服务器常规任务函数被执行、 或者事件处理器被执行时, aof.c/flushAppendOnlyFile 函数都会被调用, 这个函数执行以下两个工作:

WRITE:根据条件,将 aof_buf 中的缓存写入到 AOF 文件。

SAVE:根据条件,调用 fsync 或 fdatasync 函数,将 AOF 文件保存到磁盘中。

两个步骤都需要根据一定的条件来执行, 而这些条件由 AOF 所使用的保存模式来决定, 以下小节就来介绍 AOF 所使用的三种保存模式, 以及在这些模式下, 步骤 WRITE 和 SAVE 的调用条件。

AOF 保存模式

Redis 目前支持三种 AOF 保存模式,它们分别是:

  1. AOF_FSYNC_NO :不保存。
  2. AOF_FSYNC_EVERYSEC :每一秒钟保存一次。
  3. AOF_FSYNC_ALWAYS :每执行一个命令保存一次。

以下三个小节将分别讨论这三种保存模式。

不保存

在这种模式下, 每次调用 flushAppendOnlyFile 函数, WRITE 都会被执行, 但 SAVE 会被略过。

在这种模式下, SAVE 只会在以下任意一种情况中被执行:

  • Redis 被关闭
  • AOF 功能被关闭
  • 系统的写缓存被刷新(可能是缓存已经被写满,或者定期保存操作被执行)

这三种情况下的 SAVE 操作都会引起 Redis 主进程阻塞。

每一秒钟保存一次

在这种模式中, SAVE 原则上每隔一秒钟就会执行一次, 因为 SAVE 操作是由后台子线程调用的, 所以它不会引起服务器主进程阻塞。

注意, 在上一句的说明里面使用了词语“原则上”, 在实际运行中, 程序在这种模式下对 fsync 或 fdatasync 的调用并不是每秒一次, 它和调用 flushAppendOnlyFile 函数时 Redis 所处的状态有关。

每当 flushAppendOnlyFile 函数被调用时, 可能会出现以下四种情况:

  • 子线程正在执行 SAVE ,并且:

    1. 这个 SAVE 的执行时间未超过 2 秒,那么程序直接返回,并不执行 WRITE 或新的 SAVE 。
    2. 这个 SAVE 已经执行超过 2 秒,那么程序执行 WRITE ,但不执行新的 SAVE 。注意,因为这时 WRITE 的写入必须等待子线程先完成(旧的) SAVE ,因此这里 WRITE 会比平时阻塞更长时间。
  • 子线程没有在执行 SAVE ,并且:

    1. 上次成功执行 SAVE 距今不超过 1 秒,那么程序执行 WRITE ,但不执行 SAVE 。
    2. 上次成功执行 SAVE 距今已经超过 1 秒,那么程序执行 WRITE 和 SAVE 。

可以用流程图表示这四种情况:

digraph flush {      node [shape = plaintext, style = filled, fillcolor = "#FADCAD"];      edge [style = bold];      //      SAVE_running_choice [label = "SAVE 正在执行?", shape = diamond, fillcolor = "#A8E270"];      over_2_second_choice [label = "运行时间\n超过 2 秒?", shape = diamond, fillcolor = "#95BBE3"];      not_over_2_second [label = "情况 1 :\n函数直接返回\n 不执行 WRITE 或\n新的 SAVE"];      over_2_second [label = "情况 2 :\n执行 WRITE \n 但不执行新的 SAVE \n"];      SAVE_running_choice -> over_2_second_choice [label = "是"];      over_2_second_choice -> not_over_2_second [label = "否"];      over_2_second_choice -> over_2_second [label = "是"];      finish_over_2_second [label = "距离上次 SAVE\n 执行成功\n超过 1 秒?", shape = diamond, fillcolor = "#95BBE3"];      no [label = "情况 3 :\n 执行 WRITE \n 但不执行新的 SAVE "];      yes [label = "情况 4 :\n 执行 WRITE 和\n新的 SAVE\n"];      SAVE_running_choice -> finish_over_2_second [label = "否"];      finish_over_2_second -> yes [label = "是"];      finish_over_2_second -> no [label = "否"];  }

根据以上说明可以知道, 在“每一秒钟保存一次”模式下, 如果在情况 1 中发生故障停机, 那么用户最多损失小于 2 秒内所产生的所有数据。

如果在情况 2 中发生故障停机, 那么用户损失的数据是可以超过 2 秒的。

Redis 官网上所说的, AOF 在“每一秒钟保存一次”时发生故障, 只丢失 1 秒钟数据的说法, 实际上并不准确。

每执行一个命令保存一次

在这种模式下,每次执行完一个命令之后, WRITE 和 SAVE 都会被执行。

另外,因为 SAVE 是由 Redis 主进程执行的,所以在 SAVE 执行期间,主进程会被阻塞,不能接受命令请求。

AOF 保存模式对性能和安全性的影响

在上一个小节, 我们简短地描述了三种 AOF 保存模式的工作方式, 现在, 是时候研究一下这三个模式在安全性和性能方面的区别了。

对于三种 AOF 保存模式, 它们对服务器主进程的阻塞情况如下:

  1. 不保存(AOF_FSYNC_NO):写入和保存都由主进程执行,两个操作都会阻塞主进程。
  2. 每一秒钟保存一次(AOF_FSYNC_EVERYSEC):写入操作由主进程执行,阻塞主进程。保存操作由子线程执行,不直接阻塞主进程,但保存操作完成的快慢会影响写入操作的阻塞时长。
  3. 每执行一个命令保存一次(AOF_FSYNC_ALWAYS):和模式 1 一样。

因为阻塞操作会让 Redis 主进程无法持续处理请求, 所以一般说来, 阻塞操作执行得越少、完成得越快, Redis 的性能就越好。

模式 1 的保存操作只会在AOF 关闭或 Redis 关闭时执行, 或者由操作系统触发, 在一般情况下, 这种模式只需要为写入阻塞, 因此它的写入性能要比后面两种模式要高, 当然, 这种性能的提高是以降低安全性为代价的: 在这种模式下, 如果运行的中途发生停机, 那么丢失数据的数量由操作系统的缓存冲洗策略决定。

模式 2 在性能方面要优于模式 3 , 并且在通常情况下, 这种模式最多丢失不多于 2 秒的数据, 所以它的安全性要高于模式 1 , 这是一种兼顾性能和安全性的保存方案。

模式 3 的安全性是最高的, 但性能也是最差的, 因为服务器必须阻塞直到命令信息被写入并保存到磁盘之后, 才能继续处理请求。

综合起来,三种 AOF 模式的操作特性可以总结如下:

模式WRITE 是否阻塞?SAVE 是否阻塞?停机时丢失的数据量
AOF_FSYNC_NO阻塞阻塞操作系统最后一次对 AOF 文件触发 SAVE 操作之后的数据。
AOF_FSYNC_EVERYSEC阻塞不阻塞一般情况下不超过 2 秒钟的数据。
AOF_FSYNC_ALWAYS阻塞阻塞最多只丢失一个命令的数据。

这篇关于AOF 命令同步数据备份的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/980361

相关文章

Linux join命令的使用及说明

《Linuxjoin命令的使用及说明》`join`命令用于在Linux中按字段将两个文件进行连接,类似于SQL的JOIN,它需要两个文件按用于匹配的字段排序,并且第一个文件的换行符必须是LF,`jo... 目录一. 基本语法二. 数据准备三. 指定文件的连接key四.-a输出指定文件的所有行五.-o指定输出

Linux jq命令的使用解读

《Linuxjq命令的使用解读》jq是一个强大的命令行工具,用于处理JSON数据,它可以用来查看、过滤、修改、格式化JSON数据,通过使用各种选项和过滤器,可以实现复杂的JSON处理任务... 目录一. 简介二. 选项2.1.2.2-c2.3-r2.4-R三. 字段提取3.1 普通字段3.2 数组字段四.

Redis中的AOF原理及分析

《Redis中的AOF原理及分析》Redis的AOF通过记录所有写操作命令实现持久化,支持always/everysec/no三种同步策略,重写机制优化文件体积,与RDB结合可平衡数据安全与恢复效率... 目录开篇:从日记本到AOF一、AOF的基本执行流程1. 命令执行与记录2. AOF重写机制二、AOF的

java中ssh2执行多条命令的四种方法

《java中ssh2执行多条命令的四种方法》本文主要介绍了java中ssh2执行多条命令的四种方法,包括分号分隔、管道分隔、EOF块、脚本调用,可确保环境配置生效,提升操作效率,具有一定的参考价值,感... 目录1 使用分号隔开2 使用管道符号隔开3 使用写EOF的方式4 使用脚本的方式大家平时有没有遇到自

Linux命令rm如何删除名字以“-”开头的文件

《Linux命令rm如何删除名字以“-”开头的文件》Linux中,命令的解析机制非常灵活,它会根据命令的开头字符来判断是否需要执行命令选项,对于文件操作命令(如rm、ls等),系统默认会将命令开头的某... 目录先搞懂:为啥“-”开头的文件删不掉?两种超简单的删除方法(小白也能学会)方法1:用“--”分隔命

Redis 的 SUBSCRIBE命令详解

《Redis的SUBSCRIBE命令详解》Redis的SUBSCRIBE命令用于订阅一个或多个频道,以便接收发送到这些频道的消息,本文给大家介绍Redis的SUBSCRIBE命令,感兴趣的朋友跟随... 目录基本语法工作原理示例消息格式相关命令python 示例Redis 的 SUBSCRIBE 命令用于订

防止Linux rm命令误操作的多场景防护方案与实践

《防止Linuxrm命令误操作的多场景防护方案与实践》在Linux系统中,rm命令是删除文件和目录的高效工具,但一旦误操作,如执行rm-rf/或rm-rf/*,极易导致系统数据灾难,本文针对不同场景... 目录引言理解 rm 命令及误操作风险rm 命令基础常见误操作案例防护方案使用 rm编程 别名及安全删除

Java使用jar命令配置服务器端口的完整指南

《Java使用jar命令配置服务器端口的完整指南》本文将详细介绍如何使用java-jar命令启动应用,并重点讲解如何配置服务器端口,同时提供一个实用的Web工具来简化这一过程,希望对大家有所帮助... 目录1. Java Jar文件简介1.1 什么是Jar文件1.2 创建可执行Jar文件2. 使用java

Python与MySQL实现数据库实时同步的详细步骤

《Python与MySQL实现数据库实时同步的详细步骤》在日常开发中,数据同步是一项常见的需求,本篇文章将使用Python和MySQL来实现数据库实时同步,我们将围绕数据变更捕获、数据处理和数据写入这... 目录前言摘要概述:数据同步方案1. 基本思路2. mysql Binlog 简介实现步骤与代码示例1

Linux查询服务器 IP 地址的命令详解

《Linux查询服务器IP地址的命令详解》在服务器管理和网络运维中,快速准确地获取服务器的IP地址是一项基本但至关重要的技能,下面我们来看看Linux中查询服务器IP的相关命令使用吧... 目录一、hostname 命令:简单高效的 IP 查询工具命令详解实际应用技巧注意事项二、ip 命令:新一代网络配置全