​概述


本文提供Redis持久化技术说明,  建议所有Redis用户阅读. 如果您想更深入了解Redis持久性原理机制和底层持久性保证, 请参考文章 揭秘Redis持久化: http://antirez.com/post/redis-persistence-demystified.html

Redis持久化


Redis提供了不同级别的持久化选项:

  • RDB模式, Redis数据库备份文件(Redis Database Backup)持久化方式, 提供周期性基于时间点的数据集快照备份, 比如每小时生成一个快照备份.

  • AOF模式, 仅追加到文件(AppendOnlyFile)持久化方式, 在每次数据库服务收到写操作时记录日志文件, 当服务重启时, 自动回放该日志来重建原始数据集. 日志中使用Redis自己的协议, 并按照统一的格式, 采用只追加的方法记录. 当日志文件太大时, Redis可以在后台重写该日志, 生成一个最小化版本的日志文件.

  • 你也可以完全禁用持久化, 比如只要保证服务在运行中有数据或可以自动生成缓存数据即可.

  • 你还可以在同一个Redis实例上结合AOF和RDB两种持久化方式. 请注意: 这种方式在Redis重启时, AOF文件会被用来重建原始数据集, 因为, 相对RDB周期快照的方式, AOF被认为是更完整的数据备份, 比如它可以做到准实时备份(只丢失1秒的数据).

接下来, 让我们来对比RDB和AOF的优缺点:

RDB优点


  • RDB采用一个压缩单文件来表示基于时间点的Redis数据, RDB文件是完美的备份. 例如, 你可以保留过去24小时的每小时的快照备份, 并且保存过去30天, 每天的快照备份, 当数据遇到丢失时, 你可以很方便的从不同的备份粒度(版本)来恢复数据集.

  • RDB用来做灾备恢复非常好, 因为紧凑的单文件非常便于在远端数据中心或者亚马逊S3(对象存储,可以加密)间传输.

  • RDB使Redis性能最大化, 因为Redis父进程只需要启动一个子进程完成快照备份即可, 父进程不执行由备份引起的磁盘I/O

  • 与AOF模式相比, RDB在大数据集的情况下, 数据恢复时, 服务重启速度更快.

RDB缺点


  • 如果你想要在Redis意外停止工作时(比如断电), 最小可能的丢失数据, RDB不是一个好的方案. 你可以在RDB生成的地方, 配置不同的保存点(比如每5分钟,对数据集产生至少100次写操作时,创建一个保存点, 你也可以配置多个保存点策略). 然而, 这样你通常会在每5分钟甚至更长时间间隔才创建RDB快照, 所以当Redis异常停止工作时, 你会丢失最后产生快照时间点到现在的数据.

  • RDB会调用系统fork()方法派生一个子进程来完成数据持久化到硬盘. 如果数据集比较大, Fork()方法会非常耗时, 造成Redis停止为客户端服务, 停止时间可能是上微秒, 如果数据集非常大并且CPU性能不是很好, 停止时间可以达到1秒钟或更多. 在持久化时, AOF也会调用fork()方法, 但是你可以不带任何协商(trade-off), 调整重写日志的频率.

AOF优点


使用AOF持久化程度更高: 你可以配置不同的fsync策略:

  • 不带fsync

  • 每秒钟一次fsync

  • 每次查询的时候fsync

