redis持久化机制

  • Post author:
  • Post category:其他




持久化的原因

很多时候我们需要持久化数据也就是将内存中的数据写入到硬盘里面,大部分原因是为了之后重用数据(比如重启机器、机器故障之后回复数据),或者是为了防止系统故障而将数据备份到一个远程位置。

Redis的数据是存在内存中的,如果Redis发生宕机,那么数据会全部丢失,因此必须提供持久化机制。



持久化方式

  • 快照模式
  • AOF 只追加文件模式

快照模式它可以将存在于某一时刻的所有数据都写入硬盘里面.

AOF模式它会在执行写命令时,将被执行的写命令复制到硬盘里面.


快照是内存数据的二进制序列化形式,而AOF日志记录的是内存数据修改的指令记录文本.即RDB记录的是数据,AOF记录的是指令.



快照(snapshotting)持久化



概念


Redis可以通过创建快照来获得存储在内存里面的数据在某个时间点上的副本

。Redis创建快照之后,可以对快照进行备份,可以将快照复制到其他服务器从而创建具有相同数据的服务器副本,还可以将快照留在原地以便重启服务器的时候使用.



配置信息

快照持久化是Redis默认采用的持久化方式,在redis.conf配置文件中默认有此下配置:

save 900 1           #在900秒(15分钟)之后,如果至少有1个key发生变化,Redis就会自动触发BGSAVE命令创建快照。

save 300 10          #在300秒(5分钟)之后,如果至少有10个key发生变化,Redis就会自动触发BGSAVE命令创建快照。

save 60 10000        #在60秒(1分钟)之后,如果至少有10000个key发生变化,Redis就会自动触发BGSAVE命令创建快照。

stop-writes-on-bgsave-error no #创建快照失败后是否仍然继续执行写命令

rdbcompression yes  #是否对快照文件进行压缩

dbfilename dump.rdb #快照文件名称

dir ./   #快照文件保存的位置

根据配置,快照将被写入dbfilename选项指定的文件里面,并存储在dir选项指定的路径上面。


如果在新的快照文件创建完毕之前,Redis、系统或者硬件这三者中的任意一个崩溃了,那么Redis将丢失最近一次创建快照写入的所有数据

举个例子:假设Redis的上一个快照是2:35开始创建的,并且已经创建成功。下午3:06时,Redis又开始创建新的快照,并且在下午3:08快照创建完毕之前,有35个键进行了更新。如果在下午3:06到3:08期间,系统发生了崩溃,导致Redis无法完成新快照的创建工作,那么Redis将丢失下午2:35之后写入的所有数据。另一方面,如果系统恰好在新的快照文件创建完毕之后崩溃,那么Redis将丢失35个键的更新数据。



创建快照的方式

创建快照的办法有如下几种:

  • BGSAVE命令: 客户端向Redis发送 BGSAVE命令 来创建一个快照。对于支持BGSAVE命令的平台来说(基本上所有平台支持,除了Windows平台),

    Redis主线程会fork一个子进程,然后子进程负责将快照写入硬盘,而父进程则继续处理命令请求

  • SAVE命令: 客户端还可以向Redis发送 SAVE命令 来创建一个快照.

    接到SAVE命令的Redis服务器在快照创建完毕之前不会再响应任何其他命令

    。SAVE命令不常用,我们通常只会在没有足够内存去执行BGSAVE命令的情况下,又或者即使等待持久化操作执行完毕也无所谓的情况下,才会使用这个命令。
  • save选项: 如果用户设置了save选项(一般会默认设置),比如 save 60 10000,那么从Redis最近一次创建快照之后开始算起,当“60秒之内有10000次写入”这个条件被满足时,Redis就会自动触发BGSAVE命令。

    如果用户设置了多个save配置选项,那么当任意一个save配置选项所设置的条件被满足时,redis就会触发一次BGSAVE命令
  • SHUTDOWN命令:

    当Redis通过SHUTDOWN命令接收到关闭服务器的请求时,或者接收到标准TERM信号时,会执行一个SAVE命令,阻塞所有客户端,不再执行客户端发送的任何命令,并在SAVE命令执行完毕之后关闭服务器

  • 一个Redis服务器连接到另一个Redis服务器: 当一个Redis服务器连接到另一个Redis服务器,并向对方发送SYNC命令来开始一次复制操作的时候,如果主服务器目前没有执行BGSAVE操作,或者主服务器并非刚刚执行完BGSAVE操作,那么主服务器就会执行BGSAVE命令

在只使用快照持久化来保存数据时,一定要记住:如果系统真的发生崩溃,用户将丢失最近一次生成快照之后更改的所有数据。因此,

快照持久化只适用于即使丢失一部分数据也不会造成一些大问题的应用程序

。不能接受这个缺点的话,可以考虑AOF持久化。



运作方式

  1. redis调用fork(),同时拥有父进程和子进程.
  2. 子进程将数据集写入到一个临时RDB文件中.
  3. 当子进程完成对新RDB文件的写入时,redis用新RDB文件替换原来的RDB文件,并删除旧的RDB文件.



