草庐IT

redis持久化之AOF详解

lans_g 2023-06-15 原文

一、AOF日志

​ 既然要存储数据,那就不可避免的要对数据进行持久化操作。在redis中,有RDBAOF两种持久化方式,其中RDB持久化通过保存数据库中的键值对来记录数据库状态不同AOF持久化是通过保存Redis服务器所执行的写命令来记录数据库状态的(不记录读命令)

​ 试想一下,如果 Redis 每执行一条写操作命令,就把该命令以追加的方式写入到一个名为AOF的文件里,服务器在启动时,可以通过载入和执行AOF文件中保存的命令来还原服务器关闭之前的数据库状态。

Redis 是先执行写操作命令后,然将该命令记录到 AOF缓冲区中,最后再将文件写入磁盘。

这么做有两个好处:

  • **避免额外的检查开销。**如果先写日志的话可能存在语法错误,导致数据恢复时出错。
  • 不会阻塞当前写操作命令的执行

但是也存在两个问题:

  • 如果Redis 在还没来得及将命令写入到硬盘时,服务器发生宕机了,这个数据就会有丢失的风险
  • 可能会给「下一个」命令带来阻塞风险

二、AOF载入与数据还原

​ 因为AOF文件里面包含了重建数据库状态所需的所有写命令,所以 服务器只要读入并重新执行一遍AOF文件里面保存的写命令,就可以还 原服务器关闭之前的数据库状态。

步骤如下:

1.创建一个不带网络连接的伪客户端(fake client):因为Redis的 命令只能在客户端上下文中执行,而载入AOF文件时所使用的命令直接 来源于AOF文件而不是网络连接,所以服务器使用了一个没有网络连接 的伪客户端来执行AOF文件保存的写命令,伪客户端执行命令的效果和 带网络连接的客户端执行命令的效果完全一样。

2.从AOF文件中分析并读取出一条写命令。

3.使用伪客户端执行被读出的写命令。

4.一直执行步骤2和步骤3,直到AOF文件中的所有写命令都被处 理完毕为止。

执行完成后,数据库的状态就会被还原。

三、AOF的实现

AOF持久化功能的实现可以分为命令追加(append)文件写入、 **文件同步(sync)**三个步骤。

1.命令追加

​ 当AOF持久化功能处于打开状态时,服务器在执行完一个写命令之 后,会以协议格式将被执行的写命令追加到服务器状态的aof_buf缓冲区的末尾

eg.如果执行以下指令:

redis> SET KEY VALUE
OK

那么会将以下协议内容追加到 aof_buf缓冲区的末尾:

*3\r\n$3\r\nSET\r\n$3\r\nKEY\r\n$5\r\nVALUE\r\n

2.文件写入

(1)通过 write() 系统调用,将 aof_buf 缓冲区的数据写入到 AOF 文件,此时数据并没有写入到硬盘,而是拷贝到了内核缓冲区 page cache,等待内核将数据写入硬盘;

(2)具体内核缓冲区的数据什么时候写入到硬盘,由内核决定。

(为了提高文件的写入效率,在现代操作系统中,当用户调用write 函数,将一些数据写入到文件的时候,操作系统通常会将写入数据暂时保存在一个内存缓冲区里面,等到缓冲区的空间被填满、或者超过 了指定的时限之后,才真正地将缓冲区中的数据写入到磁盘里面。)

3.文件同步

​ 如果由内核决定将数据写入硬盘的话,如果服务器宕机,那么就会有数据丢失的风险。为了解决这个问题,系统提供了fsync和fdatasync两个同步函数三种写回策略,它们可以强制让操作系统立即将缓冲区中的数据写入到硬盘里面,从而确保写入数据的安全性。

redis.conf 配置文件中的 appendfsync 配置项可以有以下 3 种参数可填:

  • always:服务器在每次写操作后都将 aof_buf缓冲区中的所有内容写入到AOF文件,然后立即执行fsync()函数同步AOF文件到磁盘,所以always的效率是最慢的,但也是最安全的。可靠性高,性能低。
  • everysec:服务器在每次写操作后都要 将aof_buf缓冲区中的所有内容写入到AOF文件,并且每隔一秒就要在子线程中对AOF文件进行一次同步,创建一个异步任务执行fsync()函数可靠性和性能都适中。
  • no:将缓冲区的内容写入AOF文件后,何时进行同步由操作系统控制,不执行fsync()函数性能好,可靠性低,宕机可能会丢失较多数据。

四、AOF后台重写机制

​ 随着写操作的不断执行,AOF文件会变得越来越大,这就会带来一些性能问题(比如恢复慢)。所以当AOF文件大小超过阈值时,redis就会进行AOF重写。redis服务器会创建一个新的AOF文件来覆盖现有的AOF文件,新的文件中减少了冗余的命令。

为什么要创建一个新的文件而不直接对原先文件进行修改删除呢?

因为如果在原先文件上操作,重写失败的话,原先的文件就会被污染,这就导致我们无法回到之前的状态了。而创建一个新的文件就算重写失败了,对原文件也没有影响。

eg.现在执行如下两条命令

