最近接到反馈,客户端偶尔出现接口异常。经过初步的日志排查发现是MySQL的问题,因而针对此次的异常对MySQL进行了一次性能问题追踪和优化。事情搞定后就一直想写一篇总结记录下追踪的过程以及优化的思路,最后在磨蹭了一个星期后有了这遍笔记。(笔记里的数据表名和数据都为过后在本地模拟,可能难以反映出线上当时的真实耗时状况)
首先说一下问题的情况:客户端反应的情况是mysql间歇性链接超时。问题并不能明显地反馈在某个sql上,经过查看监控日志,发现内存间歇性飙升。
最开始怀疑是定时任务的问题,由于业务在后台有不少定时任务,其中不乏对数据库大量数据作聚合操做的任务,有一些任务在实现时没有考虑其对性能的影响,每每会产生间歇性的数据库性能不稳定。可是经过观察发现此次内存飙升的时间间隔并不固定,初步排除是定时脚本的问题。
会不会是慢SQL的问题,因为项目迭代开发速度的问题缺少很好的质量检测,通常状况下,这个问题是最常出现的性能问题,此次也不例外,第一时间将慢SQL日志调出来查看。而后。。。嗯?此次的慢SQL有点多啊,而且有点繁杂,反应出来就是其中某个表 partner
,跟这个表相关的sql所有被标记为慢SQL。渐渐意识到问题有点严重呀!
而后随便拿出一条慢SQL执行,嗯? 8ms,这是啥慢SQL,这已经要比数据库的绝大多数哦SQL快了。索性在有一次问题复现时根据当时的 [show processlist]() 反馈,发现有会话数量很不稳定,忽高忽低,在内存飙升的延后几秒中内每每会有大量的进程。在案发现场,定位到一个条可疑SQL,(根据进程的状态、执行SQL的时间)。拿出来执行一下,发现确实是慢SQL,而后怀疑是当前内存不足的缘由致使的慢SQL,因而将这条SQL拿到写数据库中执行,发现依旧是很慢,因而定位到一个问题,具体是否是它致使了所有问题须要先优化了它再分析,保存了当时的processlist后对这条sql进行优化。
先来看一下这条SQL:select
uid from partners where
id in (1,2,3,"4","5",...)
SQL的结构很简单,就是根据id搜索partners表的uid,其中uid和id都有索引,id为主键。问题SQL找到了下面就是SQL优化三板斧的工做了。
先进一步经过 profiles 来进一步定位问题。
mysql
Status | Duration | Block_ops_in | Block_ops_out |
---|---|---|---|
starting | 0.000046 | 0 | 0 |
checking permissions | 0.000027 | 0 | 0 |
Opening tables | 0.000049 | 0 | 0 |
init | 0.000038 | 0 | 0 |
System lock | 0.000027 | 0 | 0 |
optimizing | 0.000028 | 0 | 0 |
statistics | 0.000037 | 0 | 0 |
preparing | 0.000030 | 0 | 0 |
executing | 0.000025 | 0 | 0 |
Sending data | 0.146700 | 64 | 1144 |
end | 0.000077 | 0 | 0 |
query end | 0.000027 | 0 | 0 |
closing tables | 0.000031 | 0 | 0 |
freeing items | 0.000049 | 0 | 0 |
cleaning up | 0.000028 | 0 | 0 |
能够很清楚的看到,语句在Sending data模块消耗太多时间,而且进行了大量的IO操做。sending data表示收集+发送数据,一般产生的状况有一下几种:sql
因为SQL返回结果只有1条,且不存在大字段,很容易定位到是数据收集阶段的问题,因而怀疑到了索引上。
再使用 explain或desc 看一下执行计划:数据库
type | possible_key | key | extra |
---|---|---|---|
index | PRIMARY | idx_uid | Using where; Using index |
发现并无使用主键也就是id索引。因而问题已经很明显了。最后给出优化方案。性能优化
在Mysql中id字段为int型,where条件中使用 in(1, 2, 3)
是能够命中索引的,使用 in("1","2","3") 经过mysql优化器的优化为int型后也能够命中索引,但当二者混用时,mysql不会对它进行优化,致使索引失败。后面就是业务层的事情了。服务器