AOF持久化
edis 每执行一条写操作命令,就把该命令以追加的方式写入到一个文件里,然后重启 Redis 的时候,先去读取这个文件里的命令,并且执行它,相当于恢复了缓存数据。
redis 默认的持久化是RDB快照的方式,将在下一个篇博客中介绍
AOF持久化开启
appendonly yes
appendfilename "appendonly.aof" //设置AOF持久化的名称
AOF持久化的特点
- 只会记录写操作命令,读操作命令是不会被记录的
- aof文件中存储的命令是按照redis的协议存储的
- redis 是先执行写操作,再将命令追加到aof直至中
好处是
1 避免额外的检查开销。
2 不会阻塞当前写操作命令的执行 但是会阻塞下一条命令的执行
AOF 持久化风险
1 当redis服务器发生宕机,Redis 在还没来得及将命令写入到硬盘时了,这个数据就会有丢失的风险。
2. 不会阻塞当前写操作命令的执行 但是会阻塞下一条命令的执行
因为将命令写入到日志的这个操作也是在主进程完成的(执行命令也是在主进程),也就是说这两个操作是同步的
如果在将日志内容写入到硬盘时,服务器的硬盘的 I/O 压力太大,就会导致写硬盘的速度很慢,进而阻塞住了,也就会导致后续的命令无法执行。
认真分析一下,其实这两个风险都有一个共性,都跟「 AOF 日志写回硬盘的时机」有关。
三种redis回写策略
aof持久化的具体流程
- Redis 执行完写操作命令后,会将命令追加到 server.aof_buf 缓冲区;
- 然后通过 write() 系统调用,将 aof_buf 缓冲区的数据写入到 AOF 文件,此时数据并没有写入到硬盘,而是拷贝到了内核缓冲区page cache,等待内核将数据写入硬盘;
- 具体内核缓冲区的数据什么时候写入到硬盘,由内核决定。
redis提供的三种回写策略都是决定 着内存缓冲区的数据什么时候写入磁盘
always
译为总是,也就是说每次写操作命令执行完后,同步将 AOF 日志数据写回硬盘;
Everysec
这个单词的意思是「每秒」,所以它的意思是每次写操作命令执行完后,先将命令写入到 AOF 文件的内核缓冲区,然后每隔一秒将缓冲区里的内容写回到硬盘;
NO
味着不由 Redis 控制写回硬盘的时机,转交给操作系统控制写回的时机,也就是每次写操作命令执行完后,先将命令写入到 AOF 文件的内核缓冲区,再由操作系统决定何时将缓冲区内容写回硬盘
以上三种回写策略都无法完美的解决主进程阻塞和减少数据丢失的问题。 因为着两种问题数据 鱼和熊掌,都是不可兼得的。
主线程阻塞属于性能问题,减少数据丢失属于安全问题,偏向于一边的话,就会要牺牲另外一边
- Always 策略的话,可以最大程度保证数据不丢失,但是由于它每执行一条写操作命令就同步将 AOF
内容写回硬盘,所以是不可避免会影响主进程的性能; - No 策略的话,是交由操作系统来决定何时将 AOF 日志内容写回硬盘,相比于 Always
策略性能较好,但是操作系统写回硬盘的时机是不可预知的,如果 AOF 日志内容没有写回硬盘,一旦服务器宕机,就会丢失不定数量的数据。 - Everysec 策略的话,是折中的一种方式,避免了 Always 策略的性能开销,也比 No
策略更能避免数据丢失,当然如果上一秒的写操作命令日志没有写回到硬盘,发生了宕机,这一秒内的数据自然也会丢失。
这三种策略都是再控制 fsync()函数的调用时机。
fsync()就是将内核中的缓冲区的数据刷到磁盘中
-
Always 策略就是每次写入 AOF 文件数据后,就执行 fsync() 函数;
-
Everysec 策略就会创建一个异步任务来执行 fsync() 函数;
-
No 策略就是永不执行 fsync() 函数;
AOF 缺点
- AOF 日志是一个文件,随着执行的写操作命令越来越多,文件的大小会越来越大
- 如果当 AOF 日志文件过大就会带来性能问题,比如重启 Redis 后,需要读 AOF 文件的内容以恢复数据,如果文件过大,整个恢复的过程就会很慢。