持久化

参考:小林 coding
参考视频

AOF

Append Ony File,是一个文本文件,因此较大。 当客户端发起一个写请求时,Redis 主线程首先在内存中执行写操作,接着将这个写命令同步到磁盘中的 AOF 日志中。

AOF的优点

  1. 先在内存中执行写命令,再写回磁盘。这样可以确保命令是正确的,才记录在日志中,也可以防止写回磁盘的操作阻塞当前写操作

如何配置AOF?

AOF默认不开启,需要更改Redis的配置文件。 /opt/homebrew/etc/redis.conf

1
2
3
4
5
6
appendonly yes #开启 AOF
appendfilename "appendonly.aof" #指定 AOF 文件名字

# appendfsync always
appendfsync everysec #默认同步策略
# appendfsync no

写回策略

AOF

  1. appendfsync always:每次写请求完成,把这条写命令写入磁盘的日志中。数据最安全,但是频繁磁盘IO影响性能。如果对数据完整性要求极高,比如金融交易平台,则会选择这种策略。
  2. appendfsync everysec🌟:每次写请求完成,把写命令写入到内核缓冲区中,然后每秒钟,内核缓存区调用一次fsynch(),把数据写入磁盘。
  3. appendfsync no: 不主动写回。等待操作系统把命令写入磁盘中。系统崩溃或断电时会丢失大量未同步数据。

AOF重写

AOF文件可能会过大,因此当文件大于64M时,就会 通过重写来压缩文件体积

  1. fork一个子进程bgrewriteaof来遍历数据库中所有的key-value pair, 为每一个pair生成一条命令,写入一个新的AOF文件。
  2. 在这个过程中,主线程一边处理客户端发来的请求,一边把命令追加进AOF缓冲区和AOF重写缓冲区
  3. 当子进程完成重写后,主进程会将 AOF 重写缓冲区中的增量数据追加到新 AOF 文件中。最后用新AOF日志代替旧AOF日志。

为什么重写需要一个新的AOF文件?

如果重写失败,则会污染现有的AOF文件。

为什么用子进程,而不是子线程?

因为线程会共享内存,需要加锁来保证两个线程对内存的操作互不影响。但是子进程可以利用写时复制的特点,保证两个进程的物理内存分开。 Alt text

AOF重写缓冲区

重写期间的新写命令会写入 AOF 重写缓冲区,确保数据不丢失。

重写会阻塞主线程吗?

写时复制时会阻塞主线程;当子进程遍历完 pair之后,会发给主进程一个signal,主进程会执行signal handler,将AOF重写缓冲区的命令追加到新AOF文件中。在执行handler时也会阻塞。

RDB

RDB是一个二进制文件,对此时内存中的全量数据做一个快照。主进程fork一个子进程,子进程来将内存写在临时RDB文件中。
与此同时,主进程继续处理新的写操作,但它并不是在原来的物理内存上操作,因为写时复制的机制,它在OS复制出来的内存副本上执行写操作。

优点:性能高,因为恢复数据时,能以二进制的形式加载。 文件小
缺点:可能会丢失数据。因为每次备份具有时间间隔,如果在两次备份之间出现了宕机,那么会丢失数据。

Alt text

配置

save 300 10
实际上执行的是bgsave命令,也就是 fork 子进程,让子进程生成快照。 代表 300 秒内至少有 10 次数据库的改动时,会触发一次备份。备份不能太频繁,因为全量快照开销大,每秒备份会影响数据库性能。

RDB & AOF 混合

混合持久化。
在AOF重写阶段,主进程fork出一个子进程。子进程此时为数据库生成一个RDB快照,写入新AOF文件中。该文件的开头是RDB格式,后面是AOF命令格式。
与此同时,主进程也在处理客户端请求,它会把这段时间的写请求追加在重写缓存区和AOF缓存区。
等子进程工作完毕,主进程就将重写缓存区中的命令追加到新的AOF文件中。最后,用新文件替换旧文件。

配置:aof-use-rdb-preamble yes