set name xiaoming
set name xiaoli

没有重写的AOF文件中,会记录这两条命令。而经过重写的AOF文件中,只会保留第二条命令。因为AOF文件是为了记录数据库的状态,历史数据就没必要进行保存了。

1.实现原理

​ 因为redis服务器使用单个线程来处理命令请求,而重写AOF文件的话会进行大量写入操作,如果使用主进程可能会被长时间阻塞,以致于无法处理客户端发来的请求。为了解决这个问题,redis通过创建一个后台子进程bgrewriteaof,子进程会拿到父进程的数据副本,然后完成重写操作

  • 为什么创建一个子进程而不是在主进程中创建一个线程呢?

​ 因为如果是使用线程,多线程之间会共享内存,那么在修改共享内存数据的时候,需要通过加锁来保证数据的安全,而这样就会降低性能。而使用子进程,创建子进程时,父子进程是共享内存数据的,不过这个共享的内存只能以只读的方式,而当父子进程任意一方修改了该共享内存,就会发生「写时复制」,于是父子进程就有了独立的数据副本,就不用加锁来保证数据安全。


​ 这样虽然服务器进程可以继续处理命令请求了,不过子进程在进行AOF 重写期间,新的命令可能会对现有的数据库状态进行修改,从而使得服务器当前的数据库状态重写后的AOF文件所保存的数据库状态不一致

像这样,重写的AOF中只有k1一个键,而当前数据库中却有k1,k2,k3,k4四个键,导致状态不一致!

为了解决这种数据不一致问题,redis服务器设置了一个AOF重写缓冲区这个缓冲区在服务器创建子进程之后开始使用当redis服务器执行完一个写命令之后,它会同时将这个写命令发送给AOF缓冲区和 AOF重写缓冲区

这样在子进程重写AOF时,主进程会执行以下工作:

(1)执行客户端发来的命令。

(2)将执行后的写命令追加到AOF缓冲区。AOF缓冲区的内容会定期被写入和同步到AOF文件,对现有AOF 文件的处理工作会如常进行。

(3)将执行后的写命令追加到AOF重写缓冲区。从创建子进程开始,服务器执行的所有写命令都会被记录到AOF 重写缓冲区里面。

子进程完成AOF重写工作后,会向父进程发送一个信号,父进程收到信号会调用信号处理函数执行以下工作:

(1)将AOF重写缓冲区中的所有内容写入到新AOF文件中,这时新 AOF文件所保存的数据库状态将和服务器当前的数据库状态一致。

(2)对新的AOF文件进行改名,原子地覆盖现有的AOF 文件,完成新旧两个AOF文件的替换。

注意:在整个AOF后台重写过程中,只有信号处理函数执行时会对服务器进程(父进程)造成阻塞。

参考
小林coding
【redis设计与实现】

