Redis - 持久化
之前提到,准确的说 Redis 是一个基于内存的数据库,所以也会提供持久化的功能,避免进程退出导致数据丢失的问题。
持久化
先说说持久化在系统层面的模型或者流程,不管是文件持久化、Redis 或者其他应用的持久化都是类似的,都是应用怎么把数据放到磁盘上。
进程内产生的数据一般需要经过上述的步骤进行持久化。
1、数据从应用进程的内存到内核缓存
应用调用内核函数 write 进行持久化,但该函数只是将数据写入操作系统内核的缓存(pagecache),不会真正的将数据落盘到硬盘,所以该步骤是很快的。
2、数据从内核缓存到硬盘
应用调用内核函数 fsync 或者操作系统自行调用 fsync 将数据刷到硬盘,该步骤真正将数据落盘的硬盘,需进行 io 操作,耗时。
3、数据从硬盘缓存到硬盘物理空间
这里就完全是硬盘自身的行为了,应用或者操作系统都无法控制。一般来说,硬盘也不会针对写入操作开启硬盘缓存,因为这提高了数据丢失的风险。除非是备用供电做得很充分,可考虑开启。
在上层应用分析时,该阶段可以不做考虑,因为无法控制。
所以,核心要理解的是第一步和第二步,可以在这两步调整持久化的策略,以达到不同的目的。
Redis 的持久化策略
Redis 提供了两种持久化策略 RDB(redis database)和 AOF(append only file)。
RDB
将某一时刻的内存快照数据持久化。
触发时机
手动触发,提供两个命令:
save:阻塞当前 Redis 进程,直到持久化完成,线上不推荐
bgsave:Redis 进程执行 fork 操作,在子进程持久化,只是在 fork 期间会阻塞 Redis 进程,后续不会阻塞
fork 相关,有位大哥已经说得比较清楚了:链接
bgsave 就是针对 save 的阻塞优化,save 命令现在已经废弃
自动触发,内部就是用的 bgsave,自动场景如下
1、提供配置,用户可以制定自动触发机制
2、从节点执行全量复制,主节点会自动生成 rdb 文件,发送给从节点
3、shutdown 命令执行时,如果没有开启 AOF 知就好则自动执行 bgsave
执行流程
save 已经废弃,就不再说明。主要说下 bgsave。
1、Redis 进程(父进程)调用内核函数 fork,创建一个共享内存的子进程(fork 自身的写时复制机制,以内存页为单位进行)
fork 期间,Redis 进程阻塞,不会响应 Redis 命令,直到 fork 完成。fork 阻塞时间跟父进程的内存空间大小相关,1G 内存大约需要 20ms。
2、子进程负责持久化并生成 rdb 格式的数据文件
3、子进程通知父进程持久化结果并退出
RDB 文件处理
Redis 默认采用 LZF 算法对生成的 RDB 文件进行压缩处理,压缩后的文件远小于内存大小。压缩解压带来的 CPU 开销相较于空间占用和网络传输时带来的好处是可以接受的,因此线上建议开启。
RDB 优缺点
优点:
1、持久化的 rdb 文件是经过压缩后的紧凑格式,占用空间小,适于备份,全量复制
2、Redis 加载 rdb 的速度远远快于 AOF 方式
缺点:
1、只能针对某一时刻的快照备份,该时刻后面的数据不能得到及时备份,可能丢失。无法做到实时持久化
2、如果频繁的进行持久化,则需要频繁调用 fork,会影响 Redis 性能
AOF
AOF 即就是为了解决 RDB 不能实时持久化的问题而诞生。AOF 将每一个会影响数据集的写操作以日志的方式持久化记录,需要恢复数据时,进行操作重放即可。
触发时机
从 AOF 的定位看,AOF 是用于实时持久化,每一个写命令到达时均会触发。
执行流程
1、每一条影响数据集的写命令执行后都会追加到 AOF 缓存
2、Redis 根据一定的策略将 AOF 缓存的命令持久化到 AOF 文件
支持三种策略:
1)Always
Redis 命令执行线程将 AOF 缓存中每个命令 fsync 持久化
2)no
Redis 命令执行线程只将 AOF 缓存中的命令 write 持久化,后续不管了。
操作系统一般每隔 30s 会自动调用 sync。
3)every second
Redis 命令执行线程将 AOF 缓存中的命令 write 持久化,另外线程每隔一秒进行 sync 持久化。
从这里看,AOF 最多会丢失 1s(没来得及落盘)的数据,但实际最多会丢失 2s 的数据,因为命令执行线程每次追加命令到 AOF 缓存时都会检查上一次 sync 时间,若超过 2s 则不再响应命令(比如说另外线程挂掉),所以最多会丢失 2s。
大家知道 Redis 命令执行线程是单线程,需要保证执行效率。兼顾效率和数据安全,建议选择策略 3),默认也是该策略。
3、AOF 文件以追加的方式记录命令,文件势必会越来越大,通过重写解决
实际上也是使用 fork 的方式基于当前时刻的快照重新生成一份 AOF 文件,替换掉以前的文件。当前快照 + 后续写命令即可得到完整数据。
AOF 优缺点
优点:
1、支持实时持久化
缺点:
1、AOF 文件保存的原始命令文本,所以文件较大
2、因为有追加 AOF 缓存,write 调用,性能相较 RDB 差一点
AOF 是主要的持久化方式,但官方是强烈建议 RDB 和 AOF 同时使用,以便满足不同的目的。
RDB:用于定期备份,数据恢复
AOF:实时持久化,保证数据不丢失。
官方也有计划合并 RDB 和 AOF 两种持久化方式,但是是一个长久的事情。目前以 Redis6.0 来看,重写前 AOF 文件都是记录的原始命令,经重写后就变成了类似 RDB 文件的二进制格式,是有这么个现象,但没有深究。
问题定位与优化
持久化一直是影响 redis 性能的高发地。
fork 操作
fork 是一个重量级操作,即便有了写时复制的机制,但 fork 时还是会拷贝虚拟内存空间,这也是有一定开销的。10G 内存空间的 Redis 进程,fork 时会复制大约 20M 的虚拟内存空间。之后,物理内存页发生变化,也会为子进程创建新的物理内存页,也是一笔开销。
优化建议:
1)优先使用物理机或者高效支持 fork 操作的虚拟化技术
2)控制 Redis 实例最大可用内存
3)合理配置 linux 内存分配策略,避免物理内存不足导致 fork 失败
4)降低 fork 操作的频率
子进程操作
无论是 RDB 还是 AOF 文件重写,都是将内存数据写入磁盘,涉及 CPU、内存、磁盘资源。
1、cpu
需要将数据分批查询出来写入文件,是 CPU 密集型操作
2、内存
虽然提供了写时复制的机制,但还是会涉及内存的拷贝、分配等
3、磁盘
最终写入磁盘,势必会对此案带来压力。
优化建议:
1、控制子进程数量,不要对主进程产生资源争用
2、部署时注意,不要和其他相同资源需求的应用部署在一起
AOF 阻塞
主进程发现 2s 未进行 AOF 同步,则不再相应外部请求,导致阻塞。
优化建议:
1、资源给充足,避免出现阻塞
2、监控到位,及时发现。
redis 会记录阻塞发生次数,可以监控该指标,也可以监控磁盘等资源负载。
评论