本文提供Redis持久化技术说明, 建议全部Redis用户阅读. 若是您想更深刻了解Redis持久性原理机制和底层持久性保证, 请参考文章 揭秘Redis持久化: http://antirez.com/post/redis-persistence-demystified.htmlhtml
Redis提供了不一样级别的持久化选项:python
RDB模式, Redis数据库备份文件(Redis Database Backup)持久化方式, 提供周期性基于时间点的数据集快照备份, 好比每小时生成一个快照备份.linux
AOF模式, 仅追加到文件(AppendOnlyFile)持久化方式, 在每次数据库服务收到写操做时记录日志文件, 当服务重启时, 自动回放该日志来重建原始数据集. 日志中使用Redis本身的协议, 并按照统一的格式, 采用只追加的方法记录. 当日志文件太大时, Redis能够在后台重写该日志, 生成一个最小化版本的日志文件.redis
你也能够彻底禁用持久化, 好比只要保证服务在运行中有数据或能够自动生成缓存数据便可.数据库
你还能够在同一个Redis实例上结合AOF和RDB两种持久化方式. 请注意: 这种方式在Redis重启时, AOF文件会被用来重建原始数据集, 由于, 相对RDB周期快照的方式, AOF被认为是更完整的数据备份, 好比它能够作到准实时备份(只丢失1秒的数据).编程
接下来, 让咱们来对比RDB和AOF的优缺点:缓存
RDB采用一个压缩单文件来表示基于时间点的Redis数据, RDB文件是完美的备份. 例如, 你能够保留过去24小时的每小时的快照备份, 而且保存过去30天, 天天的快照备份, 当数据遇到丢失时, 你能够很方便的从不一样的备份粒度(版本)来恢复数据集.安全
RDB用来作灾备恢复很是好, 由于紧凑的单文件很是便于在远端数据中心或者亚马逊S3(对象存储,能够加密)间传输.服务器
RDB使Redis性能最大化, 由于Redis父进程只须要启动一个子进程完成快照备份便可, 父进程不执行由备份引发的磁盘I/O网络
与AOF模式相比, RDB在大数据集的状况下, 数据恢复时, 服务重启速度更快.
若是你想要在Redis意外中止工做时(好比断电), 最小可能的丢失数据, RDB不是一个好的方案. 你能够在RDB生成的地方, 配置不一样的保存点(好比每5分钟,对数据集产生至少100次写操做时,建立一个保存点, 你也能够配置多个保存点策略). 然而, 这样你一般会在每5分钟甚至更长时间间隔才建立RDB快照, 因此当Redis异常中止工做时, 你会丢失最后产生快照时间点到如今的数据.
RDB会调用系统fork()方法派生一个子进程来完成数据持久化到硬盘. 若是数据集比较大, Fork()方法会很是耗时, 形成Redis中止为客户端服务, 中止时间多是上微秒, 若是数据集很是大而且CPU性能不是很好, 中止时间能够达到1秒钟或更多. 在持久化时, AOF也会调用fork()方法, 可是你能够不带任何协商(trade-off), 调整重写日志的频率.
使用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文件通常比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更具备鲁棒性(健壮). 可是有如下两点值得注意:
每次AOF被Redis重写的时候,它会从包含在数据集中的实际数据中从头开始从新建立,使新AOF文件对bug的抵抗力比不重写的, , 一直追加的AOF文件更强.
在实际使用中, 咱们重来没有收到过一个关于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)
快照不是很持久, 若是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)).
你能够配置时间间隔, Redis来执行fsync
到磁盘. 这里有三个策略:
appendfsync always
: 每一个新的命令追加到AOF文件时执行fsync
. 很是慢, 可是很是安全. 注意, 若是追加的命令来自多个客户端或管道的批量命令, 在发送响应以前, 这会被当作一次写操做, 只会执行一次fsync.
appendfsync everysec
: 每秒执行一次fsync
. 速度足够快(在Redis2.4版本中, 与RDB快照的速度同样快), 若是出现意外, 你最多丢失1秒的数据.
appendfsync no
: 从不执行 fsync
, 只把数据交给操做系统. 这虽然更快, 可是更不安全. 这种配置, 一般Linux会每30秒刷新一次数据到硬盘, 但实际时间能够经过内核配置调优.
每秒执行一次fsync
是建议而且是默认的方式. 它既快又安全. appendfsync always
策略在实践中很是慢, 可是支持组提交, 因此能够将多个并行写操做合并, 执行一次fsync
便可.
在写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文件不只被截断了, 中间还被插入了无效的字节, 事情将变得更加复杂, 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文件.
在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文件.
Redis >= 2.4会保证当RDB快照在运行时, 避免触发一个AOF重写进程, 或者当AOF重写已经运行时, 不容许后台保存快照BGSAVE. 这能够防止两个后台进程同时产生高负载的磁盘I/O.
开始本节内容前, 请确认已经对数据库进行备份, 若是磁盘损坏, 云实例消失等, 没有备份意味着数据面临着巨大风险, 会消失在"黑洞" /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框架6-XML/JSON/YAML/ProtoBuf等渲染
Golang GinWeb框架5-绑定请求字符串/URI/请求头/复选框/表单类型
Golang GinWeb框架3-自定义日志格式和输出方式/启禁日志颜色
Golang GinWeb框架2-文件上传/程序panic崩溃后自定义处理方式
GolangWeb编程之控制器方法HandlerFunc与中间件Middleware