fork 操作

fork 操作

Redis 无论使用 RDB 还是 AOF 均需要使用到 fork 子进程操作。fork 操作不需要拷贝父级进程的物理内存空间,但会复制父进程的空间内存表。

例如:对于 10 GB 的 Redis 进程,需要复制大约 20 MB 的内存页表。

因此:fork 操作耗时与进程总内存息息相关。如果使用虚拟化技术,特别是Xen虚拟机,fork操作会更耗时。

fork 耗时定位

例如高流量的 Redis 实例(OPS 可达 5W 以上),如果 fork 操作耗时将在秒级内拖慢几万命令的执行,对生产环境的影响非常明显。

正常情况下 fork 耗时应该是每GB消耗20毫秒左右。可以在 info stats 统 计中查 latest_fork_usec 指标获取最近一次 fork 操作耗时,单位微秒。

优化 fork 耗时

  • 优先使用物理机或者高效支持 fork 操作的虚拟化技术,避免使用 Xen。
  • 控制 Redis 实例的最大可用内存,fork 耗时与内存量成正比,线上建议:<= 10GB / 实例
  • 合理配置 Linux 的内存分配策略,避免物理内存不足引起的 fork 失败。
  • 降低 fork 的操作频率,如调整 RDB 、AOF 的自动触发时机,减少不必要的全量复制等。

子进程开销监控与优化

子进程主要用于 RDB 和 AOF 文件的重写,主要耗费 CPU、内存、硬盘的性能。

CPU

CPU 性能开销

子进程负责把进程内的数据写入到文件的操作属于 CPU 密集型。
单核 CPU 下使用率接近 90%**。

CPU 性能优化

  • 尽量不使用单核 CPU,否则会与父进程进行资源竞争。
  • 尽量不要和其他 CPU 密集型的服务部署在同一服务器。
  • 如单服务器部署多 Redis 实例,尽量保证同时刻只有一个子进程进行文件重写操作。

内存

内存消耗分析

  • 子进程通过 fork 方式,占用内存大小等同父进程。理论上持久化操作,需要两倍的物理内存来完成。
  • 但因为 Linux 的写时复制机制 (copy-on-write),父子进程共享物理内存,父进程会将写操作创建副本去耗费内存。fork 子进程共享整个父进程的内存快照。

内存消耗监控

  • RDB 和 AOF 在进行重写时,Redis 日志会体现占用内存信息。

运维提示:可通过 Redis 日志排查持久化过程的内存消耗。

内存消耗优化

  • 同 CPU 单服务器部署多 Redis 示例,尽量保证通识课只有一个子进程在操作。
  • 减少大量写操作的时候,进行子进程操作,避免过度消耗父进程维护副本的内存。

Linux kernel在 2.6.38 内核增加了 Transparent Huge Pages(THP),支持 huge page(2MB)的页分配,默认开启。当开启时可以降低 fork 创建子进程 的速度,但执行 fork 之后,如果开启 THP,复制页单位从原来4KB 变为 2MB,会大幅增加重写期间父进程内存消耗。建议设置“sudo echo never>/sys/kernel/mm/transparent_hugepage/enabled”关闭THP

硬盘

硬盘开销分析

  • RDB 和 AOF 持久化操作势必造成硬盘的写入压力。

使用 sariostatiotop 等,分析硬盘负载情况。

硬盘开销优化

  • 不要和其他对会引起硬盘负载的服务放一起,如:存储服务、消息队列服务。
  • AOF 重写会消耗大量硬盘 IO,可开启 no-appendfsync-on-rewrite(默认:no,表示重写期间,不进行 fsync 操作)
  • 将普通机械硬盘变更为 SSD 等高速硬盘;可使用不同分盘进行存储,分摊硬盘压力。
  • AOF 的实时性重写,针对普通硬盘(吞吐约为 100MB/s),硬盘会成为 Redis 性能瓶颈。

说明:配置 no-appendfsync-on-rewrite=yes 时,在极端情况下可能丢失整个 AOF 重写期间的数据,需要根据数据安全性决定是否配置。

AOF 追加阻塞

AOF 使用 everysec 做刷盘策略的流程图:

image.png

AOF 阻塞流程的两个问题:

**

  • everysec 配置最多可能丢失 2 秒数据,不是 1 秒。
  • 如果系统 fsync 缓慢,将会导致 Redis 主线程阻塞影响效率

问题定位

  • Redis 日志会有相应的日志输出
  • 通过 info Persistence 命令的 aof_delayed_fsync 进行观察。
  • 对硬盘 IO 进行分析

多实例部署

待补充