RDB
RDB持久化是把当前进程数据生成快照保存到硬盘的过程,触发RDB持久化过程分为手动触发和自动触发。
触发机制
手动触发分别对应save 和bgsave
- save 阻塞redis 服务器,直到RDB过程完成,对于内存比较大的实例会造成长时间阻塞,不建议线上环境使用。
- bgsave,fork 子进程完成RDB,阻塞只发生在fork 阶段,一般时间很短。
自动触发
save m n m表示秒内数据集存在n次修改时,自动触发bgsave。
从节点执行全量复制操作,主节点自动执行bgsave生成RDB文件并发送从节点。
执行 debug reload 命令会重新加载redis,会自动触发save操作。
默认执行shutdown,如果没有开启AOF 会自动执行bgsave。
流程
执行bgsave 命令,父进程判断当前是否存在正在执行的子进程,如RDB/AOF 子进行,如果存在bgsave 直接返回结果。
父进程执行fork,fork 过程中会阻塞父进程,通过info stats 查看latest_fork_use 选项,可以获取最近一次fork 的耗时,单位是微秒。
1
2
3
4
5127.0.0.1:6379> INFO stats
# Stats
……
latest_fork_usec:612
……
父进程fork 完,bgsave 命令会返回
background saveing started
信息,并不在阻塞父进程,可以继续响应其他命令。1
2127.0.0.1:6379> BGSAVE
Background saving started
子进程创建RDB 文件,根据父进程内存生成临时快照文件,完成后对原有文件进行原子替换。执行lastsave 命令可以获取最后一次生成RDB 的时间,对应 info 统计中的rdb_last_save_time 选项。
1
2127.0.0.1:6379> LASTSAVE
(integer) 1556158402
- 进程发送信号给父进程表示完成,父进程更新统计信息,具体见 info Persistence下的 rdb_* 相关选项。
1 | 127.0.0.1:6379> INFO stats |
配置
RDB 优缺点
RDB的优点:
- RDB是一个紧凑压缩的二进制文件,代表Redis在某个时间点上的数据快照。非常适用于备份,全量复制等场景。比如每6小时执行bgsave备份,并把RDB文件拷贝到远程机器或者文件系统中(如hdfs),用于灾难恢复。
- Redis加载RDB恢复数据远远快于AOF的方式。
RDB的缺点:
- RDB方式数据没办法做到实时持久化/秒级持久化。因为bgsave每次运行都要执行fork操作创建子进程,属于重量级操作,频繁执行成本过高。
- RDB文件使用特定二进制格式保存,Redis版本演进过程中有多个格式的RDB版本,存在老版本Redis服务无法兼容新版RDB格式的问题。针对RDB不适合实时持久化的问题,Redis提供了AOF持久化方式来解决。
AOF
AOF(append only file)持久化:以独立日志的方式记录每次写命令,重启时再重新执行AOF文件中的命令达到恢复数据的目的。AOF的主要作用是解决了数据持久化的实时性,目前已经是Redis持久化的主流方式。理解掌握好AOF持久化机制对我们兼顾数据安全性和性能非常有帮助。
使用
配置
1 | cat redis.conf | grep ^append |
AOF的工作流程操作:命令写入(append)、文件同步(sync)、文件重写(rewrite)、重启加载(load)
流程
- 所有写入追加到 aof_buf 中。
- AOF 缓冲区根据对应的策略向硬盘做同步操作。
- 随着AOF文件的变大,需要定期对AOF 文件进行重写,达到压缩目的。
- 当redis 服务器重启时,可以加载AOF文件进行数据恢复。
为什么采用文本方式写入
AOF 写入采用文本方式,理由如下:
- 兼容性好
- 开启AOF 后,所有写操作都是追加的,避免了二次处理开销。
- 文本协议具有可读性,方便直接修改和处理。
为什么要把命令追加到AOF_BUF
redis 使用单线程响应命令,如果每次写AOF 都直接追加到磁盘,性能会很差,先写入缓存区,然后在同步到磁盘性能会有很大提升。
AOF 的文件同步
可配置值 | 说明 |
---|---|
always | 命令写人aof_ buf 后调用系统fsync操作同步到AOF文件,fsync 完成后线程返回 |
everysec | 命令写入aof_ buf后调用系统write操作,write完成后线程返回。fsync 同步文件操作由专门线程每秒调用- -次 |
no | 命令写人aof_ buf 后调用系统write操作,不对AOF文件做fsync同步,同步硬盘操作由操作系统负责,通常同步周期最长30秒 |
系统调用write和fsync说明:
- write操作会触发延迟写(delayed write)机制。Linux在内核提供页缓冲区用来提高硬盘IO性能。write操作在写入系统缓冲区后直接返回。同步硬盘操作依赖于系统调度机制,例如:缓冲区页空间写满或达到特定时间周期。同步文件之前,如果此时系统故障宕机,缓冲区内数据将丢失。
- fsync针对单个文件操作(比如AOF文件),做强制硬盘同步,fsync将阻塞直到写入硬盘完成后返回,保证了数据持久化。
AOF 重写
随着不断写入AOF 文件不断变大,redis 通过AOF 重写机制压缩文件体积。把进程内的数据转化为写命令同步到新的AOF文件。
重写AOF的优势
- 降低文件占用,提升redis 加载速度。
AOF 分手动和自动触发
- 手动: bgrewriteaof
- 自动,根据配置文件中的下面2个参数确定自动触发时间
1 | # cat redis.conf |grep ^auto-aof |
自动触发时机=aof_current_size>auto-aof-rewrite-minsize&&(aof_current_size-aof_base_size)/aof_base_size>=auto-aof-rewritepercentage其中aof_current_size和aof_base_size可以在info Persistence统计信息中查。
流程说明
执行AOF 重写请求,如果当前有进程正在执行AOF重写,请求不执行并返回
1
ERR Background append only file rewriting already in progre
如果当前正在执行bgsave,重写命令延迟到bgsave 完成后执行,返回
1
Background append only file rewriting schedul
父进程fork 子进程,开销等同于bgsave。
- 主进程 fork 后,继续执行其他命令,所有修改依然写入AOF 缓冲区,并根据appendfsync 策略同步到磁盘。
- 由于fork 操作运用写时复制技术,子进程只贡献fork 操作时的内存数据,由于父进程依然响应命令,redis 使用
AOF重写缓冲区
保存这部分新数据,防止新AOF文件生成时丢失这部分数据。
子进程根据内存快照,按命令合并规则写入到新的AOF 文件。每次批量写入磁盘数据由配置
置aof-rewrite-incremental-fsync
控制,默认为32MB,防止单次刷盘数据过多造成硬盘阻塞。新AOF 文件写入后,子进程发送信号给父进程,更新统计信息。
父进程把AOF 重写缓存区的数据写入到新AOF文件。
使用新AOF文件替换老文件,完成AOF重写。
问题定位与优化
fork 操作耗时问题排查
redis 做 RDB 或 AOF 重写时,会进行fork,虽然fork创建的子进程不需要拷贝父进程的物理内存空间,但是会复制父进程的空间内存页表。例如对于10GB的Redis进程,需要复制大约20MB的内存页表,因此fork操作耗时跟进程总内存量息息相关,如果使用虚拟化技术,特别是Xen虚拟机,fork操作会更耗时。
如何改善fork操作的耗时:
1)优先使用物理机或者高效支持fork操作的虚拟化技术,避免使用Xen。
2)控制Redis实例最大可用内存,fork耗时跟内存量成正比,线上建议每个Redis实例内存控制在10GB以内。
3)合理配置Linux内存分配策略,避免物理内存不足导致fork失败。
4)降低fork操作的频率,如适度放宽AOF自动触发时机,避免不必要的全量复制等。
子进程开销监控和优化
CPU
CPU开销分析。子进程负责把进程内的数据分批写入文件,这个过程属于CPU密集操作,通常子进程对单核CPU利用率接近90%.
CPU消耗优化。Redis是CPU密集型服务,不要做绑定单核CPU操作。由于子进程非常消耗CPU,会和父进程产生单核资源竞争。
不要和其他CPU密集型服务部署在一起,造成CPU过度竞争。
如果部署多个Redis实例,尽量保证同一时刻只有一个子进程执行重写工作。
内存
内存消耗分析。子进程通过fork操作产生,占用内存大小等同于父进
程,理论上需要两倍的内存来完成持久化操作,但Linux有写时复制机制(copy-on-write)。父子进程会共享相同的物理内存页,当父进程处理写请求时会把要修改的页创建副本,而子进程在fork操作过程中共享整个父进程内存快照。内存消耗监控。
磁盘
通过iostat 等监控磁盘负载。