redis宕机了,如何避免数据丢失?(AOF日志)

日常工作中,我们一般会把 redis 当作缓存使用。但是一旦服务器宕机,内存中的数据将会全部丢失。
目前,redis 的持久化主要有两大机制,即 AOF 日志和 RDB 快照。

AOF 日志是如何实现的?

我们比较熟悉的是数据库的写前日志(Write Ahead Log,WAL),就是在实际写数据前,先把修改的数据记到日志文件中,以便故障时进行恢复。但是 AOF 日志刚好相反,它是写后日志,就是 redis 先执行命令,再把数据写入内存,然后才记录到日志。在这里插入图片描述

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

传统的数据库日志,例如 redo log(重做日志),记录的是修改后的数据,而 AOF 记录的是 redis 收到的每一条命令,这些命令是以文本形式保存的。
在这里插入图片描述
比如 “set testkey testvalue”这条命令,AOF 日志中的内容:"*3" 表示当前命令有三个部分,每部分由“$数字”组成,后面跟具体的命令、键或值。比如“$3 set” 表示这部分有3个字节,也就是“set”命令。
但是,为了避免额外的检查开销,redis 在向 AOF 记录日志的时候,并不会先对这些命令进行语法检查,因为如果先记日志再执行命令的话,日志中就有可能记录了错误的命令,redis 在使用日志恢复数据时,就有可能出错。
而写后日志这种方式,就是先让系统执行命令,只有命令执行成功,才会被记录到日志中,否则,系统就会直接向客户端报错。所以,redis 使用写后日志的一大好处是,可以避免出现记录错误命令的情况。此外,AOF另一大好处:它是在命令执行后才记录日志,所以不会阻塞当前的写操作。
不过,AOF 也有两个潜在的风险。
首先,如果刚执行完一个命令,还没有来得及记日志就宕机了,那么这个命令和相应的数据就有丢失的风险。如果此时 redis 是用作缓存,还可以从后端数据库重新读入数据进行恢复,但是如果redis是直接用作数据库的话,此时因为命令没有记入日志,所以就无法用日志进行恢复了。
其次,AOF 虽然避免了对当前命令的阻塞,但可能会给下个操作带来阻塞风险。这是因为,AOF 日志也是在主线程中执行的,如果在把日志文件写入磁盘时,磁盘写压力大,就会导致写盘很慢,进而导致后续的操作也无法执行了。
这两个风险都是和 AOF 写回磁盘的时机相关的。如果能够控制一个写命令执行完后 AOF 日志写回磁盘的时机,这两个风险就解除了。

三种写回策略

AOF 机制提供了三种选择,也是 AOF 配置项 appendfsync 的三个可选值。

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

这三种方式各有优缺点:

  • “同步写回” 可以做到基本不丢数据,但是它在每个写命令后都有一个慢速的落盘操作,不可避免的会影响主线程性能;

  • 虽然“操作系统控制的写回” 在写完缓冲区后,就可以继续执行后续的命令,但是落盘的时机已经不在 redis 手中了,只要 AOF 记录没有写回磁盘,一旦宕机对应的数据就丢失了;

  • “每秒写回” 采用一秒写回一次的频率,避免了 “同步写回” 的性能开销,虽然减少了对系统性能的影响,但是如果宕机,上一秒未落盘的命令操作仍然会丢失。所以,这种方式是在前面两种方式中间取个折中。
    在这里插入图片描述
    根据系统对高性能和高可靠的要求,选择不同的写回策略:想要获得高性能,采用 No 策略;如果想要高可靠,采用 Always 策略;如果允许数据有一点丢失,又希望性能别受太大的影响,可以选择 Everysec 策略。
    但是 AOF 是以文件形式记录接收到的所有写命令。随着命令越来越多,AOF 会越来越大。AOF 文件过大可能会带来的性能问题:

  • 文件系统本身对文件大小有限制,无法保存过大的文件;

  • 文件太大,之后再往里面追加命令记录的话,效率也会变低;

  • 如果宕机,AOF 中记录的命令要一个个被重新执行,用于故障恢复,如果日志文件太大,整个恢复过程中就会非常缓慢,这就会影响到 redis 的正常使用。
    所以,AOF 重写机制就出现了。

AOF 重写机制

AOF 重写机制就是在重写时,redis 根据数据库的现状创建一个新的 AOF 文件,读取数据库中的所有键值对,然后对每一个键值对用一条命令记录它的写入。
为什么重写机制可以把日志文件变小?
实际上,AOF 在重写时,会把旧日志文件中的多条命令,在重写后的新日志中变成一条命令。
AOF 文件是以追加的方式,逐一记录接收到的写命令。当一个键值对被多条写命令反复修改时,AOF 文件会记录相应的多条命令。但是,在重写的时候,是根据这个键值对当前的最新状态,为它生成对应的写入明朗了。这样在重写日志时,只需要一条命令就可以了。而且在日志恢复时,只需要执行这条命令就可以了。例如:
在这里插入图片描述

AOF 重写会阻塞吗?

和 AOF 日志由主线程写回不同,重写过程是由后台子进程 bgrewriteaof 来完成,这也是为了避免阻塞主线程,导致数据库性能下降。
重写过程总结为 “一个拷贝,两处日志”。
“一个拷贝” 就是指,每次执行重写时,主线程 fork 后台 bgrewriteaof 子进程,子进程会拷贝父进程的页表,而不会拷贝物理内存。子进程复制了父进程页表,也能共享访问父进程的内存数据了。然后,子进程就可以在不影响主线程的情况下,逐一把拷贝的数据写出操作命令,记入重写日志。
“两处日志” :
因为主线程未阻塞,仍然可以处理新来的操作。此时,如果有写操作,第一处日志就是指正在使用的 AOF 日志,redis 会把这个操作写到它的缓冲区。这样即使宕机了,这个 AOF 日志的操作仍然是齐全的,可以用于恢复。
第二处日志,就是指新的 AOF 重写日志。这个操作也会被重写到该日志的缓冲区。这样重写日志也不会丢失最新的操作。等到拷贝数据的所有操作记录重写完成后,重写日志记录的这些最新操作也会写入到新的 AOF 文件,以保证数据库的最新状态。这时,我们就可以用新的 AOF 文件替代旧文件了。
在这里插入图片描述
总结:每次 AOF 重写时,redis 会先执行一个内存拷贝,用于重写;然后,使用两个日志保证在重写过程中,新写入的数据不会丢失。而且,因为 redis 采用额外的线程进行数据重写,所以这个过程并不会阻塞主线程。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值