写在前面
参考文章:
为什么要持久化?为了重启恢复或者故障恢复。
Redis提供了两种持久化方案,分别是RDB和AOF。
定时对内存进行快照存储,默认保存为dump.rdb文件,快照完成后替换旧rdb文件。
两个参数:时间和写操作总数,比如:
save 60 1000
表示:60秒内至少1000次变更则触发快照。
分析:根据以上配置,无论写操作有多频繁,最快也是60秒进行一次快照,如果60秒内不超过1000次则不会进行,也就是说如果每分钟的写操作都不超过1000次,那么永远不会进行快照,为了防止该情况,Redis RDB的默认配置如下:
save 900 1 #900秒内至少有1个key被更改就执行快照
save 300 10 #300内描述至少有10个key被更改就执行快照
save 60 10000 #60秒内至少有10000个key被更改就执行快照
通过配置save 900 1保证15分钟内如果有写操作则最少进行一次快照。
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。