概述本文提供了Redis持久化的技术描述,推荐所有Redis用户阅读。如果想深入了解Redis持久化的原理机制和底层持久化保障,请参考文章揭秘Redis持久化:http://antirez.com/post/redis-persistence-demystified.htmlRedis持久化Redis提供了不同层次持久化选项:RDB模式、Redis数据库备份文件(RedisDatabaseBackup)持久化模式,提供周期性的时间点数据集快照备份,比如每小时生成一次快照备份。AOF模式,只追加到文件(AppendOnlyFile)持久化方式,每次数据库服务收到写操作时记录日志文件,当服务重启时,自动重放日志重建原始数据集。在日志中使用Redis自带的协议,并遵循统一的格式,采用append-only的方式进行记录。当日志文件过大时,Redis可以在后台重写日志,生成最小化版本的日志文件。也可以完全禁用持久化,比如只需要保证服务有数据在运行或者可以自动生成缓存数据。您还可以在同一个Redis实例上组合使用AOF和RDB持久化方法。请注意:这种方式在Redis重启时,会使用AOF文件来重构原始数据集,因为相对于RDB周期快照的方式,AOF被认为是更完整的数据备份,例如,它可以实现准实时备份(仅丢失1秒数据)。接下来我们比较一下RDB和AOF的优缺点:RDB的优点RDB使用压缩的单个文件来表示时间点的Redis数据,RDB文件是完美的备份。例如,您可以保留过去24小时的每小时快照备份,以及保存过去30天的每日快照备份,当数据丢失时,您可以轻松地从不同的备份粒度(版本)恢复数据集。RDB非常适合灾难恢复,因为紧凑的单个文件非常方便在远程数据中心或AmazonS3(对象存储,可以加密)之间传输。RDB最大化了Redis的性能,因为Redis父进程只需要启动一个子进程就可以完成快照备份,而父进程不会通过RDB引起的DiskI/O来执行备份与AOF模式相比,在大的情况下数据集,恢复RDB时服务重启速度更快。当停止工作时(例如电源故障),RDB不是一个尽可能小的数据丢失的好解决方案。可以在RDB产生的地方配置不同的savepoint(比如每5分钟,对数据集至少产生100次写操作,创建一个savepoint时,也可以配置多个savepoint策略)。但是,你通常每5分钟或更长时间创建一次RDB快照,因此当Redis异常停止工作时,你将丢失最后生成快照时间点到现在的数据。RDB会调用系统的fork()方法派生一个子进程来完成数据持久化到硬盘。如果数据集比较大,Fork()方法会非常耗时,导致Redis停止作为客户端Service,停止时间可能是微秒级,如果数据集很大,CPU性能不行很好,停止时间可以达到1秒以上。在持久化时,AOF也会调用fork()方法,但是你可以不经任何协商(trade-off),调整重写日志的频率。AOF的优点使用AOF持久化程度更高:可以配置不同的fsync策略:不带fsyncfsync每秒一次fsync注意每次查询时:fsync(https://man7.org/linux/man-pages/man2/fsync.2.html)是一个系统方法,用于将内核态的缓存数据持久化到存储设备,比如将内存数据写入硬盘默认使用每秒执行一次fsync的策略.在这种场景下,Redis的写性能也可以很好,因为fsync运行在后台线程中,主线程会尽量完成写操作。所以你最多丢失1秒的数据。AOF日志是一个只能追加的文件,所以断电后,该文件不会出现寻道(seek)或损坏。即使由于磁盘满或其他原因导致日志中只有写了一半的命令,也可以使用redis-check-aof工具轻松修复。当AOF文件过大时Redis会在后台自动重写日志。重写是非常安全的。重写时,Redis会派生一个子进程将大的AOF文件重写为最小的可用数据集日志文件。当此时有写操作时,Redis会继续追加到旧的AOF文件中,同时也会追加到AOF重写缓冲区aof_rewrite_buf中。当rewrite完成后,新的小AOF文件会合并到buffernewdata中,最后将新的AOF文件重命名为旧的AOF文件,完成替换操作,以后的数据会写入到新的AOF文件中OF文件。AOF日志文件以一种易于理解和解析的格式顺序记录所有操作。导出AOF文件非常容易。即使误执行了清除命令FLUSHALL(https://redis.io/commands/flushall),如果此时不进行rewrite操作,仍然可以通过关闭服务,删除最后一条错误命令来完成数据恢复的文件,并重新启动Redis。AOF缺点对于相同的数据集,AOF文件一般比RDB文件大。根据具体Withfsync策略,AOF可能比RDB慢。通常,在默认的每秒fsync策略下,Reids的性能也很高。如果禁用fsync,即使在高负载下,AOF也应该和RDB一样快。但是,在写入负载巨大的情况下,RDB提供了更多的最大延迟保证。以前Redis在执行一些特殊的命令时(比如这里涉及阻塞BRPOPLPUSH的命令:https://redis.io/commands/brpoplpush),Redis遇到了一些罕见的BUG,会导致AOF重建数据时数据不一致.这些问题非常罕见。我们进行了单元测试,自动创建随机复杂的数据集来进行重构测试,并没有出现这些问题。但是如果使用RDB持久化,这样的问题几乎不可能出现。为了清楚地说明这一点:AOF类似于MySQL或MongoDB,采用增量更新现有状态的工作机制,但RDB快照每次都是从头开始创建的,从概念上讲,RDB更健壮(robust)。但有两点值得注意:Redis每次重写AOF时,都会从数据集中包含的实际数据重新创建,这样新的AOF文件比没有重写的AOF文件更能抵抗bug,并始终附加。在实际使用中,我们从未收到过单个用户报告AOF文件错误的情况。那么我应该使用哪一个?通常,如果你想像PostgreSQL一样获得数据安全,你应该结合RDB和AOF。如果你非常关心你的数据,但允许数据丢失几分钟,那么你可以直接使用RDB进行持久化。有很多用户只使用AOF,但我们不建议这样做,因为RDB的时间点快照在做数据库备份、快速重启或AOF引擎问题时非常有用。注:由于这些原因,在未来(长长期计划),我们最终会把AOF和RDB统一成一个持久化模型的方案。在接下来的部分中,我们将说明有关RDB和AOF的更多细节。快照Redis默认将快照保存到硬盘的dump.rdb文件中。可以配置,每N分钟,至少有M个数据集变化进行一次快照,或者手动执行保存SAVE或者后台保存BGSAVE命令。save601000是如何工作的?每当Redis需要将数据集保存到磁盘时,它会执行以下任务:Redisforkspawnchildprocesses。这时候就会有一个父进程和一个子进程。子进程开始将数据集写入到RDB临时文件中。当子进程写完新的RDB文件后,会把原来旧的RDB文件写替换。这个方法就是Redis的写时复制语义(copy-on-write)。AOF只是追加文件快照,持久性不是很好。如果Redis服务异常停止,掉电停止,或者不小心执行kill-9杀掉Redis服务进程,最后写入的数据就会丢失。虽然这对于某些应用程序来说是个小问题,但是对于需要完全持久化的场景来说,RDB并不是一个好的选择。appendonlyyes从现在开始,每当Redis收到一个改变数据集的命令(如SET)时,该操作将追加到AOF文件中,当你重启Redis时,数据集将基于AOF文件重建。logrewriteAOF文件的大小随着操作的增加而增加。例如,如果要将count递增100次,最终的数据集只包含一个key值就是最终的结果,但是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到磁盘。这里有3种策略:appendfsyncalways:当每条新命令追加到AOF文件时执行fsync。很慢,但是很安全。注意,如果追加的命令来自多个客户端或者来自管道的批处理命令,在发送响应之前,这将被视为写操作,只会执行一次fsync。appendfsynceverysec:fsync将每秒执行一次。速度足够快(在Redis2.4版本中,与RDB快照一样快),如果发生意外,最多丢失1秒的数据。appendfsyncno:从不执行fsync,只将数据交给操作系统。虽然这样速度更快,但安全性较低。这个配置,通常Linux会每隔30秒刷新一次数据到硬盘,但实际时间可以通过内核配置进行调整。建议每秒执行一次fsync,这是默认设置。它既快速又安全。appendfsyncalways策略在实践中非常慢,但是支持groupcommit,所以可以将多个并行的写操作组合起来,执行一次fsync。AOF文件被截断了怎么办?写入AOF文件时,服务器崩溃或磁盘空间满。此时AOF中仍然包含一致的数据,代表给定时间点的数据集(默认的fsync策略可能会丢失1秒的数据),但是AOF记录中最后一条命令会被截断,最新的Redistrunk版本仍然会导入所有AOF文件内容,但会忽略最后一个不完整的命令,即有时,服务器会发出警告日志:*ReadingRDBpreamblefromAOFfile...*ReadingtheremainingAOFtail...#!!!Warning:shortreadwhileloadingtheAOFfile!!!#!!!TruncatingtheAOFatoffset439!!!#AOFloadedanywaybecauseof-load-truncateddisenabled可以更改默认配置强制停止这种事情发生,但是默认配置会忽略最后一个不完整的命令,以确保服务可用重启后。旧版本的Redis不会自动恢复,恢复需要以下步骤:备份AOF文件。使用Redis提供的工具redis-check-aof修复AOF文件:$redis-checkk-aof--fix可以执行diff-u检查两个AOF文件之间的差异,并确认错误已被修复。使用修复后的AOF文件重启Redis服务,重建数据集。AOF文件损坏了怎么办?如果AOF文件不仅被截断了,而且中间还插入了无效的字节,事情就会变得更加复杂。Redis在启动的时候会中断提示:*ReadingtheremainingAOFtail...#Badfileformatreadingtheappendonlyfile:makeabackupofyourAOFfile,然后用./redis-check-aof--fix
