Java教程

AOF日志与RDB快照

本文主要是介绍AOF日志与RDB快照,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

一:概述

我们知道 Redis 会当作缓存使用,因为Redis是内存数据库,它把后端数据库中的数据存储在内存中,然后直接从内存中读取数据,响应速度非常快。但是一旦服务器宕机,内存中的数据将全部丢失。所以对于 Redis 来说,实现数据的持久化是至关重要的。
目前 Redis 的持久化主要有两大机制:AOF日志RDB快照。

二:AOF 日志

首先我们来了解一下什么是写前日志(Write Ahead Log,WAL),也就是在实际写数据之前,先把修改的数据记录到日志文件中,以便故障时恢复。而 AOF(Append Only File) 日志则正好相反,是一种写后日志,也就是先执行 Reids 指令,把数据写入内存,再记录数据。AOF 日志保存在文件 appendonly.aof 中。

image

那么为什么 AOF 要先执行指令再记录数据呢,因为 AOF 日志里面记录的是 Redis 收到的每一条信息,是以文本形式存储的。Redis 为了避免额外的检查开销,在向 AOF 里面记录日志的时候并不会进行语法检查,所以如果先记录日志再执行命令的话,日志中就可能记录了错误的命令,导致使用日志恢复数据时出错。而使用写后日志会先让系统执行命令,只有执行成功了才能被记录到日志中,可以避免记录错误命令的情况。另外,AOF 在执行完写命令之后才记录日志,不会阻塞当前的写操作。
以下是指令 set mykey abc 的 AOF 日志。

*3
$3
set
$5
myKey
$3
abc

*3 代表当前命令有三个部分,每部分都是由 "$数字" 开头,后面跟着具体的命令、键或者值。$3 set 代表这部分有三个字节,也就是 "set" 命令。

2.1 写回策略

但是 AOF 也存在着风险,就是当指令执行完成之后,服务器宕机了,没有来得及将指令记录到日志当中,造成了数据丢失的风险,如果此时是使用 Redis 来作为数据库的话,就无法使用日志来进行数据的恢复了。另外,AOF 虽然避免了当前命令的阻塞,但是在将日志文件写入磁盘的时候,磁盘压力大导致写盘很慢,造成阻塞。所以控制写命令执行完后 AOF 日志写回磁盘的时机非常重要。

为了解决这个问题,AOF 机制提供了三种选择,也就是 AOF 配置中 appendfsync 的三个可选值。
  Always:同步写回,每个写命令执行完,就立马同步地将日志写回磁盘。
  everysec:每秒写回,每个写命令执行完,先把日志写到 AOF 文件的内存缓冲区,每隔一秒把缓冲区的内容写入到磁盘。
  no:操作系统控制写回,每个写命令执行完,先把日志写到 AOF 文件的内存缓冲区,由操作系统决定何时将缓冲区内容写到磁盘。

appendfsync always
appendfsync everysec
appendfsync no

那么如何选择使用哪种方式呢,需要对三种方式的优缺点进行对比。
Always,同步写回,命令执行完立马同步日志,基本上不会丢失数据,但是写命令都有一个慢速落盘的操作,无法避免对主线程的性能造成影响。
everysec,每秒写回,每一秒写回一次的频率避免了同步写回的性能开销,但是如果发生宕机,上一秒未落盘的数据可能会造成丢失。
no:,操作系统控制写回,在执行完写缓冲区数据后,只要 AOF 的记录没有写回磁盘,发生宕机也会造成对于的数据丢失。

综上,如果对数据可靠性没有要求,想要获得高性能就选择 No 策略,如果对数据可靠性要求高就选择 Always 策略,如果允许数据造成一点丢失,又希望有高性能就选择 Everysec 策略。

2.2 重写机制

AOF 采用文件的形式记录接收到的所有命令,但是随着接收到的命令越来越多,文件也会越来越大,可能会带来性能问题:文件系统对文件的大小有限制;文件过大再追加命令记录效率会变低;如果发生宕机,AOF 中记录的命令要一个个被重新执行,文件过大的话恢复过程就会很慢。

为了解决文件越来越大的问题,AOF 采用了重写机制。
在重写时,Redis 根据数据库的使用情况创建一个新的 AOF 文件,读取数据库中的所有键值对,例如读取键值对 "mykey" : "myvalue",重写机制会记录 "set mykey myvalue" 这条指令,这样原来的多条指令就变为了一条指令存在新的 AOF 文件中。而且在重写时,根据键值对的最新状态,生成对应的写入命令,这样在日志恢复时只需要执行这一条命令,就可以完成键值对的写入了。

AOF 的重写过程是由后台线程 bgrewriteaof 完成,避免了主线程的阻塞。

