云计算百科
云计算领域专业知识百科平台

MySQL 单点详解(16)

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */
#content_views .toc,
/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */
#content_views.markdown_views > p:empty:has(+ .toc),
#content_views.markdown_views > .toc + p:empty,
/* 富文本旧版目录标记 */
#content_views.htmledit_views #main-toc,
#content_views.htmledit_views #hr-toc,
#content_views.htmledit_views p[id*=\”-toc\”] {
display: none !important;
}
/* 目录去掉后,紧跟的首个标题不再多出一块上边距 */
#content_views.markdown_views > .toc + h1,
#content_views.markdown_views > .toc + h2,
#content_views.markdown_views > .toc + h3,
#content_views.markdown_views > .toc + h4,
#content_views.markdown_views > .toc + p:empty + h1,
#content_views.markdown_views > .toc + p:empty + h2,
#content_views.markdown_views > .toc + p:empty + h3,
#content_views.markdown_views > .toc + p:empty + h4 {
margin-top: 0 !important;
}

MySQL 单点详解(16):一条 SQL 突然变慢,按什么顺序查

面试官:线上一条 SQL 变慢了,怎么排查?

面试官:加了索引还是慢,下一步看什么?

排查的顺序不是背命令,是先定性:一直慢是写法问题,突然慢是环境问题。定性错了,后面每一步都白做。

一、先分清一直慢还是突然慢

表① 两种现象,指向完全不同的原因

现象先在哪儿找原因
上线就慢,一直慢 没索引、索引失效、写法有问题,查执行计划
跑很久都正常,突然慢 数据量涨了、统计信息变了、锁等待、刷脏,查现场状态

这个判断只用一句话就能问出来:这条 SQL 昨天快不快。

答“一直不快”,往写法上查;答“昨天还很快”,别急着改 SQL,先看数据库当时在干什么。

二、第一步永远是慢日志

表② 四个参数

参数作用
slow_query_log 开关,生产必须打开
long_query_time 阈值,默认 10 秒,建议改成 1 秒
log_queries_not_using_indexes 顺手记下没走索引的语句,量大会撑爆日志
pt-query-digest 把日志聚合成按耗时排序的报表

慢日志的价值不在单条记录,在聚合。几千条记录直接用眼睛看没有意义,按总耗时排序之后,往往前三条就占了八成时间。

看聚合报表时优先看两个数:总耗时占比、平均扫描行数。前者说明影响面,后者说明这条 SQL 本身有没有问题。

三、拿到 SQL 之后按四步走

表③ 四步,顺序不能反

步骤看什么判断
一、执行计划 key、type、rows、Extra 有没有走索引,走得好不好
二、扫描与回表 rows 与返回行数的比值 扫了很多只返回几行,是索引问题
三、数据分布 条件的实际区分度 索引对了但选择率差,也会慢
四、现场状态 锁等待、连接数、CPU、IO 前三步都正常,就是被别的东西挡住了

第一步就是 explain,前面的第 10 期已经讲过怎么读,这里只用一句话:key 为空说明没走索引,type 是 index 说明扫了整棵索引树,两者都不算好。

第二步看比值。扫十万行返回十行,说明索引只起到了排序作用,没起到定位作用,问题在索引设计;扫十万行返回九万行,那是查询本身要的数据就多,加索引救不了。

第四步最容易被跳过,但它能解释大半“突然变慢”。三种典型:一是锁等待,某个事务没提交,后面全排队;二是 MDL 等待,一个 DDL 拿不到元数据锁,把这张表上所有查询都堵住;三是刷脏,写入突增导致脏页刷不过来,用户线程被迫下场刷页。

四、几种一眼能认出来的情况

改了索引却没生效,八成是第 04 期那几种:隐式转换、函数包裹、前导模糊。

执行计划突然变了,是统计信息不准。优化器按采样估算,数据倾斜严重时会选错索引。临时办法是 analyze table 重算统计,或者 force index 先顶住。

缓存全冷,重启之后第一批查询必然慢,跑一阵自己会好,这种不用改。

连接数打满,表现是所有 SQL 一起变慢,不是某一条。这时候查 SQL 没有意义,要看连接池配置和有没有连接泄漏。

五、面试官会往下问的三层

第一层:加了索引还是慢,可能是什么原因?

四种可能:索引没走成(类型不匹配、函数包裹);走了但回表太多(选择率差);排序和分组仍然要额外做;根本不是 SQL 的问题,是锁等待或者机器负载。

判断办法只有一个:重新 explain,看 key 和 rows 有没有按预期变化。

第二层:线上不能改代码,怎么先止血?

先降级再修根因。可以 force index 固定执行计划;可以把这条查询临时挪到从库;可以在应用层加短缓存挡住重复请求;锁等待就先找出持锁的长事务处理掉。

第三层:怎么让它不再发生?

上线前把执行计划纳入 review,重点看新 SQL 的 type 和 rows;慢日志阈值调到 1 秒并配告警;核心表的数据量变化要有监控,因为执行计划会随数据分布改变。

收尾

这期压成一句:先问昨天快不快,一直慢查写法,突然慢查现场;慢日志看聚合,执行计划看 key 和 rows,最后别忘了锁和刷脏。

MySQL 这个模块到这里 16 期

赞(0)
未经允许不得转载:网硕互联帮助中心 » MySQL 单点详解(16)
分享到: 更多 (0)

评论 抢沙发

评论前必须登录!