谈谈 AOF
Append Only File,只追加文件。
AOF 文件存储的是具体的操作命令。
Redis 每执行一条写操作命令,执行完之后,就把该命令追加到 AOF_Buffer 缓冲区中,然后会使用某种写回策略,写回磁盘的AOF文件中。
Redis 重启之后,通过一个伪客户端将 AOF 文件中的命令,发送给 Redis 执行,不就恢复数据了吗?
为什么先执行完命令之后,再写入 AOF 文件呢?有啥好处?
第一个好处,避免额外的检查开销。
因为如果先将写操作命令记录到 AOF 日志里,再执行该命令的话,如果当前的命令语法有问题,那么如果不进行命令语法检查,该错误的命令记录到 AOF 日志里后,Redis 在使用日志恢复数据时,就可能会出错。
而如果先执行写操作命令再记录日志的话,只有在该命令执行成功后,才将命令记录到 AOF 日志里,这样就不用额外的检查开销,保证记录在 AOF 日志里的命令都是可执行并且正确的。
第二个好处,不会阻塞当前写操作命令的执行,因为当写操作命令执行成功后,才会将命令记录到 AOF 日志。
Redis将 AOF_Buffer中的数据,写回磁盘的策略?
Redis 提供了 3 种写回硬盘的策略,控制的就是上面说的第三步的过程。
在 redis.conf 配置文件中的 appendfsync 配置项可以有以下 3 种参数可填:
● Always,它的意思是每次写操作命令执行完后,同步将 AOF 日志数据写回硬盘;【性能太差了】
● Everysec,它的意思是每次写操作命令执行完后,先将命令写入到 AOF 文件的内核缓冲区,然后每隔一秒将缓冲区里的内容写回到硬盘;【企业中实际使用最多的】【最多丢1s的数据】
● No,每次写操作命令执行完后,先将命令写入到 AOF 文件的内核缓冲区,再由操作系统决定何时将缓冲区内容写回硬盘。【数据不安全】
AOF 重写机制
随着写操作命令不断的追加写入 AOF 文件,AOF 文件的大小会越来越大。
如果当 AOF 日志文件过大就会带来性能问题,比如重启 Redis 后,需要读 AOF 文件的内容以恢复数据,如果文件过大,整个恢复的过程就会很慢。
所以,Redis 为了避免 AOF 文件越写越大,提供了 AOF 重写机制,当 AOF 文件的大小超过所设定的阈值后,Redis 就会启用 AOF 重写机制,来压缩 AOF 文件。
所以,重写机制的妙处在于,尽管某个键值对被多条写命令反复修改,最终也只需要根据这个「键值对」当前的最新状态,然后用一条命令去记录键值对,代替之前记录这个键值对的多条命令,这样就减少了 AOF 文件中的命令数量。最后在重写工作完成后,将新的 AOF 文件覆盖现有的 AOF 文件。【扫描 Redis 数据库中所有键值对,然后为每一个键值对生成一条对应的写操作命令,再将命令记录到新的 AOF 文件中】
这里说一下为什么重写 AOF 的时候,不直接复用现有的 AOF 文件,而是先写到新的 AOF 文件再覆盖过去。
因为如果 AOF 重写过程中失败了,现有的 AOF 文件就会造成污染,可能无法用于恢复使用。
所以 AOF 重写过程,先重写到新的 AOF 文件,重写失败的话,就直接删除这个文件就好,不会对现有的 AOF 文件造成影响。
重写 AOF 过程是由子进程来完成的,在重写过程中,如果又有新的写操作命令,那么新重写的AOF文件中的数据就与Redis内存中的数据不一致了,怎么办?
子进程执行 AOF 重写期间,主进程需要执行以下三个工作:
● 执行客户端发来的命令;
● 将执行后的写命令追加到 「AOF 缓冲区」;
● 将执行后的写命令追加到 「AOF 重写缓冲区」;
子进程重写完之后,发一个信号给主进程,告诉主进程我重写好了,主进程收到信号后,会将 AOF 重写缓冲区中的命令追加到新重写的 AOF 文件中(会阻塞主进程),然后将新的 AOF 文件改名,覆盖旧的 AOF 文件。
在整个 AOF 后台重写过程中,除了fork 主进程,发生写时复制会对主进程造成阻塞,还有信号处理函数执行时也会对主进程造成阻塞,在其他时候,AOF 后台重写都不会阻塞主进程。
Redis重启恢复数据的时候,优先加载AOF文件,因为AOF文件更能保证数据的完整性。
在 Redis 恢复数据时, RDB 恢复数据的效率会比 AOF 高,因为直接将 RDB 文件读入内存就可以,不需要像 AOF 那样还需要额外执行操作命令的步骤才能恢复数据。
用 AOF 日志的方式来恢复数据其实是很慢的,因为 Redis 执行命令由单线程负责的,而 AOF 日志恢复数据的方式是顺序执行日志里的每一条命令,如果 AOF 日志很大,这个「重放」的过程就会很慢了
重写AOF的时候,如果有 bgsave 命令,是不会执行的。
所以说,不要在业务繁忙的时候,进行AOF文件的重写。