Redis bgsave 线上分析
redis在做RDB(快照)持久化时会fork一个子进程出来,把内存中的数据持久化到磁盘的rdb文件。
线上有个接口响应延迟P99波动较大,后对其进行了优化。响应延迟折线图如下:
优化完成后,P99趋于平稳,平均在70ms左右。
1 思考接口的执行过程
这个接口一共会经过三个服务,最终返回给客户端。执行流程如下:
按照箭头所示流程,先访问服务1,服务1的结果返回给接口层,在请求服务2,服务2请求服务3,然后将结果返回给接口层。
2 分析
然后分别观察了服务1、服务2、服务3,主要观察的指标如下:
- 服务对外响应延迟
- CPU负载
- 网络抖动
观察后,服务2和服务3的这几个指标都没啥问题。
服务2的对外响应延迟波动情况与接口的波动颇为相似,再针对服务2分析。服务2是个IO密集型的服务,平均QPS在3K左右。
主要的几个IO操作包括:
- 单点Redis的读取
- 集群Redis的读取
- 数据库的读取
- 两个http接口的拉取
- 一次其他服务的调用
集群Redis的响应很快,平均在5ms左右(加上来回的网络消耗),数据库在10ms左右,http接口只有偶尔的慢请求,其他服务的调用也没问题。
最后发现单点的Redis响应时间过长
如图所示,服务2接受到的每次请求会访问三次这个单点redis,这三次加起来有接近100ms,然后针对这个单点redis进行分析。
发现这台redis的CPU有如下波动趋势
基本上每一分钟会波动一次。
马上反应过来是开启了bgsave引起的(基本1分钟bgsave一次),因为之前有过类似的经验,就直接关掉bgsave再观察
3 解决方案
线上的bgsave不能一直关闭,万一出现故障,会造成大量数据丢失。
具体方案如下:
- 先开启这台机器的bgsave
- 申请一台从服务器,并从这台机器上同步数据
- 同步完成后,主节点关闭bgsave,从节点开启bgsave
这样一来,主节点的读写不再受bgsave影响,同时也能用从节点保证数据不丢失。
4 bgsave引起CPU波动原因探索
首先要说一下bgsave的执行机制。执行bgsave时(无论以哪种方式执行),会先fork出一个子进程来,由子进程把数据库的快照写入硬盘,父进程会继续处理客户端的请求。
所以在平时没有bgsave的时候,进程状态如下:
bgsave时,进程状态如下:
最上面CPU占用100%的就是fork出来的子进程,在执行bgsave,同时他完全独占了一个CPU(上面的红框)。
所以得出结论,这个CPU的波动是正常的,每一个波峰都是子进程bgsave所致。
RDB为了将数据持久化到硬盘,需要经常fork一个子进程出来。数据集如果过大的话,fork()的执行可能会非常耗时,如果数据集非常大的话,可能会导致Redis服务器产生几毫秒甚至几秒钟的拒绝服务,并且CPU的性能会急剧下降。
这个停顿的时间长短取决于redis所在的系统,对于真实硬件、VMWare虚拟机或者KVM虚拟机来说,Redis进程每占用1个GB的内存,fork子进程的时间就增加10-20ms,对于Xen虚拟机来说,Redis进程每占用1个GB的内存,fork子进程的时间需要增加200-300ms。
但对于一个访问量大的Redis来说,10-20ms已经是很长时间了(我们的redis占用了10个G左右内存,估计停顿时间在100ms左右)。