今天主要分享继Redis持久化方式RDB、AOF之后的一些常用的Redis问题定位于优化方式。这里主要CPU、内存、磁盘在三个维度去分析问题!
Fork操作
当Redis做RDB或AOF重写时,一个必不可少的操作就是执行fork操作创建子进程,对于大多数操作系统来说fork是个重量级操作
虽然fork创建的子进程不需要拷贝父进程的物理内存空间,但是会复制父进程的空间内存页表。例如对于10GB的Redis进程,需要复制大约20MB的内存页表,因此fork 操作耗时跟进程总内存量息息相关,如果使用虚拟化技术,特别是Xen虚拟 机,fork操作会更耗时
- 在做 RDB 或 AOF 重写时, fork 是必不可少的
- 对于大多数操作系统来说, fork 是个重量级错误
- fork 会复制符进程的空间内存页表
- 如果使用虚拟化技术, 特别是 Xen 虚拟机, fork 操作会更耗时
fork 耗时问题定位:
- 高流量的 Redis 实例 ops 可达5万以上
- 正常情况 fork 耗时应该是每 GB 消耗 20ms 左右
- 可以用 info stats 命令查看 latest_fork_usec 指标, 获取最近一次 fork 操作耗时, 单位微秒
如何改善 fork 操作的耗时:
- 优先使用物理机或者高效支持 fork 操作的虚拟化技术, 避免使用 Xen
- 控制 Redis 实例最大可用内存, fork 耗时跟内存量成正比, 线上建议每个 Redis 实例内存控制在 10GB 以内
- 合理配置 Linux 内存分配策略, 避免物理内存不足导致 fork 失败, 具体细节见12.1节 “Linux 配置优化”
- 降低 fork 操作的频率, 如适度放宽 AOF 自动触发时机, 避免不必要的全量复制等
子进程开销进程与优化
子进程负责AOF或者RDB文件的重写,它的运行过程主要涉及CPU、内存、硬盘三部分的消耗
CPU
- CPU 开销分析。子进程负责把进程内的数据分批写入文件, 这个过程属于 CPU 密集操作, 通常子进程对单核 CPU 利用率接近90%
- CPU 消耗优化。Redis 是 CPU 密集型服务, 不要做绑定单核 CPU 操作。由于子进程非常消耗 CPU, 会和父进程产生单核资源竞争.
- 不要和其他 CPU 密集型服务部署在一起, 造成 CPU 过度竞争
- 如果部署多个 Redis 实例, 尽量保证同一时刻只有一个子进程执行重写工作, 具体细节见5.4节多实例部署
硬盘
- 硬盘开销分析
子进程主要职责是把 AOF 或者 RDB 文件写入硬盘持久化。势必造成硬盘写入压力。根据 Redis 重写 AOF/RDB 的数据量, 结合系统工具如 sar、iostat、iotop 等, 可分析出重写期间硬盘负载情况
- 硬盘开销优化
- 不要和其他高硬盘负载的服务部署在一起。如: 存储服务、消息队列服务等
- AOF 重写时会消耗大量硬盘 IO, 可以开启配置 no-appendfsync-on-rewrite, 默认关闭。表示在 AOF 重写期间不做 fsync 操作
- 当开启 AOF 功能的 Redis 用于高流量写入场景时, 如果使用普通机械磁盘, 写入吞吐一般在 100MB/s 左右, 这时 Redis 实例的瓶颈主要在 AOF 同步硬盘上
- 对于单机配置多个 Redis 实例的情况, 可以配置不同实例分盘存储 AOF 文件, 分摊硬盘写入压力
配置 no-appendfsync-on-rewrite=yes 时, 在极端情况下可能丢失整个 AOF 重写期间的数据,需要根据数据安全性决定是否配置
内存
- 内存消耗分析
子进程通过 fork 操作产生, 占用内存大小等同于父进程, 理论上需要两倍的内存来完成持久化操作, 但 Linux 有写时复制机制 (copy-on-write)。父子进程会共享相同的物理内存页, 当父进程处理写请求时会把要修改的页创建副本, 而子进程在 fork 操作过程中共享整个父进程内存快照。
- 内存消耗监控
- RDB 重写: 被修改的内存页可以等价认为 RDB 重写的消耗
- AOF 重写: 被修改的内存页 + AOF 重写缓冲区
- 内存消耗优化
- 如果部署多个 Redis 实例, 尽量保证同一时刻只有一个子进程在工作
- 避免在大量写入时做子进程重写操作, 这样将导致父进程维护大量页副本, 造成内存消耗
Transparent Huge Pages(THP) 是 Linux kernel 在2.6.38增加的功能, 支持 huge page (2MB) 页分配, 会降低 fork 速度, 默认开启. 当开启时, 在 fork 后会大幅增加重写期间父进程的内存消耗, 建议关闭:
sudo echo never>/sys/kernel/mm/transparent_hugepage/enabled
AOF追加阻塞
当开启AOF持久化时,常用的同步硬盘的策略是everysec,用于平衡性 能和数据安全性。对于这种方式,Redis使用另一条线程每秒执行fsync同步 硬盘。当系统硬盘资源繁忙时,会造成Redis主线程阻塞!如下图所示
阻塞流程分析:
- 如果距上次同步成功时间在2秒内,主线程直接返回
- 如果距上次同步成功时间超过2秒,主线程将会阻塞,直到同步操作完 成。
- 主线程负责写入AOF缓冲区
- AOF线程负责每秒执行一次同步磁盘操作,并记录最近一次同步时 间
- 主线程负责对比上次AOF同步时间:
通过对AOF阻塞流程可以发现两个问题:
- everysec配置最多可能丢失2秒数据,不是1秒
- 如果系统fsync缓慢,将会导致Redis主线程阻塞影响效率
AOF阻塞问题定位:
- 每当发生AOF追加阻塞事件发生时,在info Persistence统计中, aof_delayed_fsync指标会累加,查看这个指标方便定位AOF阻塞问题。
- AOF同步最多允许2秒的延迟,当延迟发生时说明硬盘存在高负载问 题,可以通过监控工具如iotop,定位消耗硬盘IO资源的进程
- 发生 AOF 阻塞时, Redis 输出如下日志, 用于记录 AOF fsync 阻塞导致拖慢 Redis 服务的行为
Asynchronous AOF fsync is taking too long (disk is busy). Writing the AOF bufferwithout waiting for fsync to complete, this may slow down Redis
- 每当发生 AOF 追加阻塞事件发生时, 在info Persistence 统计中, aof_delayed_fsync 指标会累加, 查看这个指标方便定位 AOF 阻塞问题
- AOF 同步最多允许2秒的延迟, 当延迟发生时说明硬盘存在高负载问题, 可以通过监控工具如 iotop, 定位消耗硬盘 IO 资源的进程
更多原创技术分享,关注公众号:码农架构