持久化阻塞
对于开启了持久化功能的Redis节点, 需要排查是否是持久化导致的阻塞。 持久化引起主线程阻塞的操作主要有: fork阻塞、 AOF刷盘阻塞、HugePage写操作阻塞。
fork操作
当Redis做RDB或AOF重写时, 一个必不可少的操作就是执行fork操作创建子进程, 对于大多数操作系统来说fork是个重量级错误。 虽然fork创建的子进程不需要拷贝父进程的物理内存空间, 但是会复制父进程的空间内存页表。 例如对于10GB的Redis进程, 需要复制大约20MB的内存页表, 因此fork操作耗时跟进程总内存量息息相关, 如果使用虚拟化技术, 特别是Xen虚拟机, fork操作会更耗时。
fork耗时问题定位: 对于高流量的Redis实例OPS可达5万以上, 如果fork操作耗时在秒级别将拖慢Redis几万条命令执行, 对线上应用延迟影响非常明显。 正常情况下fork耗时应该是每GB消耗20毫秒左右。 可以在info stats统计中查latest_fork_usec指标获取最近一次fork操作耗时, 单位微秒。
如何改善fork操作的耗时:
1) 优先使用物理机或者高效支持fork操作的虚拟化技术, 避免使用Xen。
2) 控制Redis实例最大可用内存, fork耗时跟内存量成正比, 线上建议每个Redis实例内存控制在10GB以内。
3) 合理配置Linux内存分配策略, 避免物理内存不足导致fork失败
4) 降低fork操作的频率, 如适度放宽RDB自动触发时机, 避免不必要的全量复制等。
[root@localhost ~]# /usr/local/redis//bin/redis-cli bgsave
Background saving started
[root@localhost ~]# tail -f /usr/local/redis/redis_6379.log
1320:M 06 May 2020 21:57:12.690 * Background saving started by pid 20322
20322:C 06 May 2020 21:57:12.771 * DB saved on disk
20322:C 06 May 2020 21:57:12.772 * RDB: 4 MB of memory used by copy-on-write
1320:M 06 May 2020 21:57:12.781 * Background saving terminated with success
[root@localhost ~]# /usr/local/redis/bin//redis-cli info stats | grep la
latest_fork_usec:37470
fork阻塞
fork操作发生在RDB和AOF重写时, Redis主线程调用fork操作产生共享内存的子进程, 由子进程完成持久化文件重写工作。 如果fork操作本身耗时过长, 必然会导致主线程的阻塞。可以执行info stats命令获取到latest_fork_usec指标, 表示Redis最近一次
fork操作耗时, 如果耗时很大, 比如超过1秒, 则需要做出优化调整, 如避免使用过大的内存实例和规避fork缓慢的操作系统等。
AOF刷盘阻塞
当我们开启AOF持久化功能时, 文件刷盘的方式一般采用每秒一次, 后台线程每秒对AOF文件做fsync操作。 当硬盘压力过大时, fsync操作需要等待, 直到写入完成。 如果主线程发现距离上一次的fsync成功超过2秒, 为了数据安全性它会阻塞直到后台线程执行fsync操作完成。 这种阻塞行为主要是硬盘压力引起, 可以查看Redis日志识别出这种情况, 当发生这种阻塞行
为时, 会打印如下日志:
Asynchronous AOF fsync is taking too long (disk is busy). Writing the AOF
buffer without waiting for fsync to complete, this may slow down Redis.
也可以查看info persistence统计中的aof_delayed_fsync指标, 每次发生datasync阻塞主线程时会累加。
当开启AOF持久化时, 常用的同步硬盘的策略是everysec, 用于平衡性能和数据安全性。 对于这种方式, Redis使用另一条线程每秒执行fsync同步硬盘。 当系统硬盘资源繁忙时, 会造成Redis主线程阻塞, 如图5-5所示。
阻塞流程分析:
1) 主线程负责写入AOF缓冲区。
2) AOF线程负责每秒执行一次同步磁盘操作,并记录最近一次同步时间(AOF线程复制统计时间,主线程负责对比和上次时间差)
3) 主线程负责对比上次AOF同步时间:
·如果距上次同步成功时间在2秒内, 主线程直接返回。
·如果距上次同步成功时间超过2秒, 主线程将会阻塞, 直到同步操作完成。
通过对AOF阻塞流程可以发现两个问题:
1) everysec配置最多可能丢失2秒数据, 不是1秒。
2) 如果系统fsync缓慢, 将会导致Redis主线程阻塞影响效率。
AOF阻塞问题定位:
1) 发生AOF阻塞时, Redis输出如下日志, 用于记录AOF fsync阻塞导致拖慢Redis服务的行为:
Asynchronous AOF fsync is taking too long (disk is busy). Writing the AOF buffer
without waiting for fsync to complete, this may slow down Redis
2) 每当发生AOF追加阻塞事件发生时, 在info Persistence统计中,aof_delayed_fsync指标会累加, 查看这个指标方便定位AOF阻塞问题。
[root@localhost ~]# /usr/local/redis/bin/redis-cli info | grep aof_delay
aof_delayed_fsync:0
3) AOF同步最多允许2秒的延迟, 当延迟发生时说明硬盘存在高负载问题, 可以通过监控工具如iotop, 定位消耗硬盘IO资源的进程。
运维提示
硬盘压力可能是Redis进程引起的, 也可能是其他进程引起的, 可以使用iotop查看具体是哪个进程消耗过多的硬盘资源。
HugePage写操作阻塞
子进程在执行重写期间利用Linux写时复制技术降低内存开销, 因此只有写操作时Redis才复制要修改的内存页。 对于开启Transparent HugePages的操作系统, 每次写命令引起的复制内存页单位由4K变为2MB, 放大了512倍, 会拖慢写操作的执行时间, 导致大量写操作慢查询。 例如简单的incr命令也会出现在慢查询中。