第四章 AOF日志:宕机了,Redis如何避免数据丢失?

第四章 AOF日志:宕机了,Redis如何避免数据丢失?

目前,Redis 的持久化主要有两大机制,即 AOF(Append Only File)日志和 RDB 快照。

AOF 日志是如何实现的 ?

AOF日志:Redis先执行命令,把数据写入内存,再记录日志

第四章 AOF日志:宕机了,Redis如何避免数据丢失?_第1张图片

AOF 为什么要先执行命令再记录日志呢 ?

AOF 里记录的是 Redis 收到的每一条命令,这些命令是以文本形式保存的。

举例:redis执行 “set testkey testvalue” 命令:

第四章 AOF日志:宕机了,Redis如何避免数据丢失?_第2张图片

  • 其中,“*3”表示当前命令有三个部分,每部分都是由“$+数字”开头,后面紧跟着具体的命令、键或值。
  • “数字”表示这部分中的命令、键或值一共有多少字节。
  • 例如,“$3 set”表示这部分有 3 个字节,也就是“set”命令。

为了避免额外的检查开销,Redis 在向 AOF 里面记录日志的时候,并不会先去对这些命令进行语法检查。

  • 不会对命令进行语法检查
  • 只记录执行成功的命令
  • 执行完之后再记录,不会阻塞当前的写操作
  • 先执行命令,就可以保证命令语法的正确性,因此可以通过先执行后写入来减少检查日志正确性的损耗

使用AOF的好处

  • 省去了检查命令正确性的步骤,只有正确执行了的命令才会被写入日志
  • 在命令执行后才记录日志,所以不会阻塞当前写操作

使用AOF的风险

  • 如果刚执行完一个命令,还没有来得及记日志就宕机了,那么这个命令和相应的数据就有丢失的风险。
  • AOF 日志也是在主线程中执行的,如果在把日志文件写入磁盘时,磁盘写压力大,就会导致写盘很慢,进而导致后续的操作也无法执行了。

AOF 三种写回策略

AOF 配置项 appendfsync 的三个可选值:

  • Always,同步写回:每个写命令执行完,立马同步地将日志写回磁盘;
  • Everysec,每秒写回:每个写命令执行完,只是先把日志写到 AOF 文件的内存缓冲区,每隔一秒把缓冲区中的内容写入磁盘;
  • No,操作系统控制的写回:每个写命令执行完,只是先把日志写到 AOF 文件的内存缓冲区,由操作系统决定何时将缓冲区内容写回磁盘。

三种AOF写回策略优缺点

第四章 AOF日志:宕机了,Redis如何避免数据丢失?_第3张图片

  • 想要获得高性能,就选择 No 策略;
  • 如果想要得到高可靠性保证,就选择 Always 策略;
  • 如果允许数据有一点丢失,又希望性能别受太大影响的话,那么就选择 Everysec 策略。

AOF日志文件过大带来的性能问题

  • 操作系统对文件大小有限制,超过则无法继续写入
  • 文件太大,写入的效率也会变低
  • 文件太大,恢复数据也很耗时

AOF 重写机制

什么是AOF重写机制 ?

  • Redis 可以在 AOF 文件体积变得过大时,自动地在后台对 AOF 进行重写 。
  • 对过大的AOF文件进行重写,以此来压缩AOF文件的大小。
  • 具体的实现是:检查当前键值数据库中的键值对,记录键值对的最终状态。
  • 从而实现对某个键值对重复操作后产生的多条操作记录压缩成一条的效果。进而实现压缩AOF文件的大小。

AOF重写日志和AOF日志不是同一个文件,为什么 ?

  • 父子进程写同一个文件必然会产生竞争问题,控制竞争就意味着会影响父进程的性能。
  • 如果AOF重写过程中失败了,那么原本的AOF文件相当于被污染了,无法做恢复使用。
  • 所以 Redis AOF 重写一个新文件,重写失败的话,直接删除这个文件就好了,不会对原先的AOF文件产生影响。等重写完成之后,直接替换旧文件即可。