优点

  • 对性能影响最小。Redis在保存RDB快照时会fork出子进程进行接下来的保存工作,父工程无须执行任何磁盘IO,几乎不影响Redis处理客户端请求的效率。

  • 每次快照会生成一个完整的数据快照文件,所以可以辅以其他手段保存多个时间点的快照(例如把每天0点的快照备份至其他存储媒介中),作为非常可靠的灾难恢复手段。

  • 使用RDB文件进行数据恢复比使用AOF要快很多。



缺点

  • 快照是定期生成的,所以在Redis发生故障时时或多或少会丢失一部分数据。

  • 如果数据集非常大且CPU不够强时,Redis在fork子进程时可能会消耗相对较长的时间,此时会造成服务器在一定时间内停止处理客户端请求.



注意事项

随着Redis占用的内存越来越多,BGSAVE在创建子进程时耗费的时间也会越来越多.如果Redis的内存占用量达到数十个GB,并且剩余的空闲内存并不多,或者Redis运行在虚拟机上面,那么执行BGSAVE可能会导致系统长时间地停顿,也可能引发系统大量地使用虚拟内存,从而导致Redis的性能降低至无法使用的程度.


为了防止Redis因为创建子进程而出现停顿,我们可以考虑关闭自动保存,转而通过手动发送BGSAVE或者SAVE来进行持久化

.手动发送BGSAVE一样会引起停顿,唯一不同的是用户可以通过手动发送BGSAVE命令来控制停顿出现的时间.另一方面,虽然SAVE会一直阻塞Redis直到快照生成完毕,但是因为它不用创建子进程,所以就不会像BGSAVE一样因为创建子进程而导致停顿,并且因为没有子进程在争抢资源,所以SAVE创建快照的速度会比BGSAVE命令快一些.



AOF(append-only file)持久化



概念

简单来说,AOF持久化会将被执行的写命令写到AOF文件的末尾,以此来记录数据发生的变化.因此,redis只要从头到尾重新执行一次AOF文件包含的所有写命令,就可以恢复AOF文件所记录的数据集.

与快照持久化相比,AOF持久化 的实时性更好,因此已成为主流的持久化方案。默认情况下Redis没有开启AOF(append only file)方式的持久化,可以通过appendonly参数开启:



配置信息

appendonly yes

开启AOF持久化后每执行一条会更改Redis中的数据的命令,Redis就会将该命令写入硬盘中的AOF文件。AOF文件的保存位置和RDB文件的位置相同,都是通过dir参数设置的,默认的文件名是appendonly.aof。

在Redis的配置文件中存在三种不同的 AOF 持久化方式,它们分别是:

appendfsync always    #每个Redis写命令都要同步写入硬盘,这样会严重降低Redis的速度
appendfsync everysec  #每秒钟同步一次,显示地将多个写命令同步到硬盘
appendfsync no        #让操作系统决定何时进行同步

appendfsync always 可以实现将数据丢失减到最少,不过这种方式需要对硬盘进行

大量的写入而且每次只写入一个命令

,十分影响Redis的速度。另外使用固态硬盘的用户谨慎使用appendfsync always选项,因为这会明显降低固态硬盘的使用寿命。

为了兼顾数据和写入性能,用户可以考虑 appendfsync everysec选项 ,让Redis每秒同步一次AOF文件,Redis性能几乎没受到任何影响。而且这样即使出现系统崩溃,用户最多只会丢失一秒之内产生的数据。当硬盘忙于执行写入操作的时候,Redis还会优雅的放慢自己的速度以便适应硬盘的最大写入速度。

appendfsync no 选项一般不推荐,这种方案当系统崩溃时,会使Redis丢失不定量的数据.另外如果用户的硬盘处理写入操作的速度不够的话,那么当缓冲区被等待写入的数据填满时,Redis的写入操作将被阻塞,这会导致Redis的请求速度变慢。

虽然AOF持久化非常灵活地提供了多种不同的选项来满足不同应用程序对数据安全的不同要求,但AOF持久化也有缺陷——

AOF文件的体积太大



AOF重写


Redis提供了AOF rewrite功能,可以重写AOF文件,只保留能够把数据恢复到最新状态的最小写操作集。

AOF虽然在某个角度可以将数据丢失降低到最小而且对性能影响也很小,但是极端的情况下,体积不断增大的AOF文件很可能会用完硬盘空间。另外,如果AOF体积过大,那么还原操作执行时间就可能会非常长。

为了解决AOF体积过大的问题,用户可以向Redis发送 BGREWRITEAOF命令 ,

这个命令会通过移除AOF文件中的冗余命令来重写(rewrite)AOF文件来减小AOF文件的体积

。BGREWRITEAOF命令和BGSAVE创建快照原理十分相似,所以

AOF文件重写也需要用到子进程,由子进程负责对AOF文件进行重写,这样会导致性能问题和内存占用问题,和快照持久化一样

