文章资讯 / 云服务器 / 一条SQL从800毫秒到8毫秒,慢日志立了功

一条SQL从800毫秒到8毫秒,慢日志立了功

发布时间: 分类:云服务器 作者:188mi 来源:jianshu.com,blog.csdn.net,php.cn,cloud.tencent.com,zhuanlan.zhihu.com 阅读:694
从一次线上报警说起:某用户查询接口的响应时间频繁超过800毫秒,用户侧已经能明显感觉到卡顿。复盘这次优化的全过程——从慢日志锁定真凶,到一条索引把耗时砍到8毫秒——每一步的做法都可以直接抄作业。这套流程不挑数据库规模,小站照做同样受益。

复盘第一步:让慢日志开口优化的起点不是猜,是慢查询日志。先确认配置:slow_query_log要开启,long_query_time按业务定——MySQL默认10秒,对线上业务太宽松,普遍建议压到1秒,抓取更完整的问题样本;再加一条log_queries_not_using_indexes,没走索引的查询一网打尽。日志开启后记得确认文件在增长,配了没生效的案例并不少见。这次报警后从慢日志里抓到的SQL,是一条看起来人畜无害的订单查询:按用户ID和状态过滤、按时间倒序、取前二十条——800毫秒的耗时,就藏在这么普通的一句话里。

复盘第二步:EXPLAIN看穿执行计划拿到慢SQL,下一步是EXPLAIN分析执行计划:看type列——出现ALL就是全表扫描,数据量一大必然慢;看key列——是否真的用上了你以为的索引;看rows列——预估扫描行数,几千行和几百万行的差别就是秒与毫秒的差别。这条订单查询的EXPLAIN结果:type为ALL,rows接近全表——扫描几十万行才挑出二十条,慢是必然的,快才是运气——过滤条件里的两列都没有可用的联合索引,排序还在用文件排序。病因清楚了,药方也就清楚了。

复盘第三步:补索引,一击致命针对where里的用户ID加状态、排序用到的时间列,建一条联合索引,让过滤和排序都能吃上索引。索引生效后复测:800毫秒降到8毫秒,一百倍。建索引的两条纪律:索引列的顺序要匹配查询的过滤与排序模式,不是随便挑几列拼一起;索引不是越多越好——每个索引都有写入开销,表上的冗余索引反过来会拖慢INSERT和UPDATE,建新索引时顺手清掉被替代的旧索引。索引生效后观察几天写入性能,确认没有副作用才算收尾。

复盘第四步:写法收敛,消灭下一个800毫秒索引之外,SQL写法的四个通病也要排查:禁用select星号——只查需要的字段,大文本字段尤其不要顺手带出;分页深翻页要改写——limit十万页的写法改成基于上一页末尾ID的定位方式;避免在索引列上套函数——函数包住索引列,索引直接失效;能走覆盖索引的查询,把需要的字段都纳入索引——整个查询在索引上完成,不回表——高频接口的查询优先考虑这个形态。

复盘第五步:把排查变成制度单次优化解决单次问题,制度才能防止复发。三件事常态化:慢日志定期轮转和清理——它可能长得很快,用logrotate这类工具管理;每周过一遍慢日志新增,重点看新出现的高频慢SQL;上线新功能前,对涉及的查询先跑一遍EXPLAIN。数据库的 Performance 就是这样养出来的——不是靠一次神优化,而是靠每次800毫秒都被看见、被处理。慢查询最怕的不是存在,而是存在了很久却没人知道。