注: fsync(https://man7.org/linux/man-pages/man2/fsync.2.html)是系统方法, 用于将内核态的缓存数据持久化到存储设备, 比如将内存数据写入硬盘

默认使用每秒执行一次fsync的策略, 这种场景下, Redis的写性能也能非常好, 因为fsync运行在一个后台线程, 而主线程会尽力完成写操作. 所以你最多丢失1秒钟的数据.

  • AOF日志是一个只能追加的文件, 所以在断电后, 该文件不会出现查找(seek)或损坏的问题. 即使由于磁盘满或其他原因导致日志中存在只写了一半的命令, 也可以使用redis-check-aof工具轻松修复.

  • Redis会在AOF文件太大的时候, 自动在后台重写日志. 重写十分安全, 重写时, Redis派生一个子进程将大的AOF文件重写为最小可用的数据集日志文件, 此时有写操作时, Redis继续追加到旧的AOF文件的同时也追加到AOF重写缓冲区aof_rewrite_buf, 重写完成时, 新的小AOF文件将合并缓冲区中的新数据, 最后将新的AOF文件重命名为老的AOF文件完成替换操作, 以后的数据将写入新的AOF文件.

  • AOF日志文件以一种容易理解和解析的格式依次记录了所有的操作. 导出一个AOF文件非常容易.  甚至在失误执行了清除命令FLUSHALL(https://redis.io/commands/flushall) , 如果这时候重写操作没有被执行, 你仍然可以通过关闭服务, 删除文件最后的错误命令, 重启Redis完成数据恢复.

AOF缺点


    • 对于相同的数据集, AOF文件一般比RDB文件大.

    • 根据具体的fsync策略, AOF可能比RDB速度慢. 通常默认的每秒fsync策略下, Reids性能也非常高, 如果禁用fsync, 即使在高负载的情况下, AOF的速度应该和RDB一样快. 尽管如此, 在巨大写负载的情况下, RDB提供了更多最大延迟的保证.

    • 在过去, 当执行一些特殊的命令时(比如这里有一个涉及到阻塞的命令BRPOPLPUSH:https://redis.io/commands/brpoplpush), Redis遇到了一些罕见的BUG, 它会导致AOF重建数据时, 数据出现不一致.这些问题非常罕见, 我们进行了单元测试, 自动创建随机复杂的数据集来执行重建测试, 没有出现这些问题.  但是如果使用RDB持久化, 几乎不可能出现这类问题. 为了清楚的说明这一点: AOF类似MySQL或者MongoDB, 采用增量更新现有状态的工作机制, 但是RDB快照是每次从头开始创建, 从概念上来说, RDB更具有鲁棒性(健壮). 但是有以下两点值得注意:

      1. 每次AOF被Redis重写的时候,它会从包含在数据集中的实际数据中从头开始重新创建,使新AOF文件对bug的抵抗力比不重写的, , 一直追加的AOF文件更强.

      2. 在实际使用中, 我们重来没有收到过一个关于AOF文件出错的用户报告.

那我该使用哪个?


通常, 如果你想获得像PostgreSQL那样的数据安全性, 你应该结合RDB和AOF.

如果你非常关心你的数据, 但是允许丢失几分钟的数据, 你可以只使用RDB持久化.

有很多用户只使用AOF, 但是我们不建议那样做, 因为RDB的基于时间点的快照在做数据库备份, 快速重启, 或AOF引擎出现问题时, 非常有用.

注意: 基于这些原因, 在将来(长期计划), 我们最终会统一AOF和RDB为一个持久化模型方案.

下面几节, 我们来举例说明更多, 关于RDB和AOF的细节.

快照


Redis默认保存快照到硬盘上的dump.rdb文件. 你可以配置, 每N分钟, 至少出现了M次数据集改变执行一次快照, 或者手动执行保存 SAVE 或后台保存BGSAVE 命令.

save 60 1000

它是如何工作的?


每当Redis需要保存数据集到磁盘, 会执行下面的任务:

  • Redis forks 派生子进程, 这时候会存在一个父进程和一个子进程.

  • 子进程开始将数据集写到RDB临时文件.

  • 当子进程完成新RDB文件写入后, 会将原来的旧RDB文件替换.

这种方法就是Redis的写即拷语义(copy-on-write)

AOF仅追加文件


快照不是很持久, 如果Redis服务异常停止, 掉电停止, 或者意外执行了kill -9杀掉Redis服务进程, 最后的数据写入将会丢失. 虽然对于有些应用来说这是个小问题, 但对于要求完全持久化的场景, RDB不是一个很好的选择.

appendonly yes

从现在开始, 每当Redis收到一个改变数据集的命令(比如SET), 该操作将追加到AOF文件, 当你重启Redis时, 会基于AOF文件重建数据集.

日志重写


AOF文件大小随着操作的增加而增加. 举个例子, 如果你想递增计数100次, 最终数据集中只包含一个键值就是最终的结果, 但是在AOF文件中有100条记录, 实际上在重建数据集时, 不需要剩余的99次记录.

所以Redis支持这个有趣的功能: 在不中断Redis服务的情况下, 后台进行AOF文件重写. 当执行后台重写命令 BGREWRITEAOF 时, Reids会将当前内存中的数据集以最短的有序命令集写下来. 如果你使用Redis2.2, 你需要定时执行 BGREWRITEAOF(https://redis.io/commands/bgrewriteaof) , 从Redis2.4开始, 它可以自动触发日志重写(更多信息可以查看2.4的配置示例, 不同版本的配置(https://redis.io/topics/config)).

AOF怎么持久化?


你可以配置时间间隔, Redis来执行fsync到磁盘. 这里有三个策略:

  • appendfsync always: 每个新的命令追加到AOF文件时执行fsync. 非常慢, 但是非常安全. 注意, 如果追加的命令来自多个客户端或管道的批量命令, 在发送响应之前, 这会被当做一次写操作, 只会执行一次fsync.

  • appendfsync everysec:  每秒执行一次fsync. 速度足够快(在Redis2.4版本中, 与RDB快照的速度一样快), 如果出现意外, 你最多丢失1秒的数据.

  • appendfsync no: 从不执行 fsync, 只把数据交给操作系统. 这虽然更快, 但是更不安全. 这种配置, 通常Linux会每30秒刷新一次数据到硬盘, 但实际时间可以通过内核配置调优.

每秒执行一次fsync是建议并且是默认的方式. 它既快又安全. appendfsync always策略在实践中非常慢, 但是支持组提交, 所以可以将多个并行写操作合并, 执行一次fsync即可.

如果AOF文件被截断了应该怎么做?


在写AOF文件时, 服务器出现crash或磁盘空间满了, 这时候AOF依然包含一致的数据, 代表了给定时间点版本的数据集(默认fsync策略可能会丢失1秒的数据), 但是最后的命令在AOF记录中会被截断, 最新的Redis主干版本依然会导入所有的AOF文件内容, 但是会忽略最后的不完整的命令, 这时候, 服务器会发出警告日志:

* Reading RDB preamble from AOF file...* Reading the remaining AOF tail...# !!! Warning: short read while loading the AOF file !!!# !!! Truncating the AOF at offset 439 !!!# AOF loaded anyway because aof-load-truncated is enabled

你可以改变默认配置来强制停止这种事情发生, 但是默认配置会忽略最后这个不完整的命令, 为了保证服务重启后可用.

老版本的Redis不会自动恢复, 需要做以下步骤来恢复:

  • 对AOF文件进行备份.

  • 使用Redis提供的工具redis-check-aof 修复该AOF文件:

    $ redis-check-aof --fix

  • 可以执行 diff -u 检查两个AOF文件的差异, 确认错误被修复.

  • 用修复后的AOF文件重启Redis服务, 重建数据集.

AOF文件被损坏了怎么办?


如果AOF文件不仅被截断了, 中间还被插入了无效的字节, 事情将变得更加复杂, Redis在启动的时候会中断并提示:

* Reading the remaining AOF tail...
# Bad file format reading the append only file: make a backup of your AOF file, then use ./redis-check-aof --fix <filename>

最好是用 redis-check-aof 工具修复, 首先不适用 --fix 选项, 找到问题, 跳过该文件的错误位置, 查看是否可以手动修复该文件, AOF使用与Reids一致的协议格式,所以非常便于手动修复,  否则就使用工具修复该文件, 这种情况, 从无效的位置到文件结束的数据都可能被丢失, 如果损坏位置发生在开头的位置, 则相当于丢失整个数据集.

它是怎样工作的?


日志重写使用了与快照一致的拷贝即写(copy-on-write)的方式, 步骤如下:

  • Redis执行 forks派生, 这样就有一个主进程和一个子进程.

  • 子进程开始写入一个新的AOF到零时文件中.

  • Redis继续追加到旧的AOF文件的同时也追加到AOF重写缓冲区aof_rewrite_buf, 所以即使重新失败, 也是数据安全的.

  • 当子进程完成了AOF文件重写, 父进程收到一个完成信号, 将缓存中的数据追加到新的AOF文件.

  • 最后将新的AOF文件重命名为老的AOF文件完成替换操作, 以后的数据将写入新的AOF文件.

怎样从dump.rdb快照切换到AOF


在Redis2.0和Redis2.2用不同的步骤来切换到AOF, 而且Redis2.2切换到AOF更简单, 不需要重启.

Redis >= 2.2

  • 将最近的dump.rdb文件备份.

  • 将备份文件传输到安全的地方.

  • 执行以下两个命令:

    • redis-cli config set save ""  #取消RDB

    • redis-cli config set appendonly yes  #开启AOF

  • 检查确认数据库中的键个数没有丢失.

  • 检查写操作都正确的追加进了AOF文件.

第一个配置命令表示启用AOF功能. 这样Redis会阻塞来生成初始的备份, 然后打开新文件来写入操作记录, 后面的写操作将会持续追加到该AOF文件中.

第二个配置命令用来关闭RDB快照持久化. 这是可选的, 如果保留save表示同时使用RDB和AOF持久化.

重要: 记住同时修改redis.conf配置文件来打开AOF, 否则服务重启时将使用原来的配置.

Redis 2.0

  • 将最近的dump.rdb文件备份.

  • 将备份文件传输到安全的地方.

  • 停止所有写操作.

  • 执行后台重写AOF命令redis-cli BGREWRITEAOF. 该操作会创建AOF文件.

  • 当AOF备份完成后, 停止Redis服务.

  • 编辑redis.conf, 启用AOF功能.

  • 重启服务

  • 检查确认数据库中的键个数没有丢失.

  • 检查写操作都正确的追加进了AOF文件.

在AOF和RDB之间交互


Redis >= 2.4会保证当RDB快照在运行时, 避免触发一个AOF重写进程, 或者当AOF重写已经运行时, 不允许后台保存快照BGSAVE. 这可以防止两个后台进程同时产生高负载的磁盘I/O.

 

备份Redis数据


开始本节内容前, 请确认已经对数据库进行备份, 如果磁盘损坏, 云实例消失等, 没有备份意味着数据面临着巨大风险, 会消失在"黑洞" /dev/null中.

Redis对于数据备份非常友好, 即使数据库数据库运行中也允许你对数据进行拷贝备份: RDB文件产生时就不会被修改, 快照备份期间, 它会生成零时的文件, 当快照最终备份完成后采用重命名替换原来的RDB文件.

这意味着服务在运行时, 拷贝RDB文件是非常安全的, 下面是我们的建议:

  • 在服务器上, 创建定时任务CronJob, 每小时执行一次RDB快照, 保存到一个目录,  并且在另外一个目录下保存每日快照.

  • 每次定时任务执行时, 确认使用find命令查找最旧的快照, 将它们删除, 对于每小时快照, 你可以保留最近48小时, 对于每天快照, 你可以保留1~2个月. 并确包快照名包含时间信息.

  • 每天至少做一次数据转存, 比如将RDB快照转存到其他数据中心, 或者至少从当前Redis服务物理机转存到其他地方.

如果你使用ROF持久化方式, 仍然可以拷贝AOF文件来做备份. 这个AOF文件即使丢失最后一小段数据, Redis也可以重建它们(请参考上面的截断AOF文件处理方式)

灾难恢复


灾难恢复和备份基本是一致的, 加上可以在许多不同的数据中心间转存这些备份数据. 这种情况下, 即使影响到最主要的数据中心, 其他地方的备份也是安全并且可以恢复的.

针对刚起步, 没有太多的资金来做大型备份, 这里也提供了一些不需要太大开销的灾备恢复技术:

  • AmazonS3对象存储或其他类似服务是一个实现灾备恢复系统的好方法. 只需将每小时或每日的RDB快照加密后传输到S3即可, 你可以使用gpg -c(使用对称加密模式)对数据加密. 请确认将密码保存到不同的安全的地方(比如拷贝一份交给最重要的人来管理). 建议使用多种存储服务来提高数据安全性.

  • 使用SCP(SSH的一部分)命令来将数据转存到其他服务器. 这是一个简单而且安全的方法: 在云端, 获取远离当前Redis服务的一个小型虚拟专用服务器VPS, 在数据端, 安装ssh, 生成不带密码的ssh客户端密钥, 将它添加到VPS的authorized_keys文件, 这样就可以继续实现自动免密转存备份数据到VPS, 为了提高数据安全, 可以使用不同运营商, 不同网络区域的VPS.

这种方式可能会导致文件传输失败, 所以在传输完成后, 至少要增加文件完整性校验, 比如校验文件大小, 如果使用VPS, 甚至可以使用SHA1校验.

你也需要部署独立的监控报警系统, 对备份过程进行监控, 在备份失败时能及时发现并修复.

参考文档


Redis官方文档: https://redis.io/topics/persistence


END已结束

欢迎大家留言, 订阅, 交流哦!


往期回顾


Golang GinWeb框架9-编译模板/自定义结构体绑定/http2/操作Cookie/完结

Golang GinWeb框架8-重定向/自定义中间件/认证/HTTPS支持/优雅重启等

Golang GinWeb框架7-静态文件/模板渲染

Golang GinWeb框架6-XML/JSON/YAML/ProtoBuf等渲染

Golang GinWeb框架5-绑定请求字符串/URI/请求头/复选框/表单类型

Golang GinWeb框架4-请求参数绑定和验证

Golang GinWeb框架3-自定义日志格式和输出方式/启禁日志颜色

Golang GinWeb框架2-文件上传/程序panic崩溃后自定义处理方式

Golang GinWeb框架-快速入门/参数解析

Golang与亚马逊对象存储服务AmazonS3快速入门

Golang+Vue实现Websocket全双工通信入门

GolangWeb编程之控制器方法HandlerFunc与中间件Middleware

Golang连接MySQL执行查询并解析-告别结构体

Golang的一种发布订阅模式实现

Golang 并发数据冲突检测器(Data Race Detector)与并发安全

Golang"驱动"MongoDB-快速入门("快码加鞭")

[翻译自官方]什么是RDB和AOF? 一文了解Redis持久化!的更多相关文章

  1. redis持久化的方式RDB 和 AOF

    redis持久化的方式RDB 和 AOF 一.对Redis持久化的探讨与理解 目前Redis持久化的方式有两种: RDB 和 AOF 首先,我们应该明确持久化的数据有什么用,答案是用于重启后的数据恢复 ...

  2. 详解Redis持久化(RDB和AOF)

    详解Redis持久化(RDB和AOF) 什么是Redis持久化? Redis读写速度快.性能优越是因为它将所有数据存在了内存中,然而,当Redis进程退出或重启后,所有数据就会丢失.所以我们希望Red ...

  3. Redis持久化之rdb&aof

    Redis有两种持久化的方式:快照(RDB文件)和追加式文件(AOF文件) RDB持久化方式是在一个特定的间隔保存某个时间点的一个数据快照. AOF(Append only file)持久化方式则会记 ...

  4. redis基础:redis下载安装与配置,redis数据类型使用,redis常用指令,jedis使用,RDB和AOF持久化

    知识点梳理 课堂讲义 课程计划 1. REDIS 入 门 (了解) (操作)   2. 数据类型 (重点) (操作) (理解) 3. 常用指令   (操作)   4. Jedis (重点) (操作) ...

  5. Redis持久化----RDB和AOF 的区别

    关于Redis说点什么,目前都是使用Redis作为数据缓存,缓存的目标主要是那些需要经常访问的数据,或计算复杂而耗时的数据.缓存的效果就是减少了数据库读的次数,减少了复杂数据的计算次数,从而提高了服务 ...

  6. 搞懂Redis RDB和AOF持久化及工作原理

    前言 因为Redis的数据都储存在内存中,当进程退出时,所有数据都将丢失.为了保证数据安全,Redis支持RDB和AOF两种持久化机制有效避免数据丢失问题.RDB可以看作在某一时刻Redis的快照(s ...

  7. 11、Redis的持久化(RDB、AOF)

    写在前面的话:读书破万卷,编码如有神 --------------------------------------------------------------------------------- ...

  8. redis两种持久化方式RDB和AOF

    目录 前言 1. Redis 数据库结构 2. RDB 持久化 2.1. RDB 的创建和载入 2.1.1. 手动触发保存 SAVE 命令 BGSAVE 命令 SAVE 和 BGSAVE 的比较 2. ...

  9. Redis的持久化——RDB和AOF

    推荐阅读 Redis 持久化之RDB和AOF --来自ITDragon龙 Redis Persistence --来自Redis官网文档

随机推荐

  1. 自动化测试之Selenium篇(一):环境搭建

    当前无论找工作或者是实际项目应用,自动化测试扮演着非常重要的角色,今天我们来学习下Selenium的环境搭建 Selenium简述 Selenium是一个强大的开源Web功能测试工具系列 可进行读入测 ...

  2. 《Web接口开发与自动化测试》学习笔记(一)

    一.Django的入门 学习思路:先安装Django,然后在建立一个项目,接着运行这个项目,最后修改一下这个项目的数据,学习一下Django的原理之类的. 1.安装Django $pip instal ...

  3. 5.MVCC

    5 MVCC ​ 全称是Multi-Version Concurrent Control,即多版本并发控制,在MVCC协议下,每个读操作会看到一个一致性的snapshot,并且可以实现非阻塞的读.MV ...

  4. 自定义MFC对话窗口的类名

    默认情况下,MFC对话框的窗口类名为"#32770",如果想自定义窗口类名呢,需要两步: 1.修改rc文件 这一步需要直接编辑rc文件,使用任意记事本工具即可,找到窗口的相关定义, ...

  5. Prometheus监控告警浅析

    前言 最近有个新项目需要搞一套完整的监控告警系统,我们使用了开源监控告警系统Prometheus:其功能强大,可以很方便对其进行扩展,并且可以安装和使用简单:本文首先介绍Prometheus的整个监控 ...

  6. Javascript 参数传递

    又一个基本概念出问题,参数传递都是值传递, var a={x:10} function test(obj){obj=1} test(a) console.log(a) 输出什么,如果你说1,那就错了, ...

  7. Moment.js的常见用法

    Moment.js是一个轻量级的JavaScript时间库,它方便了日常开发中对时间的操作,提高了开发效率.通常是用来获取时间,设置时间,格式化时间,比较时间等操作.  1.获取时间       St ...

  8. read/write系统调用

    /*拷贝文件内容实例read系统调用.write系统调用ssize_t read(int fd, void *buf, size_t count);ssize_t write(int fd, cons ...

  9. Flink处理函数实战之一:深入了解ProcessFunction的状态(Flink-1.10)

    欢迎访问我的GitHub https://github.com/zq2599/blog_demos 内容:所有原创文章分类汇总及配套源码,涉及Java.Docker.Kubernetes.DevOPS ...

  10. Ceph部署的时候修改默认权重

    前言 部署集群的时候权重是默认生成的,这个是根据磁盘大小分配的,我们有的时候需要去修改一下这个默认权重 修改 如果统一的初始值,那么直接添加参数即可 osd_crush_initial_weight ...