# 持久化

Redis 数据放在内存中所以快, 但是也容易丢失。引出两种持久化方法:

  • 存储数据到硬盘中。
  • 保存存放数据的过程,恢复数据时,只需要将过程执行一遍(有点 redo 日志的味道)。

# RDB

就是将数据保存到硬盘中:

-- 会阻塞
save
-- 创建进程负责持久化,只有 fork 才会阻塞
bgsave

执行后,会在服务端目录下生成一个 dump.rdb 文件,而这个文件中就保存了内存中存放的数据,当服务器重启后,会自动加载里面的内容到对应数据库中。保存后我们可以关闭服务器:

-- 也会触发 bgsave
shutdown

可以在配置文件中设置自动保存功能(windows 下是 redis.windows.conf 文件)

以第一个为例,意思为 900 秒内有 1 个写入时,就保存。配置的 save 使用的都是 bgsave 后台执行。

RDB 保证数据一致性,生产环境中内存区都比较大,将内存中的数据同步到硬盘的过程可能会持续比较长,此时 Redis 服务收到数据写请求就需要保证数据一致性。核心思路是 Copy-on-Write:

我们需要保证的是,在某个数据执行写入磁盘操作时,该数据在内存中不会发生变化。

在正常的快照操作中,Redis 主进程会 fork 一个新的快照进程专门来做这个事情,这样保证了 Redis 服务不会停止对客户端包括写请求在内的任何响应。子进程就不断将内存数据存入磁盘。这个过程中,主进程对于内存只有 read-only 权限,当发生写请求时,会触发页异常中断,陷入内核的中断例程,此时内核就会把触发异常的页复制一份,然后对这个复制的页进行修改,而子进程相应的页没有变化。这种哪个页发生修改就复制哪个页的机制,细化了内存粒度。

在进行快照操作的这段时间,如果发生服务崩溃怎么办

很简单,在没有将数据全部写入到磁盘前,这次快照操作都不算成功。如果出现了服务崩溃的情况,将以上一次完整的 RDB 快照文件作为恢复内存数据的参考。也就是说,在快照操作过程中不能影响上一次的备份数据。Redis 服务会在磁盘上创建一个临时文件进行数据操作,待操作成功后才会用这个临时文件替换掉上一次的备份。

# AOF

RDB 缺点在于,如果在自动保存前服务器崩溃,依然会导致数据丢失。AOF 就是另一种方式,它会以日志的形式将我们每次执行的命令都进行保存,服务器重启时会将所有命令依次执行,通过这种重演的方式将数据恢复,这样就能很好解决实时性存储问题。

对于日志的记录,也有保存策略可以配置:

  • always:每次写操作都会保存
  • everysec:每秒保存一次(默认配置),这样丢失数据限定在一秒内。
  • no:看系统心情

配置文件:

# 注意得改成也是
appendonly yes
# appendfsync always
appendfsync everysec
# appendfsync no

重启服务器后,可以看到服务器目录下多了一个 appendonly.aof 文件,存储的就是我们执行的命令。

AOF 的同步策略涉及到操作系统的 write 函数和 fsync 函数,在《Redis 设计与实现》如此说明:

为了提高文件写入效率,在现代操作系统中,当用户调用 write 函数,将一些数据写入文件时,操作系统通常会将数据暂存到一个内存缓冲区里,当缓冲区的空间被填满或超过了指定时限后,才真正将缓冲区的数据写入到磁盘里。

这样的操作虽然提高了效率,但也为数据写入带来了安全问题:如果计算机停机,内存缓冲区中的数据会丢失。为此,系统提供了 fsync、fdatasync 同步函数,可以强制操作系统立刻将缓冲区中的数据写入到硬盘里,从而确保写入数据的安全性。

# AOF 文件重写机制

如果只是傻瓜式记录操作日志。没有任何优化,就会导致 appednonly.aof 变得十分大,所以 Redis 有一个 AOF 重写机制进行优化(多条语句压缩),反正就是优化后的操作和原来的操作结果一致。

输入命令执行重写操作: bgrewriteaof

AOF 重写过程是由后台进程 bgrewriteaof 来完成的。主线程 fork 出后台的 bgrewriteaof 子进程,fork 会把主线程的内存拷贝一份给 bgrewriteaof 子进程,这里面就包含了数据库的最新数据。然后,bgrewriteaof 子进程就可以在不影响主线程的情况下,逐一把拷贝的数据写成操作,记入重写日志。

所以 aof 在重写时,在 fork 进程时是会阻塞住主线程的。

或者配置文件配置自动重写:

# 百分比计算,当前 aof 文件比上一次重写后 aof 文件的增量大小,和上一次重写后 aof 文件大小的比值。
auto-aof-rewrite-percentage 100
# 当达到这个大小时,触发自动重写
auto-aof-rewrite-min-size 64mb

如何在重写时,处理写请求:

如果有新数据写入,主线程就会将命令记录到两个 aof 日志内存缓冲区中。如果 AOF 写回策略配置的是 always,则直接将命令写回旧的日志文件,并且保存一份命令至 AOF 重写缓冲区,这些操作对新的日志文件是不存在影响的。(旧的日志文件:主线程使用的日志文件,新的日志文件:bgrewriteaof 进程使用的日志文件)

  • 主线程 fork 出子进程重写 aof 日志
  • 子进程重写日志完成后,主线程追加 aof 日志缓冲
  • 替换日志文件

# 参考

https://www.yuque.com/qingkongxiaguang/spring/nka2vz#RDB

https://pdai.tech/md/db/nosql-redis/db-redis-x-rdb-aof.html

https://ningg.top/computer-basic-theory-copy-on-write/

https://blog.csdn.net/Muscleape/article/details/105670481

https://www.cnblogs.com/ybyn/p/14157568.html

https://segmentfault.com/a/1190000015983518

https://blog.csdn.net/qq_41434612/article/details/108791437

https://www.cnblogs.com/liang24/p/14180036.html

https://www.jianshu.com/p/d3ba7b8ad964

https://blog.csdn.net/liupeifeng3514/article/details/79048767

极客时间 《Redis 核心与实战》的相关内容