为什么重写机制可以把日志文件变小呢 ?

  • 旧日志文件中的多条命令,在重写后的新日志中变成了一条命令。
  • AOF 文件是以追加的方式,逐一记录接收到的写命令的。
  • 当一个键值对被多条写命令反复修改时,AOF 文件会记录相应的多条命令。
  • 但是,在重写的时候,是根据这个键值对当前的最新状态,为它生成对应的写入命令。
  • 这样一来,一个键值对在重写日志中只用一条命令就行了,而且,在日志恢复时,只用执行这条命令,就可以直接完成这个键值对的写入了。

第四章 AOF日志:宕机了,Redis如何避免数据丢失?_第4张图片

  • 注意:AOF的重写不是根据原有的AOF去做,而是根据当前内存数据库的数据,去生成一条条命令进行保存。

什么时候会触发AOF 重写呢 ?

  • auto-aof-rewrite-min-size: 表示运行AOF重写时文件的最小大小,默认为64MB
  • auto-aof-rewrite-percentage: 这个值的计算方法是:当前AOF文件大小和上一次重写后AOF文件大小的差值,再除以上一次重写后AOF文件大小。
    • 也就是当前AOF文件比上一次重写后AOF文件的增量大小,和上一次重写后AOF文件大小的比值。
  • AOF文件大小同时超出上面这两个配置项时,会触发AOF重写。

AOF 重写会阻塞吗 ?

和 AOF 日志由主线程写回不同,重写过程是由后台子进程 bgrewriteaof 来完成的,这也是为了避免阻塞主线程,导致数据库性能下降。

重写的过程可以总结为 “一个拷贝,两处日志”

“一个拷贝”就是指,每次执行重写时,主线程 fork 出后台的 bgrewriteaof 子进程。

需要注意的是:

  • fork子进程时,子进程是会拷贝父进程的页表,即虚实映射关系,而不会拷贝物理内存。
  • 子进程复制了父进程页表,也能共享访问父进程的内存数据了,此时,类似于有了父进程的所有内存数据。

两处日志:

  • 因为主线程未阻塞,仍然可以处理新来的操作。此时,如果有写操作,第一处日志就是指正在使用的 AOF 日志,Redis 会把这个操作写到它的缓冲区。这样一来,即使宕机了,这个 AOF 日志的操作仍然是齐全的,可以用于恢复。
  • 而第二处日志,就是指新的 AOF 重写日志。这个操作也会被写到重写日志的缓冲区。这样,重写日志也不会丢失最新的操作。fork出的子进程指向与父进程相同的内存地址空间,此时子进程就可以执行AOF重写,把内存中的所有数据写入到AOF文件中。但是此时父进程依旧是会有流量写入的,如果父进程操作的是一个已经存在的key,那么这个时候父进程就会真正拷贝这个key对应的内存数据,申请新的内存空间,这样逐渐地,父子进程内存数据开始分离,父子进程逐渐拥有各自独立的内存空间。
    • 当子进程完成重写日志后会向父进程发出一个信号处理函数,然后父进程会将AOF重写缓冲区的所有内容写入到新的重写日志中,并对新的日志进行改名,以覆盖原有的日志。 在整个重写过程中,只有信息处理函数执行时才会对父进程造成阻塞。

AOF 重写也有一个重写日志,为什么它不共享使用 AOF 本身的日志呢 ?

  • AOF重写不复用AOF本身的日志,一个原因是父子进程写同一个文件必然会产生竞争问题,控制竞争就意味着会影响父进程的性能。
  • 二是如果AOF重写过程中失败了,那么原本的AOF文件相当于被污染了,无法做恢复使用。
  • 所以Redis AOF重写一个新文件,重写失败的话,直接删除这个文件就好了,不会对原先的AOF文件产生影响。等重写完成之后,直接替换旧文件即可。

你可能感兴趣的:(Redis高级,aof,redis,aof重写,aof日志,redis持久化)