Redis两种持久化方式原理和单机多实例持久化脚本

注意:Linux系统使用redis需要更改系统的内存分配策略,执行sysctl vm.overcommit_memory=1命令,0-2分别代表不加载、加载、允许超额加载

一、RDB

RDB持久化是把当前数据生成二进制快照保存到硬盘的过程。

配置项:

  • dir:保存路径
  • dbfilename:保存文件名
  • rdbcompression:是否开启压缩,默认开启,可以方便保存到硬盘和发送给从节点

触发时机:

  • save:阻塞当前Redis服务器,直到RDB过程完成,线上不建议使用
  • bgsave:Redis主进程执行fork操作创建子进程,RDB持久化过程由子进程完成,阻塞只发生在fork阶段。
  • 配置save频率自动触发:save m n表示m秒内发生n次修改后自动触发bgsave
  • 从节点执行全量复制,主节点自动执行bgsave生成RDB文件并发送给从节点
  • 执行debug reload命令重新加载Redis时执行save
  • 如果没有开启aof,执行shutdown时回执行bgsave

触发流程:

  1. 执行bgsave命令,如果正在执行bgsave命令则直接返回
  2. 父进程执行fork操作创建子进程,fork操作时会阻塞主进程
  3. 父进程fork完后主进程不再阻塞,可执行其它命令
  4. 子进程创建RDB文件,写入完成后替换旧文件
  5. 子进程发送信号通知父进程表示完成,父进程更新统计信息

相关统计信息:

  • 上次fork子进程耗费的微秒数:info stats 命令,latest_fork_usec 项
  • 上次生成RDB的时间戳:info persistence 命令,rdb_last_save_time 项

RDB文件:

Redis加载损坏的RDB拒绝启动会打印日志Short read or OOM loading DB,使用 redis-check-dump 命令检测 RDB 文件,并获取错误报告。

二、AOF

记录每次的写命令,写入格式为Redis文本协议,重启时再重新执行AOF中的命令来达到恢复数据的目的,所以恢复速度相对 RDB 较慢。

配置项:

  • dir:保存路径
  • appendonly:是否开启
  • appendfilename:存储文件名
  • appendfsync:同步策略,always 每次写入调用 fsync,everysec 每秒调用 fsync,no 由系统配置决定调用 fsync 时机,一般30s

工作流程:

  1. 执行 AOF 请求,如果正在执行 AOF 重写或 bgsave 直接返回
  2. 父进程执行 fork 创建子进程,开销等同 bgsave,使用写时复制技术,子进程共享 fork 操作时的内存数据,接下来父进程的操作保存到重写缓冲区(aof_rewrite_buf)中
  3. 子进程批量写入新 AOF 文件中,每次批量写入大小由 aof-rewrite-incremental-fsync 配置控制,开启后每次写入32M,防止单次刷盘数据过大造成磁盘阻塞
  4. 新 AOF 文件重写完成后,发送信号给父进程,父进程更新统计信息
  5. 父进程将 aof_rewrite_buf 中的数据写入到新的 AOF 文件中
  6. 使用新的 AOF 文件替换老文件,重写完成

由于AOF追加阻塞造成丢失数据:

当策略为 everysec 时,丢失数据会有以下两种情况:

  • 如果距离上次同步成功时间(aof_last_rewrite_time_sec)超过2秒,Redis主线程将会阻塞
  • 如果距离上次同步成功时间在2秒内,会直接返回

如果发生上面任一种情况时宕机,会导致最多2秒的数据丢失。

发生阻塞时,会打印如下日志:

Asynchronous AOF fsync is taking too lang (disk is busy?).

同时会增加 aof_delayed_fsync 统计项次数(通过 info persistence 查看)

重写机制:

目的:降低AOF文件存储空间大小,加快重新加载速度

重写策略:

  • 超时的数据不再写入
  • 重复的无效的命令以最新的为准(比如:set why why,set why why1以后者为准)
  • 多条写命令合并为一个(比如:lpush list a、lpush list b、lpush list b,可以合并为lpush list a b c)

配置项:

  • auto-aof-rewrite-percentage:触发当前AOF文件大小和上一次重写后AOF文件大小的最小比值
  • auto-aof-rewrite-min-size:运行AOF重写时文件最小体积

相关统计信息:

  • aof_current_size:当前aof文件大小,通过info persistence命令查看
  • aof_base_siz:上次重写后AOF文件大小,通过info persistence命令查看

触发机制:

  • 手动触发:调用bgrewriteaof
  • 自动触发:aof_current_size > auto-aof-rewrite-min-size 并且 (aof_current_size - aof_base_size) / aof_base_size >= auto-aof-rewrite-percentage 时自动触发

三、混合持久化方式

由于 AOF 持久化方式在重启加载数据时效率远远不如 RDB 方式,所以 Redis 在4.0版本后引入了混合持久化方式,配置项为 aof-use-rdb-preamble,yes开启,策略为在生成或写入 AOF 文件时,将 RDB 数据写在前面,AOF 数据追加到后面,在每次启动时先加载 RDB,再加载 AOF ,下面为配置中的说明:

# When rewriting the AOF file, Redis is able to use an RDB preamble in the
# AOF file for faster rewrites and recoveries. When this option is turned
# on the rewritten AOF file is composed of two different stanzas:
#
#   [RDB file][AOF tail]
#
# When loading Redis recognizes that the AOF file starts with the "REDIS"
# string and loads the prefixed RDB file, and continues loading the AOF
# tail.
aof-use-rdb-preamble yes

四、多实例部署

Redis 单线程架构导致无法充分利用多核 CPU 特性,所以可以在一台机器上部署多个 Redis 实例。但是当多个实例同时开启 AOF 重写后,彼此之间会产生对 CPU 和 IO 的竞争,所以可以通过外部程序轮询,依此控制每个实例的 AOF 重写操作执行。

我们依赖的相关监控指标如下:

  • rdb_bgsave_in_progress:bgsave 子进程是否正在运行
  • rdb_last_save_time:当前运行 bgsave 的时间,-1表示未运行
  • aof_enabled:是否开启 AOF 
  • aof_rewrite_in_progress:AOF 重写子进程是否正在运行
  • aof_rewrite_scheduled:在 bgsave 结束后是否运行 AOF 重写
  • aof_current_rewrite_time_sec:当前运行 AOF 重写的时间,-1表示未运行
  • aof_current_size:AOF 文件当前字节数
  • aof_base_size:AOF 上次重写 rewrite 的字节数

代码实现:https://github.com/why444216978/php-redis-aof

运行前看一下 aof 文件更新时间:

why@localhost] /usr/local/var/db/redis$ll
total 48
-rw-r--r--  1 why  staff  9295  5  4 15:31 appendonly.aof
-rw-r--r--  1 why  staff  9295  5  4 15:26 dump.rdb

运行后再看一下 aof 文件更新时间:

[why@localhost] /usr/local/var/db/redis$ll
total 48
-rw-r--r--  1 why  staff  9295  5  4 17:19 appendonly.aof
-rw-r--r--  1 why  staff  9295  5  4 15:26 dump.rdb

成功!

 

 

上一篇:c博客作业01--顺序、分支结构


下一篇:js判断访问浏览器是安卓还是ios还是微信浏览器还是微博