1.持久化机制
- 持久化就是把内存的数据写到磁盘中,防止服务宕机导致内存数据丢失。
2.AOF
AOF概述
AOF持久化
:以独立日志就把该命令。以追加的方式写入到一个文件里,然后重启 Redis 的时候,先去读取这个文件里的命令达到恢复目数据的目的。- 在 Redis 中 AOF 持久化功能默认是不开启的,需要我们修改
redis.conf
配置文件中的以下参数:appendonly yes
注意只会记录写操作命令,读操作命令是不会被记录的,因为没意义。
- Redis 是
先执行写操作命令后,才将该命令记录到 AOF 日志里的
,这么做有好处也有风险。
'优点'
1. 避免额外的检查开销。(如果先写AOF日志,如果当前命令有语法问题,不进行语法检查,记录错误的AOF文件,当重启后执行的是错误的AOF文件)
2. 不会阻塞当前写操作命令的执行
'缺点'
1. 执行写操作命令和记录日志是两个过程,那当 Redis 在还没来得及将命令写入到硬盘时,服务器发生宕机了,这个数据就会有丢失的风险。
2. 由于写操作命令执行成功后才记录到 AOF 日志,所以不会阻塞当前写操作命令的执行,但是可能会给「下一个」命令带来阻塞风险。
- 因为将命令写入到日志的这个操作也是在主进程完成的(执行命令也是在主进程),也就是说这两个操作是
同步
的。 - 如果在将日志内容写入到硬盘时,服务器的硬盘的 I/O 压力太大,就会导致写硬盘的速度很慢,进而阻塞住了,也就会导致后续的命令无法执行。
AOF写到磁盘的过程
流程:
- Redis 执行完写操作命令后,会将命令追加到 server.aof_buf 缓冲区;
- 然后通过 write() 系统调用,将 aof_buf 缓冲区的数据写入到 AOF 文件,此时数据并没有写入到硬盘,而是拷贝到了内核缓冲区 page cache,等待内核将数据写入硬盘;
- 内核调用fsync函数将内核缓冲区的数据根据对应的策略向硬盘同步。
- 随着AOF文件越来越大,需要定期对AOF文件进行重写,达到压缩体积目的(AOF重写是把Redis进程内的数据转换为写命令同步到新的AOF文件的过程)
- Redis服务重启后,加载AOF文件进行恢复。
步骤3:Redis 提供了 3 种写回硬盘的策略
appendfsync Always 每次写操作命令执行完后,同步将 AOF 日志数据写回硬盘;
appendfsync Everysec 每次写操作命令执行完后,先将命令写入到 AOF 文件的内核缓冲区,然后每隔一秒将缓冲区里的内容写回到硬盘;
appendfsync No 不由 Redis 控制写回硬盘的时机,转交给操作系统控制写回的时机,也就是每次写操作命令执行完后,先将命令写入到 AOF 文件的内核缓冲区,再由操作系统决定何时将缓冲区内容写回硬盘。
优点
:
- 为了让AOF更好的保护数据不丢失,可以配置AOF每秒执行一次fsync操作,如果redis进程挂掉,最多丢失1秒数据
缺点
:
- 对于同一份文件AOF文件比RDB数据快照要大。
- 数据恢复慢
AOF重写过程
AOF 重写机制是在重写时,读取当前数据库中的所有键值对,然后将每一个键值对用一条命令记录到「新的 AOF 文件」,等到全部记录完后,就将新的 AOF 文件替换掉现有的 AOF 文件。
先写到新的 AOF 文件再覆盖过去
。因为如果 AOF 重写过程中失败了,现有的 AOF 文件就会造成污染,可能无法用于恢复使用。
AOF后台重写
- 写入 AOF 日志的操作虽然是在主进程完成的,因为它写入的内容不多,一般不影响命令的操作。但是触发AOF重写需要重写一份AOF文件,并且将重写的AOF文件,覆盖掉旧AOF文件。此时非常耗时。
所以Redis 的重写 AOF 过程是由后台子进程 bgrewriteaof 来完成的
优点
- 子进程进行 AOF 重写期间,主进程可以继续处理命令请求,从而避免阻塞主进程;
- 子进程带有主进程的数据副本,这里使用子进程而不是线程,因为如果是使用线程,多线程之间会共享内存,那么在修改共享内存数据的时候,需要通过加锁来保证数据的安全,而这样就会降低性能。而使用子进程,创建子进程时,父子进程是共享内存数据的,不过这个共享的内存只能以只读的方式,而当父子进程任意一方修改了该共享内存,就会发生「写时复制」,于是父子进程就有了独立的数据副本,就不用加锁来保证数据安全。
子进程拥有主进程一样的数据副本
- 主进程在通过 fork 系统调用生成 bgrewriteaof 子进程时,操作系统会把主进程的「页表」复制一份给子进程,这个页表记录着虚拟地址和物理地址映射关系,而不会复制物理内存,也就是说,两者的虚拟空间不同,但其对应的物理空间是同一个。
- 当父进程或者子进程在向这个内存发起写操作时,CPU 就会触发写保护中断,这个写保护中断是由于违反权限导致的,然后操作系统会在「写保护中断处理函数」里进行物理内存的复制,并重新设置其内存映射关系,将父子进程的内存读写权限设置为可读写,最后才会对内存进行写操作(
写时复制
)
进行复制的时候有两个阶段会导致阻塞父进程:
- 创建子进程的途中,由于要复制父进程的页表等数据结构,阻塞的时间跟页表的大小有关,页表越大,阻塞的时间也越长;
- 创建完子进程后,如果子进程或者父进程修改了共享数据,就会发生写时复制,这期间会拷贝物理内存,如果内存越大,自然阻塞的时间也越长;
AOF缓冲区
- 触发重写机制后,主进程就会创建重写 AOF 的子进程,此时父子进程共享物理内存,重写子进程只会对这个内存进行只读,重写 AOF 子进程会读取数据库里的所有数据,并逐一把内存数据的键值对转换成一条命令,再将命令记录到重写日志(新的 AOF 文件)。
- 子进程重写过程中,主进程依然可以正常处理命令。
- 主进程修改了已经存在 key-value,就会发生写时复制,注意这里只会复制主进程修改的物理内存数据,没修改物理内存还是与子进程共享的。
此时会出现数据不一致现象,于是使用AOF重写缓冲区。
在重写 AOF 期间,当 Redis 执行完一个写命令之后,它会同时将这个写命令写入到 「AOF 缓冲区」和 「AOF 重写缓冲区」。
重写流程
-
在 bgrewriteaof 子进程执行 AOF 重写期间,主进程需要执行以下三个工作:
- 执行客户端发来的命令;
- 将执行后的写命令追加到 「AOF 缓冲区」;
- 将执行后的写命令追加到 「AOF 重写缓冲区」;
-
当子进程完成 AOF 重写工作(扫描数据库中所有数据,逐一把内存数据的键值对转换成一条命令,再将命令记录到重写日志)后,会向主进程发送一条信号,信号是进程间通讯的一种方式,且是异步的。
-
主进程收到该信号后,会调用一个信号处理函数:
- 将 AOF 重写缓冲区中的所有内容追加到新的 AOF 的文件中,使得新旧两个 AOF 文件所保存的数据库状态一致;
- 新的 AOF 的文件进行改名,覆盖现有的 AOF 文件。
3.RDB
概述
- 快照持久化是Redis默认采用的持久化方式,在Redis.conf配置文件中默认有此下配置:
save 900 1 #在900秒(15分钟)之后,如果至少有1个key发生变化,Redis就会自动触发BGSAVE命令
创建快照。
save 300 10 #在300秒(5分钟)之后,如果至少有10个key发生变化,Redis就会自动触发BGSAVE命令创建快照。
save 60 10000 #在60秒(1分钟)之后,如果至少有10000个key发生变化,Redis就会自动触发BGSAVE命令创建快照。
RDB 快照就是记录某一个瞬间的内存数据,记录的是实际数据
,而 AOF 文件记录的是命令操作的日志,而不是实际的数据。因此在 Redis 恢复数据时, RDB 恢复数据的效率会比 AOF 高些,因为直接将 RDB 文件读入内存就可以,不需要像 AOF 那样还需要额外执行操作命令的步骤才能恢复数据。Redis 的快照是全量快照,也就是说每次执行快照,都是把内存中的「所有数据」都记录到磁盘中。
Redis 提供了两个命令来生成 RDB 文件,分别是 save
和 bgsave
,他们的区别就在于是否在「主线程」里执行:
- 执行了
save
命令,就会在主线程生成 RDB 文件,由于和执行操作命令在同一个线程,所以如果写入 RDB 文件的时间太长,会阻塞主线程; - 执行了
bgsave
命令,会创建一个子进程
来生成 RDB 文件,这样可以避免主线程的阻塞;
RDB缺点
- 执行快照是一个比较重的操作,在服务器发生故障时,丢失的数据会比 AOF 持久化的方式更多,因为 RDB 快照是全量快照的方式,因此执行的频率不能太频繁,否则会影响 Redis 性能,如果频率太低,服务器故障时,丢失的数据会更多。而 AOF 日志可以以秒级的方式记录操作命令,所以丢失的数据就相对更少。
RDB优点
- Redis加载RDB恢复数据远远快于AOF的方式
bgsave执行流程
- RDB是Redis默认的持久化方案。RDB持久化时会将内存中的数据写入到磁盘中,在指定目录下生成一个
dump.rdb
文件。Redis重启会加载dump.rdb
文件恢复数据。 - bgsave是主流的触发RDB持久化的方式,执行过程如下:
- 执行
BGSAVE
命令 - Reids父进程判断当前是否存在正在执行的子进程,如果存在,
BGSAVE
命令直接返回。 - 父进程执行
fork
操作创建子进程,fork
操作过程中父进程会阻塞。 - 父进程
fork
完成后,父进程继续接收并处理客户端的请求,而子进程开始将内存中的数据写进硬盘的临时文件。 - 当子进程写完所有数据后会用临时文件替换RDB文件。
执行快照时,数据能被修改吗?
- 执行 bgsave 过程中,由于是交给子进程来构建 RDB 文件,主线程还是可以继续工作的Redis 依然可以继续处理操作命令的,也就是数据是能被修改的。(关键的技术就在于写时复制技术(Copy-On-Write, COW))
流程
- 执行 bgsave 命令的时候,会通过 fork() 创建子进程,此时子进程和父进程是共享同一片内存数据的,因为创建子进程的时候,会复制父进程的页表,但是页表指向的物理内存还是一个。
- 如果主线程(父进程)要修改共享数据里的某一块数据(比如键值对 A)时,就会发生写时复制,于是这块数据的物理内存就会被复制一份(键值对 A’),然后主线程在这个数据副本(键值对 A’)进行修改操作。与此同时,bgsave 子进程可以继续把原来的数据(键值对 A)写入到 RDB 文件。
- Redis 使用 bgsave 对当前内存中的所有数据做快照,这个操作是由 bgsave 子进程在后台完成的,执行时不会阻塞主线程,这就使得主线程同时可以修改数据。
注意:
- bgsave 快照过程中,如果主线程修改了共享数据,发生了写时复制后,RDB 快照保存的是原本的内存数据,而主线程刚修改的数据,是没办法在这一时间写入 RDB 文件的,只能交由下一次的 bgsave 快照。
- 所以 Redis 在使用 bgsave 快照过程中,如果主线程修改了内存数据,不管是否是共享的内存数据,RDB 快照都无法写入主线程刚修改的数据,因为此时主线程(父进程)的内存数据和子进程的内存数据已经分离了,子进程写入到 RDB 文件的内存数据只能是原本的内存数据。
- 如果系统恰好在 RDB 快照文件创建完毕后崩溃了,那么 Redis 将会丢失主线程在快照期间修改的数据。
4.混合持久化
- 如果频率太低,两次快照间一旦服务器发生宕机,就可能会比较多的数据丢失;
- 如果频率太高,频繁写入磁盘和创建子进程会带来额外的性能开销。
开启混合持久化功能,可以在 Redis 配置文件将下面这个配置项设置成 yes:
aof-use-rdb-preamble yes
- 当开启了混合持久化时,在 AOF 重写日志时,fork 出来的重写子进程会先将与主线程共享的内存数据以 RDB 方式写入到 AOF 文件,然后主线程处理的操作命令会被记录在重写缓冲区里
- 重写缓冲区里的增量命令会以 AOF 方式写入到 AOF 文件,写入完成后通知主进程将新的含有 RDB 格式和 AOF 格式的 AOF 文件替换旧的的 AOF 文件。
使用了混合持久化,AOF 文件的前半部分是 RDB 格式的全量数据,后半部分是 AOF 格式的增量数据。
优点
- 重启 Redis 加载数据的时候,由于前半部分是 RDB 内容,这样加载的时候速度会很快。
- 加载完 RDB 的内容后,才会加载后半部分的 AOF 内容,这里的内容是 Redis 后台子进程重写 AOF 期间,主线程处理的操作命令,可以使得数据更少的丢失。
文章:https://www.xiaolincoding.com/redis/storage/aof.html