写在前面web
参考文章:redis
为何要持久化?为了重启恢复或者故障恢复。缓存
Redis提供了两种持久化方案,分别是RDB和AOF。安全
定时对内存进行快照存储,默认保存为dump.rdb文件,快照完成后替换旧rdb文件。服务器
两个参数:时间和写操做总数,好比:app
save 60 1000
表示:60秒内至少1000次变动则触发快照。异步
分析:根据以上配置,不管写操做有多频繁,最快也是60秒进行一次快照,若是60秒内不超过1000次则不会进行,也就是说若是每分钟的写操做都不超过1000次,那么永远不会进行快照,为了防止该状况,Redis RDB的默认配置以下:async
save 900 1 #900秒内至少有1个key被更改就执行快照 save 300 10 #300内描述至少有10个key被更改就执行快照 save 60 10000 #60秒内至少有10000个key被更改就执行快照
经过配置save 900 1保证15分钟内若是有写操做则最少进行一次快照。svg
append only file,即记录每次写操做到文件中,当须要恢复时,从新执行全部写操做来恢复现场,恢复优先级高于RDB;而且Redis还支持后台重写文件(bgrewrite),即合并一些命令使得文件不至于过大,节约硬盘空间,好比有两次写操做记录到文件中:函数
set a 1 expire a 1000
通过重写后:
setex a 1 1000
Redis的主进程就是一个事件循环(eventLoop),这个循环中处理两个事件,分为是文件事件和事件时间;文件事件负责处理客户端的命令,而时间事件则负责执行像serverCron函数这样须要定时运行的函数。
文件事件处理过程当中,若是执行了写命令,则在执行完命令后,将命令追加到aof_buf缓冲区。
每次结束一个事件循环以前,它都会调用flushAppendOnlyFile函数,考虑是否须要将aof_buf缓冲区中的内容写入和保存到AOF文件里面。
以上过程能够用如下伪代码表示:
def eventLoop(): while True: #处理文件事件 processFileEvents() # 处理时间事件 processTimeEvents() # 考虑是否要将 aof_buf 中的内容写入和保存到 AOF 文件里面 flushAppendOnlyFile()
什么是写入(wirte)和保存(save)?
写入就是调用操做系统的文件写入方法,试图将缓冲区内容写到文件中,可是由于操做系统有写缓冲区,因此Redis进程调用成功,并不表明持久化成功(当写缓冲区满了或者操做系统配置了定时同步策略时到时间了,操做系统会将写缓冲区同步到文件中)。
保存,又称为同步,操做系统提供同步接口以使得进程能够强制要求将写缓冲区同步到文件中。
flushAppendOnlyFile函数
根据【appendfsync】参数执行不一样的逻辑,appendfsync有三种,分别是:
AOF_FSYNC_NO :将缓冲区内容写入文件,可是不进行保存/同步,什么时候同步由操做系统决定。 AOF_FSYNC_EVERYSEC :将缓冲区内容写入文件,若是上次同步距离如今已超过一秒钟,则再次进行同步,这个同步操做由子线程执行。 AOF_FSYNC_ALWAYS :将缓冲区内容写入并同步到AOF文件。
在这种模式下,都由主进程执行,save 只会在如下状况中被执行。
总结:都是这三种状况下都是都会引发redis主进程阻塞。
在这种状况下,save原则上每隔一秒钟都会执行一次,save操做是由子进程执行,因此不会引发服务器主进程阻塞。
注意:实际和原则略有不一样,在这种模式下对fsync 或者fdatasync 的调用并非每秒执行一次,它和调用flushApeendOnlyFile函数时redis 所处的状态有关。
每当flushAppendOnlyFile函数被调用时,可能会出现如下四种状况:
子进程正在执行save:
1.这个save的执行时间未超过2秒,那么程序直接返回,并不执行write 或者新的save。
2.这个save的执行时间已超过2秒,那么执行write,但不执行新的save。由于write的写入必须等待子进程先完成save,所以这里write 会阻塞,即主进程会在这时阻塞,这是为了保护数据,由于若是主进程继续大量执行写操做,而这些写操做极可能是没法被持久化的。
子进程没有在执行save:
3.上次执行成功save距今不超过1秒,那么程序执行write,但不执行save,由于这时还没到save的时间。
4.上次执行成功save距今已超过1秒,那么程序执行write 和save。
总结:若是程序宕机,则那么最多丢失2秒的数据。
在这种模式下,每次执行完一个命令以后,write和save都会被执行。
总结:都由主进程执行的,因此在save 执行期间,主进程会被阻塞,不能处理新的命令请求。
AOF_FSYNC_NO:这种模式下,Redis主进程在每一个事件循环都将缓冲区写入文件,至于什么时候同步则由操做系统决定,这种模式效率最高,可是可能丢失大量数据。
AOF_FSYNC_EVERYSEC:这种模式下,Redis主进程在每一个事件循环都将缓冲区写入文件,并每隔一秒才在子线程中执行同步,性能上这种模式足够快,并且最多丢失2秒的命令数据。
AOF_FSYNC_ALWAYS:这种模式下,Redis主进程在每一个事件循环都将缓冲区写入到AOF文件,而且主动同步AOF文件,性能上是最慢的,可是是最安全的,最多只会丢失一个事件循环中的命令数据。
Redis运行期间,全部写操做都依次被追加到AOF文件中,当文件膨胀到配置的rewrite时则进行重写,主进程fork一个子进程。
主进程继续接受读写命令,可是写命令在rewrite期间不会被追加到AOF文件,而是缓存到【aof_rewrite_buf_block】缓冲区中。
子进程执行rewrite操做,执行成功后替换AOF文件,并通知主进程rewrite完成。
主进程收到子进程rewrite完成通知后,将【aof_rewrite_buf_block】缓冲区中的写操做追加到AOF文件中,而后从新开始第1步。
数据更安全,由于写操做会被依次追加到文件中,相比RDB可能几分钟甚至更久执行一次,AOF策略丢失数据的可能性大大下降。
AOF文件中写操做命令有序,很是易读,便于分析。
恢复更灵活,好比不当心执行了FLUSHALL致使内存数据丢失,若是此时AOF文件没有被重写,则关闭Redis并删除最后的FLUSHALL命令,而后重写Redis便可恢复。
RDB文件仅存储内存中的key value,而且可进行压缩,而AOF需存储操做命令,所以AOF文件经常比RDB文件大的多。
AOF相比RDB更多让主进程阻塞,写入和同步AOF文件都须要等待IO,而RDB仅fork子进程时让主进程阻塞。
Asynchronous AOF fsync is taking too long(disk is busy?).writing the AOF buffer without waiting for fsync to complete. this may slow down Redis.
意思是:异步AOF文件同步耗时很长,磁盘很繁忙么?
在不等待fsync完成的状况下写入AOF缓冲区,这有可能下降Redis的性能。
异步AOF文件同步指的就是【AOF_FSYNC_EVERYSEC】模式,由于这个模式是后台子线程执行fsync操做的。
后面的意思是:文件事件处理方法继续将写命令追加到aof_buf缓冲区,可是write将被阻塞,因此会减速Redis
打开AOF: appendonly yes
AOF同步策略:
appendfsync alway #每次(主动)同步 或者 appendfsync everysec #每秒(主动)同步 或者 appendfsync no #不(主动)同步
auto-aof-rewrite-percentage 100 auto-aof-rewrite-min-size 64mb
当文件增大一倍并且大于64mb时,触发rewrite。