Version: Next

Redis持久化

Redis是内存数据库,如果不能将内存中的数据库状态保存到磁盘,那么一旦服务器进程退出,服务器中的数据也就消失,所以Redis提供了持久化功能

RDB(Redis DataBase)

SNAPSHOTTING快照配置

什么是RDB

image-20200714150547995

在指定的时间间隔内将内存中的数据集快照写入磁盘,也就是行话讲的Snapshot快照,它恢复时是将快 照文件直接读到内存里

  • Redis会单独创建(fork)一个子进程来进行持久化,
  • 会先将数据写入到一个临时文件中,待持久化过程都结束了,再用这个临时文件替换上次持久化好的文件。
  • 整个过程中,主进程不进行任何IO操作。 这就确保了极高的性能。
  • 如果需要进行大规模数据的恢复,且对于数据恢复的完整性不是非常敏感,那 RDB方式要比AOF方式更加的高效。
  • RDB的缺点是后一次持久化后的数据可能丢失(如果意外宕机)

Fork

Fork的作用

  • 复制一个与当前进程一样的进程。
  • 新进程的所有数据(变量,环境变量,程序计数器等) 数值都和原进程一致
  • 两个返回值,给父进程返回子进程的 pid,给子进程返回 0
  • 但是是一个全新的进程,并作为原进程的子进程。
  • RDB保存的是dump.rdb文件
root@VM-0-17-ubuntu:/usr/local/bin# pwd
/usr/local/bin
root@VM-0-17-ubuntu:/usr/local/bin# ll
total 38400
drwxr-xr-x 3 root root 4096 Jul 14 11:26 ./
drwxr-xr-x 11 root root 4096 Jul 13 20:27 ../
-rwxr-xr-x 1 root root 393 Mar 11 16:52 cloud-init*
-rwxr-xr-x 1 root root 1781 Mar 11 16:52 cloud-init-per*
-rw-r--r-- 1 root root 92 Jul 14 11:26 dump.rdb ## ←###
-rwxr-xr-x 1 root root 396 Mar 11 16:52 jsonschema*
drwxr-xr-x 2 root root 4096 Jul 14 13:46 myconfig/
-rwxr-xr-x 1 root root 5452024 Jul 13 21:19 redis-benchmark*
-rwxr-xr-x 1 root root 9485688 Jul 13 21:19 redis-check-aof*
-rwxr-xr-x 1 root root 9485688 Jul 13 21:19 redis-check-rdb*
-rwxr-xr-x 1 root root 5375272 Jul 13 21:19 redis-cli*
lrwxrwxrwx 1 root root 12 Jul 13 21:19 redis-sentinel -> redis-server*
-rwxr-xr-x 1 root root 9485688 Jul 13 21:19 redis-server*

SnapShot快照配置

# 900秒(15分钟)内至少1个key值改变(则进行数据库保存--持久化)
save 900 1
# 300秒(5分钟)内至少10个key值改变(则进行数据库保存--持久化)
save 300 10
# 60秒(1分钟)内至少10000个key值改变(则进行数据库保存--持久化)
save 60 10000
stop-writes-on-bgsave-error yes  # 持久化出现错误后,是否依然进行继续进行工作
rdbcompression yes   # 使用压缩rdb文件 yes:压缩,但是需要一些cpu的消耗。no:不压 缩,需要更多的磁盘空间
rdbchecksum yes      # 是否校验rdb文件,更有利于文件的容错性,但是在保存rdb文件的时 候,会有大概10%的性能损耗
dbfilename dump.rdb  # dbfilenamerdb文件名称
dir ./    # dir 数据目录,数据库的写入会在这个目录。rdb、aof文件也会写在这个目录

如何触发RDB快照

  • 配置文件中默认的快照配置,建议多用一台主机作为备份,复制一份dump.rdb
  • 命令save或者bgsave,两个命令底层调用的都是 rdbSave 方法,这个方法一定会阻塞当前线程
    • save:只管保存,其他不管,全部阻塞
    • bgsave:fork 出一个子进程,后台异步进行快照操作,快照同时还可以响应用户请求,可以通过lastsave命令获取最后一次成功执行快照的时间
  • flushall也会触发生成dump.rdb文件,但是里面是空的
  • 退出时也会生成dump.db文件

如何恢复

  • 将dump.rdb移动到redis安装目录并启动服务即可,因为 Redis 已启动就会调用 rdbLoad 函数
  • config get dir获取目录
127.0.0.1:6379> config get dir
dir
/usr/local/bin