。更糟糕的是,如果不加以控制的话,AOF文件的体积可能会比快照文件大好几倍。在进行AOF重写并且删除旧AOF文件的时候,删除一个体积达到数十GB大的旧AOF文件可能会导致操作系统挂起数秒.

AOF重写可以产生一个新的AOF文件,这个新的AOF文件和原有的AOF文件所保存的数据库状态一样,但体积更小。

AOF重写是一个有歧义的名字,

该功能是通过读取数据库中的键值对来实现的,程序无须对现有AOF文件进行任何读入、分析或者写入操作。


在执行 BGREWRITEAOF 命令时,Redis 服务器会维护一个 AOF 重写缓冲区,该缓冲区会在子进程创建新AOF文件期间,记录服务器执行的所有写命令。当子进程完成创建新AOF文件的工作之后,服务器会将重写缓冲区中的所有内容追加到新AOF文件的末尾,使得新旧两个AOF文件所保存的数据库状态一致。最后,服务器用新的AOF文件替换旧的AOF文件,以此来完成AOF文件重写操作



AOF持久化配置

auto-aof-rewrite-percentage 100  
auto-aof-rewrite-min-size 64mb

假设用户对Redis设置了如下配置选项并且启用了AOF持久化。那么当AOF文件体积大于64mb,并且AOF的体积比上一次重写之后的体积大了至少一倍(100%)的时候,Redis将执行BGREWRITEAOF命令。



运作方式

  1. Redis执行 fork() ,现在同时拥有父进程和子进程。
  2. 子进程开始将新 AOF 文件的内容写入到临时文件。
  3. 对于所有新执行的写入命令,父进程一边将它们累积到一个内存缓存中,一边将这些改动追加到现有AOF文件的末尾:这样即使在重写的中途发生停机,现有的AOF文件也还是安全的。
  4. 当子进程完成重写工作时,它给父进程发送一个信号,父进程在接收到信号之后,将内存缓存中的所有数据追加到新 AOF 文件的末尾。
  5. 最后Redis用新文件替换旧文件,并重命名,之后所有命令都会直接追加到新 AOF 文件的末尾.



优点

  • 最安全,在启用appendfsync always时,任何已写入的数据都不会丢失,使用在启用appendfsync everysec也至多只会丢失1秒的数据。

  • 由于该机制对日志文件的写入操作采用的是append模式,因此在写入过程中即使出现宕机现象,也不会破坏日志文件中已经存在的内容,即使出现了某条日志只写入了一半的情况,也可以使用redis-check-aof工具轻松修复。

  • AOF文件易读,可修改,在进行了某些错误的数据清除操作后,只要AOF文件没有rewrite,就可以把AOF文件备份出来,把错误的命令删除,然后恢复数据。



缺点

  • AOF文件通常比RDB文件更大

  • 性能消耗比RDB高

  • 数据恢复速度比RDB慢



Redis 4.0 对于持久化机制的优化

Redis 4.0 开始支持 RDB 和 AOF 的混合持久化(默认关闭,可以通过配置项 aof-use-rdb-preamble 开启)。


如果把混合持久化打开,那么程序会优先使用 AOF 文件来恢复数据集,因为AOF文件所保存的数据通常是最完整的


AOF 重写的时候就直接把 RDB 的内容写到 AOF 文件开头。这样做的好处是可以结合 RDB 和 AOF 的优点, 快速加载同时避免丢失过多的数据。当然缺点也是有的, AOF 里面的 RDB 部分是压缩格式不再是 AOF 格式,可读性较差

.



RDB和AOF到底该如何选择?

1.不要仅仅使用 RDB,因为那样会导致你丢失很多数据,因为RDB是隔一段时间来备份数据

2.也不要仅仅使用 AOF,因为那样有两个问题,第一,通过 AOF 做冷备没有RDB恢复速度快; 第二,RDB 每次简单粗暴生成数据快照,更加健壮,可以避免 AOF 这种复杂的备份和恢复机制的 bug

3.用RDB恢复内存状态会丢失很多数据,重放AOF日志又很慢。Redis4.0推出了混合持久化来解决这个问题。将 rdb 文件的内容和增量的 AOF 日志文件存在一起。这里的 AOF 日志不再是全量的日志,而是自持久化开始到持久化结束的这段时间发生的增量 AOF 日志,通常这部分 AOF 日志很小。于是在 Redis 重启的时候,可以先加载 rdb 的内容,然后再重放增量 AOF 日志就可以完全替代之前的 AOF 全量文件重放,重启效率因此大幅得到提升。

无论是AOF持久化还是快照持久化,将数据持久化到硬盘上都是非常有必要的,但除了进行持久化外,用户还必须对持久化得到的文件进行备份(最好是备份到不同的地方),这样才能尽量避免数据丢失事故发生。如果条件允许的话,最好能将快照文件和重新重写的AOF文件备份到不同的服务器上面。



版权声明:本文为kaihuishang666原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。