AOF 重写的过程:当执行重写的时候,主线程 fork 出后台的 bgrewriteaof 子进程,fork 会把主线程的内存拷贝一份到 bgrewriteaof 子进程,bgrewriteaof 子进程在不影响主线程的情况下,逐一把拷贝的数据写成操作,记入重写日志。因为主线程没有被阻塞,如果有新的写操作,Redis 会将正在使用的 AOF 日志写到缓冲区中,这样即使宕机了 AOF 日志的操作仍然是完整的,可以用于恢复。AOF 重写日志也会被写到重写日志缓冲区,等到拷贝数据的所有操作记录重写完成后,重写日志就将这些最新的操作写入新的 AOF 文件,来保证数据库最新状态的记录。

简单来说,AOF 重写的时候,Redis 会执行一个内存拷贝,用于重写。然后使用两个日志保证在重写的过程中,新写入的数据不会丢失,Redis 采用额外的进程进行数据重写,所以不会造成主线程的阻塞。

三: RDB 快照

Redis的另一种持久化的方法:内存快照,把内存中数据的某一时刻的状态以文件的形式写到磁盘上,也就是快照,这样即使服务器宕机了快照文件也不会丢失,保证了数据的可靠性,而这个快照文件就称为RDB(Redis DataBase)文件,一般存放在 dump.rdb 文件中。

对哪些数据进行快照,在对数据做快照时,数据能不能修改,会不会造成线程阻塞,多久做一次快照是我们要关注的问题。

3.1 快照对象

使用 RDB 快照,我们给哪些数据做快照呢。实际上,Redis 的数据都在内存中,为了保证数据的可靠性,Redis 执行的是全量快照。把内存中所有数据的状态都记录到磁盘中,但是当内存的全量数据越多,RDB 文件就越大,往磁盘写数据的时间开销就越大。

Redis 提供了两个命令来生成 RDB 文件:savebgsave
save:在主线程中执行,会导致阻塞。
bgsave:创建一个子进程,专门用于写入 RDB 文件,不会造成进程阻塞,是 Redis RDB 文件生成的默认配置。

3.2 写时复制

如果我们在进行快照执行期间,数据被修改的话,那么就可能会导致数据不一致,比如已经将某一时刻数据的状态记录下来了,但是还没有被写入磁盘的数据 hello,被修改成了 helloworld,那么就会破坏快照的完整性。那么 Redis 为了快照而暂停写操作是不支持的,所以 Redis 就会借助操作系统提供的写时复制技术(Copy-On=Write,COW),在执行快照的时候正常处理写操作。

image

bgsave 子进程由主线程 fork 生成的,可以共享主线程的所有数据,bgsave 子进程运行后,开始读取主线程中的数据,并写入 RDB 文件中。当主线程要修改数据时,数据会被复制一份,生成副本数据,bgsave 会把副本数据写入到 RDB 文件中。

3.3 增量快照

如果我们将快照时间设置的比较长的话,如果某一时刻服务发生了宕机,如果上一次快照刚执行,则数据丢失的不会太大,如果距离上次快照执行时间间隔很长,那么可能会造成大量数据的丢失,所以设置快照的间隔时间非常重要。但是快照执行的时间也不能设置的太小了,因为频繁的将全量数据写入磁盘会给磁盘带来很大的压力。在主线程 fork 生成 bgsave子进程时会对主线程造成阻塞,所以如果频繁 fork 出 bgsave,会频繁的阻塞主线程。

使用增量快照可以避免每次全量快照的开销,增量快照就是在做完第一次全量快照后,后续再做快照我们只需要将被修改的数据写入快照文件即可。但同样我们也需要记住有哪些数据被修改了,需要我们使用额外的元数据信息来记录哪些信息被修改了,带来额外的空间开销问题。如果修改的数据过多,引起空间开销太大,对内存资源宝贵的 Redis 来说不值得。
默认的快照设置:

dbfilename dump.rdb
save 900 1    #当有一条Keys数据被改变时,900秒刷新到磁盘一次
save 300 10   #当有10条Keys数据被改变时,300秒刷新到磁盘一次
save 60 10000 #当有10000条Keys数据被改变时,60秒刷新到磁盘一次

3.4 混合使用

Redis 4.0提出了混合使用 AOF 日志 和 RDB 内存快照的方法。内存快照以一定的频率执行,在两次快照之间,使用 AOF 日志记录这期间的所以命令操作。这样快照就不需要很频繁的执行,避免了 fork 对主线程的影响,AOF 日志也只记录两次快照间的操作,不需要记录所有的操作了,不会出现文件过大的情况。

四:小结

image

这篇关于AOF日志与RDB快照的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!