redis持久化
1.redis持久化介绍
我们知道redis性能之所以强悍,是因为redis在运行时将数据都存放在了访问效率远高于硬盘的内存之中。可是这带来了新的问题:在redis或者外部系统重启时,内存中的数据将会丢失,由于目前的内存介质RAM是易失的,非正常的断电也会导致数据的丢失。
在一些场合下我们会希望redis能够将内存中的数据永久性的保存起来。
例如:
1.redis作为数据库,将数据永久的保存起来。
2.redis作为缓存服务器,不希望出现大量缓存数据同时丢失造成缓存被穿透的"雪崩"现象。
而将redis运行在内存中的数据同步到诸如硬盘之类的永久性存储介质上的过程,我们称之为redis的持久化。redis目前支持两种持久化的方式,RDB和AOF。
2.redis持久化之RDB
RDB(RedisDataBase)是基于快照(snapshoptting)实现的。redis在运行时会在满足一定条件的情况下将运行中的数据同步转储到一个二进制的文件并保存在磁盘中作为副本。
redis会在以下几种情况下进行RDB的持久化,生成快照文件:
2.1 满足配置规则进行自动快照时
redis允许用户在(固定时间间隔M)和(被修改的key的个数N)这两个关键维度上面进行自定义的配置。
在redis的配置文件中默认存在了三个自定义条件:
第一行代表着在每900秒的时间间隔内,存在1个或以上的key被修改过,便会进行异步快照存储;
第二行代表着在每300秒内的时间间隔内,存在10个或以上的key被修改过,便会进行异步快照存储,以此类推。
自定义触发条件可以同时存在N个。但是进行快照的持久化操作在增加了数据安全性的同时也会消耗额外的系统资源,因此不能过于频繁。用户可以根据自己的实际需求,在数据安全性和系统性能之间进行取舍。当配置设置为空字符串时,代表禁用该特性,永不进行自动快照。
2.2 执行手动备份命令时
有时在数据迁移,系统重启等情况下,我们希望能够手动的令redis立即进行快照备份。
redis提供了两个命令来满足这个需求:
1.SAVE命令:
在执行SAVE命令时,redis会执行同步阻塞式的快照操作,此时的redis会阻塞所有的其它请求。如果所要同步的数据量很大,会导致redis长时间的未响应,因此应该避免在生产环境中使用该命令。
2.BGSAVE命令:
在执行BGSAVE(BackGround SAVE)命令时,顾名思义,redis将会在后台进行快照存储。这个操作是异步的,并不会阻塞其它的操作。由于redis是单线程架构,因此异步的快照存储实际上是redis从主进程中fork了一个子进程,这时主进程继续对外提供服务,而子进程则在后台默默的执行快照存储操作,在生成新的备份文件后,会将之前过时的备份文件替换掉。
值得一提的是,由于redis在异步快照操作时使用了操作系统支持的"写时复制"策略,即fork时操作系统并不会立刻为子进程提供一片主进程同等大小的内存区域,而是fork完毕的瞬间,子进程和主进程共享同一片内存区域。当主进程的内存被写入新数据时,逐步分配新的内存区域给主进程用于存储新数据,这大大的提高了内存的利用效率。但是如果在子进程备份期间主进程写入数据过多,由于系统必须同时维护主进程的实时数据和备份子进程中需要备份的旧数据,可能会导致redis使用比较多的内存,因此最好能预见到这种糟糕的情况,事先允许redis申请足够多的内存空间,以避免内存溢出。
2.3 执行FLUSHALL命令时
执行FLUSHALL命令时,redis会将所有的数据都清除,此时如果开启了自动快照功能,则无论清除操作执行之后是否满足自定义条件,redis都默认会进行一次RDB快照,生成一个空的备份文件。如果未开启自动快照功能,则FLUSHALL不会触发快照操作。
2.4 主从模式复制时
当redis在进行主从复制时,会默认进行一次快照操作,这么做的原因会在后面主从复制的文章中介绍。
RDB总结:
上面简单的介绍了redis进行RDB持久化的几种方式,对于RDB还有很多细节例如rdbcompression(生成的备份文件是否需要压缩--->"压缩文件占据更小的磁盘空间但是在压缩和解压缩时需要耗费更多的CPU资源")、stop-writes-on-bgsave-error(BGSAVE时如果出错,是否允许继续写入新数据)等等,在这里就不再展开。
仔细观察和思考可以发现,对于设置2.1自定义触发条件的方式,一旦出现系统故障,最近一次RDB快照操作到故障发生时这段时间内的全部更新记录都会丢失,由于生成快照过于频繁会使得redis的可用性大幅度下降,因此无法实时的生成快照。至于2.2、2.3、2.4这些几乎不常用的操作更是无法满足要求。虽然实际可能只会丢失少量的数据(一个时间间隔内的所有更新操作),可是依然存在一些对数据正确性要求很高的场合,用RDB进行持久化的方式是无法满足的。这便引出了我们接下来要介绍的redis的AOF持久化方式。
3.redis持久化之AOF
当需要使用redis存储一些非临时性的,重要的数据时,推荐使用redis的AOF持久化方式。AOF(AppendOnlyFile)和RDB不同,RDB关注实时数据,通过备份实时的数据快照来达到数据的持久化,而AOF则是关注所执行的更新命令(造成数据变化的命令,查询排序之类的不算),通过实时的保存redis执行的每一条更新命令,生成细粒度的更新命令日志来达到数据持久化的目的。由于每一次更新命令都会被记录下来并且保存在硬盘上,这会导致redis的性能降低,使用高性能的硬盘可以尽可能的降低负面影响,使之达到能够被接受的程度。
3.1 重写aof文件
由于对同一个记录的数据更新操作会覆盖之前的数据,这导致了在原始的AOF文件中可能会出现大量的冗余记录,redis提供了重写aof文件的功能,通过剔除掉冗余的记录,可以降低文件占用的磁盘空间。
redis提供了两种重写(rewrite)aof文件的方式:
手动执行 BGREWRITEAOF命令:
执行BGREWRITEAOF时,和BGSAVE一样,redis不希望重写aof文件时阻塞服务,会在后台fork新的进程异步的进行AOF文件的重写。
自动触发:
除了手动触发,redis还提供了自动重写aof文件的功能,配置两个关键参数auto-aof-rewrite-percentage和auto-aof-rewrite-min-size,redis会执行周期性的函数一直监听aof文件的变化,当达到配置好的触发条件时,便会自动的进行aof文件的重写。
redis的配置文件中默认的参数如图所示,第一行代表当AOF文件超过了上一次重写文件的100%时,第二行代表当前aof文件大小是否超过了64M,当redis服务器监听并发现aof文件如果同时满足了上述两个条件,而且此时没有正在进行中的其它持久化操作(rdb和手动rewrite),便会执行自动的rewrite重写操作。
3.2 同步硬盘数据
现代的操作系统为了弥补磁盘和内存之间存取速度的差距,都采用了磁盘缓存技术,缓存数据命中时操作系统优先操作磁盘缓存,操作系统会定期的将磁盘缓存中的数据刷新写入磁盘。这意味着应用程序(例如redis)调用文件写入磁盘的接口后,默认情况下并不会实时的写入磁盘,而必须等待操作系统周期性的同步操作(一般是30s),而在此期间一旦出现系统故障,最新的aof文件变更记录没有被保存下来,数据还是会出现丢失。因此redis提供了一个配置参数appendfsync来帮助用户解决这个问题。
appendfsync有三种可选参数:
1.appendfsync always
代表着每一次的aof文件变更都会强制操作系统实时的写入硬盘,这是最安全的方式,由于这带来了频繁的IO,因此也是性能最差的。
2.appendfsync everysec
这是redis在配置中默认启用的选项,代表着每秒钟强制操作系统进行一次aof文件的硬盘同步,一秒一次的IO比起appendfsync always 每次更新操作都进行IO的方式,在系统繁忙时,性能上有着显著提高。但带来的问题是:如果这一秒内出现系统故障,同步时间间隔之内的更新操作还是会丢失。
3.appendfsync no
redis不主动的进行硬盘同步操作,而是完全交给操作系统来同步,这是运行效率最高的方式。但是由于操作系统的硬盘同步时间间隔相对比较长,因此这是最不安全的方式。
用户可以依据自己对性能和数据安全性上的综合考量来决定同步硬盘数据的方式。
AOF总结:
上面简单的介绍了redis的AOF持久化方式,对于AOF还有很多细节例如aof-load-truncated(当aof文件由于各种原因出现错误,redis启动时初始化内存数据操作是否会被终止)、aof-use-rdb-preamble(是否开启rdm,aof混合初始化)等等,在这里就不再展开。
顺带一提:
当rdb文件和aof文件由于网络故障,系统故障等造成文件损坏时,redis提供了redis-check-rdb和redis-check-aof脚本用于进行对应持久化文件的纠错修复。