当前位置: 首页 > news >正文

.net redis定时_一场由fork引发的超时,让我们重新探讨了Redis的抖动问题

2dd7b1a0ab406f8dd04dbdd4b4eedb27.gif  背景介绍

华为云数据库GaussDB(for Redis) 是一款基于计算存储分离架构,兼容Redis生态的云原生NoSQL数据库;它依靠共享存储池实现了强一致,支持持久化落盘存储,保证数据的安全可靠。其核心特点是:存算分离、强一致、低成本、超大容量。

GaussDB(for Redis)服务团队在支撑某客户业务上云的过程中,发现一次由fork引发的时延抖动问题,本着对客户负责任的态度,我们详细探究了fork这个系统调用的性能影响,并且在最新的GaussDB(for Redis)版本已解决了这个抖动问题,清零了内部的fork使用,与原生Redis相比,彻底解决了fork的性能隐患。

问题焦点

  1. 华为云GaussDB(for Redis) 服务在某客户上云线调测过程中发现,系统上量后规律性的出现每5分钟1次的时延抖动问题。

  2. 华为云GaussDB(for Redis)团队经过攻关,最终确认抖动原因是fork导致并解决了这个问题。而fork是开源Redis的一个重要依赖,希望通过本文的分享,能够帮助大家在使用开源Redis的时候,充分认识fork的影响,从而选择更优的方案。

问题现象

某客户业务接入GaussDB(for Redis)压测发现,每5分钟系统出现一次规律性的时延抖动:

  1. 正常情况消息时延在1-3ms,抖动时刻时延达到300ms左右。

  2. 通常是压测一段时间后开始出现抖动;抖动一旦出现后就非常规律的保持在每5分钟1次;每次抖动的持续时长在10ms以内。

下图是从系统慢日志中捕获到的发生抖动的消息样例(对敏感信息进行了遮掩):

984cc0e2b8fa1dd70104bb5b4f8bc508.png

问题分析

1.排查抖动源:

1)由于故障的时间分布非常规律,首先排除定时任务的影响,主要包括:

  • agent:和管控对接的周期性统计信息上报任务

  • 内核:执行引擎(Redis协议解析)和存储引擎(rocksdb)的周期性操作(包括rocskdb统计,wal清理等)

屏蔽上述2类定时任务后,抖动依然存在。

2)排除法未果后,决定回到正向定位的路上来。通过对数据访问路径增加分段耗时统计,最终发现抖动时刻内存操作(包括allocate、memcpy等)的耗时显著变长;基本上长出来的时延,都是阻塞在了内存操作上。

a80b3ee9757233df925fadd74efb0a65.png

(截图为相关日志,单位是微秒)

3)既然定位到是系统级操作的抖动,那么下一步的思路就是捕获抖动时刻系统是否有异常。我们采取的方法是,通过脚本定时抓取top信息,分析系统变化。运气比较好,脚本部署后一下就抓到了一个关键信息:每次在抖动的时刻,系统中会出现一个frm-timer进程;该进程为GaussDB(for Redis)进程的子进程,且为瞬时进程,持续1-2s后退出。

8d8e82772c20e9a6266b3385e22a088f.png

8e2a17edc5f0308c47396078a857abef.png

4)为了确认该进程的影响,我们又抓取了perf信息,发现在该进程出现时刻,Kmalloc, memset_sse,memcopy_sse等内核系统调用增多。从上述信息推断,frm-timer进程应该是被fork出来的,抖动源基本可锁定在fork frm-timer这个动作上。

2.确定引发抖动的代码:

1)分析frm-timer的来历是下一步的关键。因为这个标识符不在我们的代码中,所以就需要拉通给我们提供类库的兄弟部门联合分析了。经过大家联合排查,确认frm-timer是日志库liblog中的一个定时器处理线程。如果这个线程fork了一个匿名的子进程,就会复用父进程的线程名,表现为Redis进程创建出1个名为frm-timer的子进程的现象。

2)由于frm-timer负责处理liblog中所有模块的定时器任务,究竟是哪个模块触发了上述fork?这里我们采取了一个比较巧妙的方法,我们在定时器处理逻辑中增加了一段代码:如果处理耗时超过30ms,则调用std:: abort()退出,以生成core栈。

3)通过分析core栈,并结合代码排查,最终确认引发抖动的代码如下:

5bf11fabb5b210f450df7ab7c19c3f31.png

