当前位置: 首页 > 科技观察

什么是RDB和AOF?一篇文章了解Redis持久化!

时间:2023-03-13 12:19:52 科技观察

概述本文提供了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最好用redis-check-aof工具修复。一、--fix选项不适用,找到问题,跳过文件错误位置,检查文件是否可以手动修复。AOF使用与ReidsProtocol格式相同的方法,所以手动修复非常方便,否则使用工具修复文件,在这种情况下,从无效位置到文件末尾的数据可能会丢失,如果damagedposition发生在起始位置,相当于丢失了整个数据它是如何工作的?日志重写采用与快照一致的copy-on-write方式。步骤如下:Redis执行fork,这样就有了一个主进程和一个子进程。进程开始向零时文件写入一个新的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将是最新的转储.rdb文件备份。将备份文件转移到安全的地方。执行以下两条命令:redis-cliconfigsetsave""#取消RDBredis-cliconfigsetappendonlyyes#开启AOF检查数据库中的key数量没有丢失。检查写入操作是否正确附加到AOF文件。第一条配置命令表示开启AOF功能。这样,Redis会阻塞生成初始备份,然后打开一个新的文件写入操作记录,后续的写入操作会继续追加到AOF文件中。第二个配置命令用于关闭RDB快照持久化。这个是可选的,如果保持保存,可以同时使用RDB和AOF持久化。重要提示:记得同时修改redis.conf配置文件开启AOF,否则服务重启时会沿用原来的配置。Redis2.0会备份最新的dump.rdb文件。将备份文件转移到安全的地方。停止所有写操作。执行后台重写AOF命令redis-cliBGREWRITEAOF。此操作将创建一个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文件处理方法)容灾DisasterRecovery和Backup基本一致,这些备份数据可以在很多不同的数据之间dump中心。这样的话,即使最重要的数据中心受到影响,其他地方的备份也是安全的,可以恢复。对于初学者来说,如果你没有多少钱做大型备份,这里有一些不需要太多开销的灾难恢复技术:AmazonS3对象存储或其他类似的服务是实现灾难恢复系统的好方法。只需设置每小时或只需加密每日RDB快照并将其传输到S3。您可以使用gpg-c(使用对称加密模式)来加密数据。请务必将密码保存在不同的安全地方(例如复制一份到最重要的)建议使用多种存储服务,提高数据安全性。使用SCP(SSH的一部分)命令转储数据到其他服务器,这是一个简单安全的方法:在云端,脱离当前Redis服务的小型虚拟专用服务器VPS,在数据端安装ssh,生成无密码的ssh客户端密钥,并添加到VPS的authorized_keys文件中,这样就可以不用密码继续自动将备份数据转储到VPS中,为了提高数据的安全性,可以使用不同运营商、不同网区的VPS,这种方式可能会导致文件丢失传输失败,所以传输完成后,至少要加上文件完整性检查,比如文件大小检查,如果使用VPS,甚至可以使用SHA1验证。还需要部署一个独立的监控报警系统来监控备份pr并在备份失败时及时发现并修复。参考Redis官方文档:https://redis.io/topics/persistence