注意: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
触发流程:
- 执行bgsave命令,如果正在执行bgsave命令则直接返回
- 父进程执行fork操作创建子进程,fork操作时会阻塞主进程
- 父进程fork完后主进程不再阻塞,可执行其它命令
- 子进程创建RDB文件,写入完成后替换旧文件
- 子进程发送信号通知父进程表示完成,父进程更新统计信息
相关统计信息:
- 上次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
工作流程:
- 执行 AOF 请求,如果正在执行 AOF 重写或 bgsave 直接返回
- 父进程执行 fork 创建子进程,开销等同 bgsave,使用写时复制技术,子进程共享 fork 操作时的内存数据,接下来父进程的操作保存到重写缓冲区(aof_rewrite_buf)中
- 子进程批量写入新 AOF 文件中,每次批量写入大小由 aof-rewrite-incremental-fsync 配置控制,开启后每次写入32M,防止单次刷盘数据过大造成磁盘阻塞
- 新 AOF 文件重写完成后,发送信号给父进程,父进程更新统计信息
- 父进程将 aof_rewrite_buf 中的数据写入到新的 AOF 文件中
- 使用新的 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
成功!