上述代码是用来周期性归档日志的,它每5分钟会执行1次 system系统调用来运行相关脚本,完成归档日志的操作。而Linux system系统调用的源码如下,实际上是一个先fork子进程,再调用execl的过程。

8e26a8d3768b912863dfb8c4bfb228e7.png

4)分析至此,我们还需要回答最后一个问题:究竟是fork导致的抖动,还是脚本内容导致的抖动?为此,我们设计了一组测试用例:

  • 用例1:将脚本内容改为最简单的echo操作

  • 用例2:在Redis进程里模拟1个类似frm-timer的线程,通过命令触发该线程执行fork操作

  • 用例3:在Redis进程里模拟1个类似frm-timer的线程,通过命令触发该线程执行先fork,再excel的操作

  • 用例4:在Redis进程里模拟1个类似frm-timer的线程,通过命令触发该线程执行system的操作

  • 用例5:在Redis进程里模拟1个类似frm-timer的线程,通过命令触发该线程执行先vfork,再excel的操作

最终的验证结果:

  • 用例1:有抖动。

  • 用例2:有抖动。

  • 用例3:有抖动。

  • 用例4:有抖动。

  • 用例5:无抖动。

用例1结果表明抖动和脚本内容无关;用例2、3、4的结果表明调用system引发抖动的根因是因为其中执行了fork操作;用例5的结果进一步佐证了抖动的根因就是因为fork操作。最终的故障原因示意图如下:

6879adbd2300cba908eee9be0f7fb27c.png

3.进一步探究fork的影响:

1)众所周知,fork是Linux(严格说是POSIX接口)创建子进程的系统调用,历史上看,主流观点大多对其赞誉有加;但近年间随着技术演进,也陆续出现了反对的声音:有人认为fork是上个时代遗留的产物,在现代操作系统中已经过时,有很多害处。激进的观点甚至认为它应该被彻底弃用。(参见附录1,2)

2)fork当前被诟病的主要问题之一是它的性能。大家对fork通常的理解是其采用copy-on-wirte写时复制策略,因此对其的性能影响不甚敏感。但实际上,虽然fork时可共享的数据内容不需要复制,但其相关的内核数据结构(包括页目录、页表、vm_area_struc等)的复制开销也是不容忽视的。附录1、2中的文章对fork开销有详细介绍,我们这回遇到的问题也是一个鲜活的案例:对于Redis这样的时延敏感型应用,1次fork就可能导致消息时延出现100倍的抖动,这对于应用来说无疑是不可接受的。

4.原生Redis的fork问题:

4.1 原生Redis同样被fork问题困扰(参见附录3,4,5),具体包括如下场景:

1)数据备份

备份时需要生成RDB文件,因此Redis需要触发一次fork。

2)主从同步

全量复制场景(包括初次复制或其他堆积严重的情况),主节点需要产生RDB文件来加速同步,同样需要触发fork。

3)AOF重写

当AOF文件较大,需要合并重写时,也会产生一次fork。

4.2 上述fork问题对原生Redis的影响如下:

1)业务抖动

原生Redis采用单线程架构,如果在电商大促、热点事件等业务高峰时发生上述fork,会导致Redis阻塞,进而对业务造成雪崩的影响。

2)内存利用率只有50%

Fork时子进程需要拷贝父进程的内存空间,虽然是COW,但也要预留足够空间以防不测,因此内存利用率只有50%,也使得成本高了一倍。

3)容量规模影响

为减小fork的影响,生产环境上原生Redis单个进程的最大内存量,通常控制在5G以内,导致原生Redis实例的容量大大受限,无法支撑海量数据。

解决方法

  1. 修改日志库liblog中的周期性归档逻辑,不再fork子进程。

  2. 系统排查并整改GaussDB(for Redis)代码(包括使用的类库代码)中的fork调用。

  3. 最终排查结果,实际只有本次的这个问题点涉及fork。当前修改后即可确保GaussDB(for Redis)的时延保持稳定,不再受fork性能影响。

注:GaussDB(for Redis)由华为云基于存算分离架构自主开发,因此不存在原生Redis的fork调用的场景。

总结

本文通过分析GaussDB(for Redis)的一次由fork引发的时延抖动问题,探究了fork这个系统调用的性能影响。最新的GaussDB(for Redis)版本已解决了这个抖动问题,并清零了内部的fork使用,与原生Redis相比,彻底解决了fork的性能隐患。希望通过这个问题的分析,能够带给大家一些启发,方便大家更好的选型。

附:

1.[是时候淘汰对操作系统的 fork() 调用了]

https://www.infoq.cn/article/BYGiWI-fxHTNvSohEUNW

