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

相关文章

postgresql数据库基本操作及命令详解

《postgresql数据库基本操作及命令详解》本文介绍了PostgreSQL数据库的基础操作,包括连接、创建、查看数据库,表的增删改查、索引管理、备份恢复及退出命令,适用于数据库管理和开发实践,感兴... 目录1. 连接 PostgreSQL 数据库2. 创建数据库3. 查看当前数据库4. 查看所有数据库

Redis的持久化之RDB和AOF机制详解

《Redis的持久化之RDB和AOF机制详解》:本文主要介绍Redis的持久化之RDB和AOF机制,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录概述RDB(Redis Database)核心原理触发方式手动触发自动触发AOF(Append-Only File)核

linux重启命令有哪些? 7个实用的Linux系统重启命令汇总

《linux重启命令有哪些?7个实用的Linux系统重启命令汇总》Linux系统提供了多种重启命令,常用的包括shutdown-r、reboot、init6等,不同命令适用于不同场景,本文将详细... 在管理和维护 linux 服务器时,完成系统更新、故障排查或日常维护后,重启系统往往是必不可少的步骤。本文

nginx启动命令和默认配置文件的使用

《nginx启动命令和默认配置文件的使用》:本文主要介绍nginx启动命令和默认配置文件的使用,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录常见命令nginx.conf配置文件location匹配规则图片服务器总结常见命令# 默认配置文件启动./nginx

基于Python实现一个Windows Tree命令工具

《基于Python实现一个WindowsTree命令工具》今天想要在Windows平台的CMD命令终端窗口中使用像Linux下的tree命令,打印一下目录结构层级树,然而还真有tree命令,但是发现... 目录引言实现代码使用说明可用选项示例用法功能特点添加到环境变量方法一:创建批处理文件并添加到PATH1

canal实现mysql数据同步的详细过程

《canal实现mysql数据同步的详细过程》:本文主要介绍canal实现mysql数据同步的详细过程,本文通过实例图文相结合给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的... 目录1、canal下载2、mysql同步用户创建和授权3、canal admin安装和启动4、canal

Linux实现线程同步的多种方式汇总

《Linux实现线程同步的多种方式汇总》本文详细介绍了Linux下线程同步的多种方法,包括互斥锁、自旋锁、信号量以及它们的使用示例,通过这些同步机制,可以解决线程安全问题,防止资源竞争导致的错误,示例... 目录什么是线程同步?一、互斥锁(单人洗手间规则)适用场景:特点:二、条件变量(咖啡厅取餐系统)工作流

Mysql的主从同步/复制的原理分析

《Mysql的主从同步/复制的原理分析》:本文主要介绍Mysql的主从同步/复制的原理分析,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录为什么要主从同步?mysql主从同步架构有哪些?Mysql主从复制的原理/整体流程级联复制架构为什么好?Mysql主从复制注意

Java -jar命令如何运行外部依赖JAR包

《Java-jar命令如何运行外部依赖JAR包》在Java应用部署中,java-jar命令是启动可执行JAR包的标准方式,但当应用需要依赖外部JAR文件时,直接使用java-jar会面临类加载困... 目录引言:外部依赖JAR的必要性一、问题本质:类加载机制的限制1. Java -jar的默认行为2. 类加

git stash命令基本用法详解

《gitstash命令基本用法详解》gitstash是Git中一个非常有用的命令,它可以临时保存当前工作区的修改,让你可以切换到其他分支或者处理其他任务,而不需要提交这些还未完成的修改,这篇文章主要... 目录一、基本用法1. 保存当前修改(包括暂存区和工作区的内容)2. 查看保存了哪些 stash3. 恢