Redis 支持RDB和AOF两种持久化机制,之所以存在两种,是因为它们各自适用于不同的场景、不同的阶段。我接下来对其进行介绍,并分析我们在实际场景中如何使用配置。
RDB
有两个重要命令,save和bgsave,一个是同步,一个是异步,完成后会在指定的目录生成一份压缩文件(默认是dump.rdb,使用LZF算法,可以通过dbfilename配置,dir配置文件夹)。
- save: 阻塞当前Redis服务器、直到RDB过程完成,因为会阻塞线程,所以基本被废弃了。
- bgsave: Redis主线程会进行fork操作,创建子进程,RDB持久化过程由子进程负责,虽然也会有阻塞,但只阻塞fork阶段,这个命令是对save的一个改进
- 自动保存,在程序中以下情景会自动触发bgsave
- 使用save相关配置,如
"save m n"
,表示m秒内数据存在n次修改时自动触发,可以配置多个规则 - 如果从节点执行全量复制,那么master节点会自动执行bgsave,然后将RDB文件发给从节点
- 执行debug reload 命令重新加载Redis时,也会触发
- 在shutdown命令执行后,如果没有开启AOF持久化功能,会触发bgsave
- 使用save相关配置,如
优势:
- 是一个压缩后的二进制文件,代表某个时间点的快照,特别适合发送到其他节点。
- Redis加载RDB的速度远远快于AOF
缺陷
- 没办法实时的持久化,bgsave是一个重量级的操作,不可能频繁执行
- RDB文件使用特定格式保存
AOF
为了应对RDB的缺点,AOF方式产生。以独立日志的方式记录每次命令,重启的时候再重新执行AOF文件中的命令达到恢复数据的目的。
开启
默认是不开启的,需要配置 appendonly yes,默认文件名是appendonly.aof,路径与RDB一致。工作流程
- 将写入命令追加到aof_buf缓冲区中
- AOF缓冲区根据对应的策略向硬盘做同步工作fsync
- 随着AOF文件越来越大,需要定期重写,达到压缩的目的
- 当redis重启时,可以加载AOF文件进行数据恢复
文件格式
为文本协议格式,也就是之前讲过的通信协议的格式,如:·*3\r\n$3\r\nset\r\n$5\r\nhello\r\n$5\r\nworld\r\n
之所以采用文本协议,是因为其兼容性好,而且可读性比较强,可以直接修改文件同步: appendfsync配置
- always 写入aof_buf后立即同步到文件,fsync完成后线程返回
- everysec 建议的配置,命令写入aof_buf后调用系统write操作,完成后线程返回。fsync同步文件操作,由另一个线程每秒调用一次。
- no 写入aof_buf后调用系统write操作,不对AOF文件做fsync同步,同步的步骤由系统来做,最长30s
- write:利用页缓冲区来提高硬盘IO性能,会有短暂的延迟,如果真正同步之前发生宕机,则会丢失数据。因此系统同时提供了fsync、fdatasync等同步函数,可以强制操作系统立刻将缓冲区中的数据写入到硬盘里,从而确保数据的安全性
- fsync:针对单个文件操作(比如AOF文件),做强制硬盘同步,fsync将阻塞直到写入硬盘后返回。
重写机制,重写采用了以下方法实现了压缩
- 进程内超时的数据不再写入
- 无效命令不再写入(也就是中间过程)
- 合并多条命令,比如lpush list a,lpush list b -> lpush list a b c.
- 手动触发: bgrewriteaof
- 自动触发: auto-aof-rewrite-min-size和auto-aof-rewrite-percentage
- 重写过程: 主进程fork一个新进程,并使用AOF重写缓冲区保存重写期间的数据。重写完成后,父进程吧重写缓冲区的数据刷新到新的文件。
重启加载
记住,优先加载AOF
运维优化
fork操作
fork操作是一个重量级操作,子进程会复制父进程的空间内存页表,在搞ops情况下有可能阻塞大量的命令。- 优先使用物理机,或者高效的虚拟化技术
- 控制Redis实例最大可用内存,耗时时间跟内存(内存页)成正比
- 合理配置Linux内存分配
- 降低fork的频率,也就是放款AOF自动触发的机制
子进程
- cpu 在子进程将进程中的文件分批写入的时候消耗比较大
- Redis是密集型服务,所以不要做单核绑定(子进程会抢占),可以使用多实例部署的方式
- 内存,子进程共享父进程的内存快照,在新命令时,父进程会创建内存页副本,这一部分是增加的内存(AOF还有重写缓冲区)。
- 因此对于内存,尽量保证只有一个子进程,另外不要再大量写入时触发重写操作。
AOF阻塞
在子线程同步磁盘时,主线程会不断对比上次AOF同步时间,如果超过2S(everysec方式),则会阻塞等待。如果发生阻塞,很可能是磁盘IO存在高负荷,导致同步变慢。