2.[Linux fork那些隐藏的开销]

https://www.mdeditor.tw/pl/29L0

3.[Redis官方文档]

https://redis.io/topics/latency

4.[Redis的一些坑]

https://www.jianshu.com/p/03df6fd516eb

5.[Redis 常见问题之-fork操作]

https://blog.csdn.net/longgeqiaojie304/article/details/89407214

6.[GaussDB(for Redis)官网链接]

https://www.huaweicloud.com/product/gaussdbforredis.html

9dbc72b82b48551532c036061bd5f262.gif

32e3e7c56d43ed527d53fced70fccb54.png

微信号:ywcclzy

相关文章:

  • antd点击跳转页面且有参数_关于react路由传参跳转后刷新页面参数消失
  • 奥威软件大数据bi_IT+美工才能做数据可视化?BI软件:不需要!不需要
  • httpslb.php_Kafka生产者的客户端(PHP)开发
  • php 实现二叉树的最大深度_PHP实现二叉树的深度优先遍历(前序、中序、后序)和广度优先遍历(层次)...
  • sap客户信贷_企业客户信用控制管理机制实践(2/2)
  • 晶闸管有几个pn结_【半导体物理】PN结
  • 模板文字修改_ppt模板适用于室内装修设计的图片展示国片混排等演示PPT模版
  • 机器学习预测时间序列 特征值怎么确定_AWS发布时间序列预测云服务,无机器学习基础也能上手...
  • mybatis 二级缓存失效_Mybatis 一二级缓存实现原理与使用指南
  • pycharm导入mysql_Pycharm创建Django项目讲解 python django
  • fanuc机器人控制柜接线_FANUC机器人系统镜像还原步骤
  • zabbix监控磁盘_zabbix监控cpu、内存、磁盘使用情况
  • 一直跳动的按钮插件_职场表格插件 Power Click功能介绍03:工作便签
  • 手机屏幕常见故障_华强北二手苹果手机面容损坏可修复原理(重磅,大家务必小心,莫贪小便宜)...
  • springcloud 创建子父项目_SpringCloud(四)- 父子项目
  • ES6之路之模块详解
  • JDK 6和JDK 7中的substring()方法
  • mysql 5.6 原生Online DDL解析
  • socket.io+express实现聊天室的思考(三)
  • swift基础之_对象 实例方法 对象方法。
  • Swoft 源码剖析 - 代码自动更新机制
  • 前端设计模式
  • 深度学习中的信息论知识详解
  • 使用docker-compose进行多节点部署
  • 小程序开发之路(一)
  • AI又要和人类“对打”,Deepmind宣布《星战Ⅱ》即将开始 ...
  • 专访Pony.ai 楼天城:自动驾驶已经走过了“从0到1”,“规模”是行业的分水岭| 自动驾驶这十年 ...
  • ​【C语言】长篇详解,字符系列篇3-----strstr,strtok,strerror字符串函数的使用【图文详解​】
  • #{} 和 ${}区别
  • #我与Java虚拟机的故事#连载02:“小蓝”陪伴的日日夜夜
  • (1)(1.11) SiK Radio v2(一)
  • (1)Android开发优化---------UI优化
  • (2020)Java后端开发----(面试题和笔试题)
  • (c语言)strcpy函数用法
  • (Redis使用系列) Springboot 整合Redisson 实现分布式锁 七
  • (二)pulsar安装在独立的docker中,python测试
  • (附源码)springboot 校园学生兼职系统 毕业设计 742122
  • (附源码)ssm高校志愿者服务系统 毕业设计 011648
  • (附源码)计算机毕业设计高校学生选课系统
  • (附源码)小程序 交通违法举报系统 毕业设计 242045
  • (一)appium-desktop定位元素原理
  • (转)ORM
  • (转)setTimeout 和 setInterval 的区别
  • .jks文件(JAVA KeyStore)
  • .net refrector
  • .NET 解决重复提交问题
  • .NET 设计一套高性能的弱事件机制
  • .net利用SQLBulkCopy进行数据库之间的大批量数据传递
  • [ Linux 长征路第二篇] 基本指令head,tail,date,cal,find,grep,zip,tar,bc,unname
  • [20190113]四校联考
  • [383] 赎金信 js
  • [AutoSAR 存储] 汽车智能座舱的存储需求
  • [BUG]Datax写入数据到psql报不能序列化特殊字符
  • [BZOJ4010]菜肴制作
  • [C++] sqlite3_get_table 的使用