SAVE、BGSAVE、AOF写入与 BGREWRITEAOF 能否同时执行

  • SAVE 会阻塞当前线程,当前 SAVE 执行完毕前,新的 SAVE、BGSAVE、BGREWRITEAOF 都阻塞
  • AOF 写入由 后台线程 完成,BGREWRITEAOF 由 子进程 完成,所以在 SAVE 执行 过程中,AOF 写入与 BGREWRITEAOF 可以同时执行
  • 在执行 SAVE 之前,会先检查有没有正在执行的 BGSAVE,如果有就返回错误信息,告知 BGSAVE 正在运行,不能运行 SAVE 防止写入竞争
  • 在执行 BGSAVE 时,新的 BGSAVE 也会返回错误
  • BGREWRITEAOF 与 BGSAVE 不能同时执行
    • BGSAVE 正在执行,则 BEREWRITEAOF 被延迟执行,返回一个延迟信息
    • BEREWRITEAOF 正在执行,则 BGSAVE 被返回一个错误信息
    • 这样做的目的主要是降低并发 IO,理论上二者不存在冲突

优缺点

优点

  • 适合大规模数据恢复
  • 对数据完整性和一致性要求不高

缺点

  • 隔一段时间进行一次备份,如果redis意外宕机,就会丢失最后一次快照后的新数据
  • Fork时,内存中的数据被复制了一份,耗费资源

AOF (Append Only File)

APPEND ONLY配置

将所有执行过的命令都记录下来,恢复的时候就把这文件的内容全部重新执行一次

image-20200714152551085

  • 以日志的形式来记录每个写操作,将Redis执行过的所有指令记录下来(读操作不记录),

  • 只许追加文件 但不可以改写文件,

  • redis启动之初会读取该文件重新构建数据,

  • 换言之,redis重启的话就根据日志文件的内容将写指令从前到后执行一次以完成数据的恢复工作

Append Only File 配置

appendonly no   # 默认关闭,需要手动打开
# 是否以append only模式作为持久化方式,默认使用的是rdb方式持久化,这种 方式在许多应用中已经足够用了
appendfilename "appendonly.aof"  
# appendfilename AOF 文件名称
appendfsync everysec  
# appendfsync aof持久化策略的配置    
# no表示不执行fsync,由操作系统保证数据同步到磁盘,速度快。    
# always表示每次写入都执行fsync,以保证数据同步到磁盘。    
# everysec表示每秒执行一次fsync,可能会导致丢失这1s数据
no-appendfsync-on-rewrite no
# if you have latency problems turn this to ‘yes'. Otherwise leave it as 'no' that is the safest pick from the point of view of durability
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb

同步命令到 AOF 文件的三个阶段

  1. 命令传播:Redis 将执行完的命令、命令的参数、命令的参数个数等信息发送到 AOF 程序中
  2. 缓存追加:AOF 程序根据接收到的命令数据,将命令转换为网络通信协议的格式,然后将协议内容追加到服务器的 aof_buffer 缓存末尾
  3. 文件写入与保存:AOF 缓存中的内容被写入到 AOF 文件末尾,如果设定的 AOF 保存条件被满足的话,fsync 函数或者 fdatasync 函数会被调用,将写入的内容真正的保存到磁盘中(Linux 同步缓存数据到磁盘的命令)
    • 调用 aof.c/flushAppendOnlyFile 函数,进行两个操作
      • WRITE:根据条件,将 aof_buffer 中的缓存写入到 AOF 文件
      • SAVE:根据条件,调用 fsync 或 fdatasync 函数,将 AOF 文件保存到磁盘
    • 具体条件,取决于 AOF 的三种保存模式

AOF 的三种保存模式

  1. AOF_FSYNC_NO:不保存
  2. AOF_FSYNC_EVERYSEC:每秒保存
  3. AOF_FSYNC_ALWAYS:每执行一个命令保存一次

AOF_FSYNC_NO 不保存—— WRITE、SAVE 都会阻塞主进程

  • 每次调用 flushAppendOnlyFile 函数,WRITE 都会被调用(写入文件),SAVE 会被跳过(跳过写入磁盘)
  • 这种模式下,SAVE 只在以下任意一种情况下被执行,且会阻塞当前 redis 主进程
    • Redis 被关闭
    • AOF 功能被关闭
    • 系统的写缓存被刷新(可能是缓存已经写满、或者定期保存操作被执行)

AOF_FSYNC_EVERYSEC 每秒保存 —— WRITE 由主线程执行阻塞自身,SAVE 由子线程执行,不阻塞主进程

  • SAVE 原则上 每隔一秒就执行一次,因为 SAVE 操作是由后台子线程调用的,所以不会因此主进程阻塞
    • 原则上:是指实际上不一定每秒调用一次 fsync 或者 fdatasync,这与调用 flushAppendOnlyFile 函数时 Redis 所处的状态有关
      • 情况一:子线程正执行 SAVE,这个 SAVE 的执行时间未超过 2 秒,那么程序直接返回,并不执行 WRITE 或者新 SAVE
      • 情况二:子线程正执行 SAVE,这个 SAVE 的执行时间已经超过 2 秒,则执行 WRITE 写入文件,但不执行新的 SAVE 写入磁盘 (此时 WRITE 必须等待当前 SAVE 结束才能执行,因此更耗时)
      • 情况三:子线程没有在执行 SAVE,上次成功执行 SAVE 距今不超过 1 秒,那么执行 WRITE,不执行 SAVE
      • 情况四:子线程没有在执行 SAVE,上次成功执行 SAVE 距今已经超过 1 秒,那么执行 WRITE,执行 SAVE
    • 情况一时宕机,那么最多损失小于 2 秒的数据;如果情况二宕机,那么损失的数据可能超过 2秒,因此官网 AOF 宕机最多之丢失 1 秒数据的说法不准确

