fork
fork操作
- 同步操作:执行 bgsave、bgrewriteaof都是首先执行一个fork操作,fork只是做一个内存页的拷贝,大部分情况下速度都是很快的;但是本次的 fork 操作卡在某个地方将会阻塞Redis的主线程。
- 与内存量息息相关:实例内存越大,fork 耗时越长(与机器类型有关)。
- info:latest_fork_usec(上次执行 fork 的微秒数),这个数值比较大将会阻塞Redis。
改善fork
- 优先使用物理机或者高效支持 fork 操作的虚拟化技术,避免使用 Xen。
- 控制Redis实例最大可用内存(maxmemory),fork耗时跟内存量成正比,线上建议 每个Redis实例内存控制在10GB以内。
- 合理配置 Linux 内存分配策略:vm.overcommint_memory=1(默认值是0,当发现没有足够内存做内存分配时就不去分配,对于 fork 来说会造成 fork 的阻塞)。
- 降低fork操作的频率,如适度放宽 AOF 自动触发时机,避免不必要的全量复制等。
子进程
子进程开销和优化
CPU
- 开销:RDB 和 AOF 文件生成(即文件的重写),例如执行basave、bgrewriteaof 的时候,最终将内存的数据写入硬盘,不仅存在内存、硬盘的开销;也存在CPU的开销,通常这个子进程的CPU开销会在90%以上(因为写入是一个非常集中的过程),所以是CPU密集型。
- 优化:
1、不做CPU绑定(不要Redis的进程绑定在一个CPU上,否则子进程和主进程将会集中消耗CPU,会对主进程造成很大的影响);
2、不要和CPU密集型的应用部署在一起(减少CPU资源的竞争)。
内存
- 开销:
子进程通过fork操作产生,占用内存大小等同于父进程,理论上需要两倍的内存来完成持久化操作,但Linux有写时复制机制 (copy-on-write)。父子进程会共享相同的物理内存页,当父进程处理写请 求时会把要修改的页创建副本,而子进程在fork操作过程中共享整个父进程内存快照。 - 优化:
1、不允许单机多部署的时候产生大量的重写;
2、在主进程写入量比较少的时候做 AOF 重写或 bgsave;
3、建议设置“sudo echo never>/sys/kernel/mm/transparent_hugepage/enabled”关闭THP(Linux kernel在2.6.38内核增加了Transparent Huge Pages(THP),支持 huge page(2MB)的页分配,默认开启)。
硬盘
- 开销:AOF 和 RDB 文件写入,可以结合 iostat、iotop工具进行分析。
- 优化:
1、不要和高硬盘负载服务部署在一起:存储服务、消息队列等;
2、AOF重写时会消耗大量硬盘IO,可以开启配置no-appendfsync-onrewrite,默认关闭。表示在AOF重写期间不做fsync操作;
3、根据写入量决定磁盘类型:例如SSD;
4、单机多实例持久,可以配置不同实例分盘存储 AOF文件,分摊硬盘写入压力。
AOF追加阻塞
使用everysec做刷盘策略的流程
阻塞流程分析:
- 主线程负载写入 AOF 缓冲区。
- AOF 线程负责每秒执行一次同步磁盘操作,并记录最近一次同步时间。
- 主线程负责对比上次 AOF 同步时间:
如果距上次同步成功时间在2秒内,主线程直接返回。
如果距上次同步成功时间超过2秒,主线程将会阻塞,直到同步操作完成。
AOF阻塞引发的两个问题
- everysec 配置最多可能丢失2秒数据,不是1秒。
- 如果系统 fsync 缓慢,将会导致 Redis 主线程阻塞影响效率。
AOF阻塞问题定位
- 发生 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.
- 每当发生 AOF 追加阻塞事件发生时,在 info Persistence 统计中,aof_delayed_fsync 指标会累加,查看这个指标方便定位 AOF 阻塞问题。
redis> info persistence
.....
.....
aof_delayed_fsync: 100
.....
.....
- AOF 同步最多允许2秒的延迟,当延迟发生时说明硬盘存在高负载问题,可以通过监控工具如iotop,定位消耗硬盘IO资源的进程。
多实例部署
当多个实例开启AOF重写后,彼此之间会产 生对CPU和IO的竞争。
对于单机多Redis部署,如果同一时刻运行多个子进程,对当前系统影响将非常明显,因此需要采用一种措施,把子进程工作进行隔离。
Redis在info Persistence中为我们提供了监控子进程运行状况的度量指标。
属性名 | 属性值 |
---|---|
rdb_basave_in_progress | bgsave 子进程是否正常运行 |
rdb_current_bgsave_time_sec | 当前运行 bgsave 的时间,-1 表示未运行 |
aof_enabled | 是否开启 AOF 功能 |
aof_rewrite_in_progress | AOF 重写子进程是否正在运行 |
aof_rewrite_scheduled | 在 bgsave 结束后是否运行 AOF 重写 |
aof_current_rewrite_time_sec | 当前运行 AOF 重写时间,-1 表示未运行 |
aof_current_size | AOF 文件当前字节数 |
aof_base_size | AOF 上次重写 rewrite 的字节数 |
基于以上指标,可以通过外部程序轮询控制 AOF 重写操作的执行,流程说明:
- 外部程序定时轮询监控机器(machine)上所有Redis实例。
- 对于开启 AOF 的实例,查看(aof_current_size — aof_base_size)/aof_base_size 确认增长率。
- 当增长率超过特定阈值(如100%),执行 bgrewriteaof 命令收到触发当前实例的 AOF 重写。
- 运行期间循环检查 aof_rewrite_in_progress 和 aof_current_rewrite_time_sec 指标,知道 AOF 重写结束。
- 确认实例 AOF 重写完成后,在检查其他实例并重复 2~4 步操作。从而保证机器内每个Redis实例 AOF 重写串行化执行。