有关redis持久化之AOF详解的更多相关文章

  1. ruby-on-rails - Rails 模型——非持久类成员或属性? - 2

    对于Rails模型,是否可以/建议让一个类的成员不持久保存到数据库中?我想将用户最后选择的类型存储在session变量中。由于我无法从我的模型中设置session变量,我想将值存储在一个“虚拟”类成员中,该成员只是将值传递回Controller。你能有这样的类(class)成员吗? 最佳答案 将非持久属性添加到Rails模型就像任何其他Ruby类一样:classUser扩展解释:在Ruby中,所有实例变量都是私有(private)的,不需要在赋值前定义。attr_accessor创建一个setter和getter方法:classUs

  2. 物联网MQTT协议详解 - 2

    一、什么是MQTT协议MessageQueuingTelemetryTransport:消息队列遥测传输协议。是一种基于客户端-服务端的发布/订阅模式。与HTTP一样,基于TCP/IP协议之上的通讯协议,提供有序、无损、双向连接,由IBM(蓝色巨人)发布。原理:(1)MQTT协议身份和消息格式有三种身份:发布者(Publish)、代理(Broker)(服务器)、订阅者(Subscribe)。其中,消息的发布者和订阅者都是客户端,消息代理是服务器,消息发布者可以同时是订阅者。MQTT传输的消息分为:主题(Topic)和负载(payload)两部分Topic,可以理解为消息的类型,订阅者订阅(Su

  3. Tcl脚本入门笔记详解(一) - 2

    TCL脚本语言简介•TCL(ToolCommandLanguage)是一种解释执行的脚本语言(ScriptingLanguage),它提供了通用的编程能力:支持变量、过程和控制结构;同时TCL还拥有一个功能强大的固有的核心命令集。TCL经常被用于快速原型开发,脚本编程,GUI和测试等方面。•实际上包含了两个部分:一个语言和一个库。首先,Tcl是一种简单的脚本语言,主要使用于发布命令给一些互交程序如文本编辑器、调试器和shell。由于TCL的解释器是用C\C++语言的过程库实现的,因此在某种意义上我们又可以把TCL看作C库,这个库中有丰富的用于扩展TCL命令的C\C++过程和函数,所以,Tcl是

  4. 【详解】Docker安装Elasticsearch7.16.1集群 - 2

    开门见山|拉取镜像dockerpullelasticsearch:7.16.1|配置存放的目录#存放配置文件的文件夹mkdir-p/opt/docker/elasticsearch/node-1/config#存放数据的文件夹mkdir-p/opt/docker/elasticsearch/node-1/data#存放运行日志的文件夹mkdir-p/opt/docker/elasticsearch/node-1/log#存放IK分词插件的文件夹mkdir-p/opt/docker/elasticsearch/node-1/plugins若你使用了moba,直接右键新建即可如上图所示依次类推创建

  5. 【Elasticsearch基础】Elasticsearch索引、文档以及映射操作详解 - 2

    文章目录概念索引相关操作创建索引更新副本查看索引删除索引索引的打开与关闭收缩索引索引别名查询索引别名文档相关操作新建文档查询文档更新文档删除文档映射相关操作查询文档映射创建静态映射创建索引并添加映射概念es中有三个概念要清楚,分别为索引、映射和文档(不用死记硬背,大概有个印象就可以)索引可理解为MySQL数据库;映射可理解为MySQL的表结构;文档可理解为MySQL表中的每行数据静态映射和动态映射上面已经介绍了,映射可理解为MySQL的表结构,在MySQL中,向表中插入数据是需要先创建表结构的;但在es中不必这样,可以直接插入文档,es可以根据插入的文档(数据),动态的创建映射(表结构),这就

  6. 最强Http缓存策略之强缓存和协商缓存的详解与应用实例 - 2

    HTTP缓存是指浏览器或者代理服务器将已经请求过的资源保存到本地,以便下次请求时能够直接从缓存中获取资源,从而减少网络请求次数,提高网页的加载速度和用户体验。缓存分为强缓存和协商缓存两种模式。一.强缓存强缓存是指浏览器直接从本地缓存中获取资源,而不需要向web服务器发出网络请求。这是因为浏览器在第一次请求资源时,服务器会在响应头中添加相关缓存的响应头,以表明该资源的缓存策略。常见的强缓存响应头如下所述:Cache-ControlCache-Control响应头是用于控制强制缓存和协商缓存的缓存策略。该响应头中的指令如下:max-age:指定该资源在本地缓存的最长有效时间,以秒为单位。例如:Ca

  7. IDEA 2022 创建 Spring Boot 项目详解 - 2

    如何用IDEA2022创建并初始化一个SpringBoot项目?目录如何用IDEA2022创建并初始化一个SpringBoot项目?0. 环境说明1.  创建SpringBoot项目 2.编写初始化代码0. 环境说明IDEA2022.3.1JDK1.8SpringBoot1.  创建SpringBoot项目        打开IDEA,选择NewProject创建项目。        填写项目名称、项目构建方式、jdk版本,按需要修改项目文件路径等信息。        选择springboot版本以及需要的包,此处只选择了springweb。        此处需特别注意,若你使用的是jdk1

  8. 详解Unity中的粒子系统Particle System (二) - 2

    前言上一篇我们简要讲述了粒子系统是什么,如何添加,以及基本模块的介绍,以及对于曲线和颜色编辑器的讲解。从本篇开始,我们将按照模块结构讲解下去,本篇主要讲粒子系统的主模块,该模块主要是控制粒子的初始状态和全局属性的,以下是关于该模块的介绍,请大家指正。目录前言本系列提要一、粒子系统主模块1.阅读前注意事项2.参考图3.参数讲解DurationLoopingPrewarmStartDelayStartLifetimeStartSpeed3DStartSizeStartSize3DStartRotationStartRotationFlipRotationStartColorGravityModif

  9. VMware虚拟机与本地主机进行磁盘共享(详解) - 2

    VMware虚拟机与本地主机进行磁盘共享前提虚拟机版本为Windows10(专业版,不是可能有问题)本地主机为家庭版或学生版(此版本会有问题,但有替代方式)最好是专业版VMware操作1.关闭防火墙,全部关闭。2.打开电脑属性3.点击共享-》高级共享-》权限4.如果没有everyone,就添加权限选择完全控制,然后应用确定。5.打开cmd输入lusrmgr.msc(只有专业版可以打开)如果不是专业版,可以跳过这一步。点击用户-》administrator密码要复杂密码,否则不行。推荐admaiN@1234类型的密码。设置完密码,点击属性,将禁用解开。6.如果虚拟机的windows不是专业版,可

  10. ruby - Ruby 程序的持久哈希表? - 2

    我的Ruby脚本需要一个小型非结构化数据库。不是Sqlite,更像是持久哈希表的东西可以完美地工作,只要它可以存储基本的Ruby结构(数组、字符串、哈希等-都是可序列化的)并且不会在Ruby脚本崩溃时被破坏。我知道有很多类似Perl和Tie::Hash的解决方案,所以可能有一些类似Ruby的gem。那会是什么gem?编辑:据我所知,PStore和yaml解决方案是基于每次更改时读取、解码、重新编码和写入整个数据库。这不仅需要所有这些都适合内存,而且是O(n^2)。所以它们似乎都不是特别好的解决方案。 最佳答案 有PStore在Rub

随机推荐