AOF_FSYNC_ALWAYS 总是保存 —— WRITE、SAVE 都会阻塞主进程

  • 每一个命令执行完都调用 WRITE、SAVE
  • 该模式下,SAVE 由主线程执行,会造成自身阻塞,不能接收命令请求

AOF的启动|修复|恢复

  • 正常恢复
    • 启动:设置yes,修改默认appendonly no为yes
    • 将有数据的aof文件复制一份保存到config get dir指向的目录
    • 恢复:重启redis然后重新加载
  • 异常恢复
    • 启动:设置Yes
    • 故意破坏appendonly.aof文件
    • 修复:redis-check-aof --fix appendonly.aof
    • 恢复:重启redis然后重新加载

AOF 重写

  • AOF 采用文件追加方式,文件会越来越大

  • 因此,新增了重写机制,当AOF文件的大小超过所设定的阈值时,Redis 就会启动AOF文件的内容压缩,只保留可以恢复数据的小指令集,

  • 可以 使用命令 bgrewriteaof !

重写原理:

  • 重写并不对原先的 AOF 文件进行任何的读取、写入操作

  • AOF 文件持续增长而过大时,会 fork 出一条 新进程 来将文件重写(也是先写临时文件后再 rename),新进程遍历 redis 数据,保存各种集合类型的状态,而不是记录最初一条一条往集合里插入数据的语句。重写aof文件的操作,并没有读取旧的aof文件,这点和快照有点类似!

  • fork 出子进程在后台进行 AOF 重写时,由于主进程依然在处理请求,产生新数据,因此会造成数据不一致,解决方案是在 fork 出子进程后,启用一个 AOF 重写缓存,记录期间的 WRITE 命令,也就是干跟 AOF 文件一样的事,重写完毕后再将 AOF 重写缓存的内容追加上去

触发机制

Redis会记录上次重写时的AOF大小,默认配置是当AOF文件大小是上次rewrite后大小的已被且文件大 于64M的触发。

优缺点

优点

  • 所有修改都被同步:appendfsync always同步持久化,每次发生数据改变会被立即记录到磁盘,性能较差但数据完整性比较好
  • 每秒同步:appendfsync everysec异步操作,每秒记录,如果一秒内宕机,则这一秒内的数据丢失
  • 不同步:appendfsync no从不同步

缺点

  • 相同数据集的数据而言,aof文件要远大于rdb文件,恢复速度慢与rdb
  • aof运行效率要慢于rdb,每秒同步粗略效率较好,不同步效率和rdb相同

总结

RDBAOF
启动优先级
体积
恢复速度
数据安全性丢数据根据策略决定
  1. RDB持久化方式能够在指定的时间间隔内对数据进行快照存储

  2. AOF持久化方式记录每次对服务器的写操作,当服务器重启的时候会重新执行这些命令来恢复原始数据,AOF命令以Redis协议追加保存每次的操作到文件末尾,Redis还能对AOF文件进行后台重写,是的AOF文件的体积不至于过大

  3. 只做缓存,如果只希望数据在服务器运行期间存在,可以不采用任何持久化

  4. 同时开启两种持久化策略

    • redis重启时优先使用aof文件恢复原始数据,因为通常情况下aof文件保存的数据要比rdb文件保存的完整
    • RDB数据不实时,同时使用两种持久化服务器重启时也只会找AOF文件,但作者不建议只使用AOF,因为RDB更适合用于备份数据库,快速重启,AOF变化太频繁不容易备份
  5. 性能建议

    • 因为RDB文件只作为后背用途,建议在集群从机上持久化RDB文件,而且只要15分钟备份一次就足够,只保留save 900 1这条规则

    • 如果开启了AOF,好处是在最差的情况下也只会丢失不超过两秒的数据,启动脚本较简单只加载自己的aof文件就可以了

      代价:

      • 带来了持续的IO
      • AOF重写的最后将重写过程中产生的新数据写道新文件造成的阻塞几乎不可避免

      只要硬盘允许,应该尽量减少AOF重写的频率,AOF重写的基础大小默认值64MB太小了,可以设置到5G以上,默认超过原大小100%大小重写可以改到适当的数值

    • 如果不开启AOF,仅靠主从复制实现高可用性也可以,能省掉一大笔IO开销,也减少了重写时带来的系统波动,代价是如果主机和从机同时宕机,会丢失十几分钟的数据,启动脚本也要比较两个主从机中的RDB文件,载入较新的RDB文件