记一次PHP并发性能调优实战 -- 性能提升104%
作者: 万千钧(祝星)
适合阅读人群
文中的调优思路无论是php, java, 还是其他任何语言都是用. 如果你有php使用经验, 那肯定就更好了
业务背景
框架及相应环境
- laravel5.7, mysql5.7, redis5, nginx1.15
- centos 7.5 bbr
- docker, docker-compose
- 阿里云 4C和8G
问题背景
php已经开启opcache, laravel也运行了optimize命令进行优化, composer也进行过dump-autoload命令.
首先需要声明的是, 系统的环境中是一定有小问题的(没有问题也不可能能够提升如此大的性能), 但是这些问题, 如果不通过使用合适的工具, 可能一辈子也发现不出来.
本文关注的就是如何发现这些问题, 以及发现问题的思路.
我们首先找到系统中一个合适的API或函数, 用来放大问题.
这个api设计之初是给nginx负载均衡做健康检查的. 使用ab -n 100000 -c 1000 进行压测, 发现qps只能到140个每秒.
我们知道Laravel的性能是出了名的不好, 但是也不至于到这个程度, 从api的编写来看不应该这么低. 所以决定一探究竟.
public function getActivateStatus(){try {$result = \DB::select('select 1');$key = 1;if ($result[0]->$key !== 1) {throw new \Exception("mysql 检查失败");}} catch (\Exception $exception) {\Log::critical("数据库连接失败: {$exception->getMessage()}", $exception->getTrace());return \response(null, 500);}try {Cache::getRedis()->connection()->exists("1");} catch (\Exception $exception) {\Log::critical("缓存连接失败: {$exception->getMessage()}", $exception->getTrace());return \response(null, 500);}return \response(null, 204);}
问题表现以及排查思路
top
top命令发现系统CPU占用100% 其中用户态占80%, 内核态占20%, 看起来没什么大问题. 有一个地方看起来很奇怪, top命令的运行结果
top命令运行结果
<figcaption style="box-sizing: border-box; display: block; text-align: center; font-size: 0.8em; line-height: 2em; color: rgb(144, 144, 144);"></figcaption>
就是有一部分php-fpm进程处在Sleep状态, 但CPU占用还是达到了近30%. 当一个进程处于Sleep状态的时候, 任然占用了不少CPU, 先不要怀疑是不是进程的问题, 我们看一下Ttop命令的man page.
%CPU -- CPU usageThe task's share of the elapsed CPU time since the last screen update, expressed as a percentage of total CPU time.
大致意思是这个占用是最后一次屏幕刷新的时候, 进程CPU的占用. 由于top命令收集信息的时候, 可能linux把这个进程强制调度了 ( 比如用于top收集进程信息 ), 所以在这一瞬间(屏幕刷新的这一瞬间)某些php-fpm进程处于sleep状态, 可以理解, 所以应该不是php-fpm的问题.
pidstat
首先选出一个php-fpm进程, 然后使用pidstat查看进程详细的运行情况
image
<figcaption style="box-sizing: border-box; display: block; text-align: center; font-size: 0.8em; line-height: 2em; color: rgb(144, 144, 144);"></figcaption>
过程中也没发现什么异样, 并且和top命令的运行结果也基本一致.
vmstat
保持压测压力, 运行vmstate查看, 除了context switch (上下文切换)有点高之外, 并没有看到太多异常. 由于我们使用的docker, redis, mysql都运行在同一台机器上, 7000左右的CS还是一个合理的范围, 但是这个IN(中断)就有点太高了, 达到了1.4万左右. 一定有什么东西触发了中断.
image
<figcaption style="box-sizing: border-box; display: block; text-align: center; font-size: 0.8em; line-height: 2em; color: rgb(144, 144, 144);"></figcaption>
我们知道中断有硬中断和软中断, 硬中断是由网卡, 鼠标等硬件发出中断信号, cpu马上停下在做的事情, 处理中断信号. 软中断是由操作系统发出的, 常用于进程的强制调度.
不管是vmstat还是pidstat都只是新能探测工具, 我们无法看到具体的中断是由谁发出的. 我们通过/proc/interrupts 这个只读文件中读取系统的中断信息, 获取到底是什么导致的中断升高. 通过watch -d命令, 判断变化最频繁的中断.
watch -d cat /proc/interrupts
image
<figcaption style="box-sizing: border-box; display: block; text-align: center; font-size: 0.8em; line-height: 2em; color: rgb(144, 144, 144);"></figcaption>
我们发现其中Rescheduling interrupts变化的最快, 这个是重调度中断(RES),这个中断类型表示,唤醒空闲状态的CPU 来调度新的任务运行。这是多处理器系统(SMP)中,调度器用来分散任务到不同 CPU的机制,通常也被称为处理器间中断(Inter-Processor Interrupts,IPI)。 结合vmstat中的命令, 我们可以确定造成qps不高的原因之一是过多的进程争抢CPU导致的, 我们现在还不能确定具体是什么, 所以还需要进一步的排查.
strace
strace可以查看系统调用, 我们知道, 当使用系统调用的时候, 系统陷入内核态, 这个过程是会产生软中断的, 通过查看php-fpm的系统调用, 验证我们的猜想
image
<figcaption style="box-sizing: border-box; display: block; text-align: center; font-size: 0.8em; line-height: 2em; color: rgb(144, 144, 144);"></figcaption>
果然, 发现大量的stat系统调用, 我们猜想, 是opcache在检查文件是否过期导致的. 我们通过修改opcache的配置, 让opcache更少的检查文件timestamp, 减少这种系统调用
opcache.validate_timestamps="60"opcache.revalidate_freq="0"
再次执行ab命令进行压测
image
<figcaption style="box-sizing: border-box; display: block; text-align: center; font-size: 0.8em; line-height: 2em; color: rgb(144, 144, 144);"></figcaption>
果然qps直接涨到了205, 提升非常明显, 有接近 46% 的提升
perf
现在任然不满足这个性能, 希望在更多地方找到突破口. 通过
perf record -g
perf report -g
看到系统的分析报告
image
<figcaption style="box-sizing: border-box; display: block; text-align: center; font-size: 0.8em; line-height: 2em; color: rgb(144, 144, 144);"></figcaption>
我们看到, 好像这里面有太多tcp建立相关的系统调用(具体是不是我还不清楚, 请大神指正, 但是看到send, ip, tcp啥的我就怀疑可能是tcp/ip相关的问题). 我们怀疑两种情况
- 与mysql, redis重复大量的建立TCP连接, 消耗资源
- 大量请求带来的tcp连接
先说第一个, 经过检查, 发现数据库连接使用了php-fpm的连接池, 但是redis连接没有, redis用的predis, 这个是一个纯PHP实现, 性能不高, 换成了phpredis:
打开laravel的config/database.php文件, 修改redis的driver为phpredis, 确保本机已安装php的redis扩展. 另外由于Laravel自己封装了一个Redis门面, 而恰好redis扩展带来的对象名也叫Redis. 所以需要修改Laravel的Redis门面为其他名字, 如RedisL5.
再次进行压测
image
<figcaption style="box-sizing: border-box; display: block; text-align: center; font-size: 0.8em; line-height: 2em; color: rgb(144, 144, 144);"></figcaption>
达到了喜人的286qps, 虽然和其他主打高性能的框架或者原生php比, 还有很高的提升空间(比如Swoole), 但是最终达到了104%的提升, 还是很有意义的
总结
我们通过top, 发现系统CPU占用高, 且发现确实是php-fpm进程占用了CPU资源, 判断系统瓶颈来自于PHP.
接着我们通过pidstat, vmstat发现压测过程中, 出现了大量的系统中断, 并通过 watch -d cat /proc/interrupts 发现主要的中断来自于重调度中断(RES)
通过strace查看具体的系统调用, 发现大量的系统调用来自于stat, 猜测可能是opcache频繁的检查时间戳, 判断文件修改. 通过修改配置项, 达到了46%的性能提升
最后再通过perf, 查看函数调用栈, 分析得到, 可能是大量的与redis的TCP连接带来不必要的资源消耗. 通过安装redis扩展, 以及使用phpredis来驱动Laravel的redis缓存, 提升性能, 达到了又一次近50%的性能提升.
最终我们完成了我们的性能提升104%的目标
记一次PHP并发性能调优实战 -- 性能提升104%相关推荐
- 字节青训营第三课之高质量编程与性能调优实战的笔记和总结
这是字节青训营第三课:高质量编程与性能调优实战的笔记和总结 概要 准备 尝试使用 test 命令,编写并运行简单测试 尝试使用 -bench参数,对函数进行性能测试 推荐阅读Go代码Review建议. ...
- jvm性能调优实战 - 32一个10万并发的BI系统,如何定位和解决频繁Young GC问题?
文章目录 Pre 模拟代码的JVM参数设置 示例Code 如何在windows上执行命令? 通过jstat观察程序的运行状态 Pre jvm性能调优实战 - 26一个每秒10万并发的系统如何频繁发生Y ...
- 调优为王!阿里巴巴彩版java性能调优实战,终于到手了!
怎样才能做好性能调优? 开始之前,我先来讲一下我对性能调优的看法.在我看来Java的性能调优并不是像学习编程语言一样可以通过学习掌握,它是没有办法用直线的思维学会并掌握使用的,并且它对于程序员来说,对 ...
- GitHub疯狂转发!阿里巴巴彩版java性能调优实战,终于到手了!文末福利
怎样才能做好性能调优? 开始之前,我先来讲一下我对性能调优的看法.在我看来Java的性能调优并不是像学习编程语言一样可以通过学习掌握,它是没有办法用直线的思维学会并掌握使用的,并且它对于程序员来说,对 ...
- JVM 性能调优实战之:使用阿里开源工具 TProfiler 在海量业务代码中精确定位性能代码...
本文是<JVM 性能调优实战之:一次系统性能瓶颈的寻找过程> 的后续篇,该篇介绍了如何使用 JDK 自身提供的工具进行 JVM 调优将 TPS 由 2.5 提升到 20 (提升了 7 倍) ...
- 从蚂蚁金服裸辞,京东三面遭调优猛击,闭关俩月啃完653页性能调优实战手册,拿到京东offer
性能优化是很多 Java 程序员希望彻底掌握的一门技能.很多人都想学好性能优化,希望能够在自己的工作中灵活运用提高性能,从而为用户提供良好的用户体验.然而,很多人在设计技术方案或者编码时缺乏系统地.方 ...
- 最新的阿里内部Java性能调优实战笔记,学完就能用的性能调优方法
年前的一波裁员"背刺",不少人失业,最近翻了不少网站的招聘信息,帮大家看看机会(附几张截图).上个月防疫政策放开,经济逐渐复苏,招聘市场也正在回暖,Java岗机会还是不少,大家多关 ...
- java性能调优实战学习笔记
这是极客时间专栏<java性能调优实战>的部分学习笔记,个人感觉这个专栏内容不深,适合初学者,我只看了编程性能调优和数据库性能调优两块,其他的暂时不打算看了,后续有时间再看吧 有任何问题可 ...
- 阿里内部Java性能调优实战宝典,堪称教科书
随着互联网的发展,高可靠.高并发以及降本增效,已成为各大公司面临的现实挑战,性能优化需求愈发迫切,大到分布式系统,小到代码块的算法优化,都已经成为你日常工作中必须要面对的事情.对于开发者而言,性能优化 ...
最新文章
- 300 Longest Increasing Subsequence
- linux 内核 目录项高速缓存 dentry cache 简介
- 《计算机网络》谢希仁第五版考试重点整理
- [机器学习] --- 参数优化与模型选择
- c mysql 工具类_Jave工具——servlet+jsp编程中mysql数据库连接及操作通用工具类
- 三大运营商公布11月运营数据:中国移动固网业务表现亮眼
- 16-elasticsearch6.x {“error“:“Content-Type header [application/x-www-form-urlencoded] is not support
- IO流-LineNumberReader
- minus出错matlab,请求帮忙指点MATLAB中的语法错误
- 分布式文件系统FastDFS安装教程
- element-ui 分页索引问题
- 注意Stream.Seek,如果想要重复使用Stream,注意用Seek复位
- esp32 io速度_Adafruit HUZZAH32-ESP32Feather的说明
- 3Dmax制作锁模型教程
- c语言中除法除以1000与1e3,北京航空航天大学C语言b第二讲(第三、四章)数据类型、运算符与表达式和最简单的C程序设计.ppt...
- 【翻译】2020年云安全综合指南(风险,最佳实践,认证)
- Python1.语言基本要素上(郭炜老师python大学mooc)
- class二进制文件解析(一)
- oracle 视图怎样修改,ORACLE视图的修改
- mysql命令行界面出现问题,MySQL命令行界面中出现字符错误提示的原因及解决方法...
热门文章
- 用CleverPDF在线办公工具怎么将PDF文件转化成Word?
- 损失函数——长尾分布 / Equalization Loss【论文笔记】
- 突发!公信宝被查封一窝端,爬虫可能又惹祸了!
- Java程序使用控制台输入时隐藏密码等敏感信息
- 全国计算机二级各个省份报名时间定了
- 金融自考专业考点讲解!应纳税额与税收优惠
- Python基础之Linux基础入门(Ubuntu)
- 在线反馈,急速解决,移动云视频客服让沟通从此不设限
- 什么是GUI(图形用户界面)?定义、要素和优势
- [mongoDB]使用Studio 3T连接MongoDB报错Illegal argument: For input string: “23-28-g865b4f6a96“