MySQL性能优化实战:慢查询到毫秒响应全链路突破
|
去年线上报警那会儿,我盯着监控页面手都在抖——一个订单查询接口从三百毫秒飙到三秒,直接把服务熔断了。运维在群里喊“赶紧回滚”,产品在工位上转圈催,我原以为又是缓存穿透的老毛病,结果一查慢查询日志,好家伙,一个带子查询的SQL在作妖,子表数据量都到百万级了,还搁那儿笛卡尔积呢。 当时我就骂出声了:“这谁写的SQL?子查询里嵌个模糊匹配,还不用索引,是嫌MySQL太闲吗?”同事小李凑过来看,说“这代码我半年前接手时就这样,原作者早离职了”。我翻了下Git提交记录,好家伙,这SQL从项目上线就没改过,注释里还写着“性能优化中”——优化了三年还在“优化中”? 咖啡凉了都没顾上喝,我直接把SQL拆成三段:主查询走主键索引,子查询改用JOIN,模糊匹配提前用Redis缓存。改完一测,QPS没细看,但接口耗时直接从三秒掉到八十毫秒。运维在群里发了个“666”,产品跑过来问“能不能再快点?”,我白了他一眼:“再快就要改架构了,你这需求文档里可没写要扛百万级并发。” 其实这事儿最气的不是慢查询本身,是没人管。工单系统里躺了半年“查询慢”的反馈,测试说“不影响功能”,前端说“后端问题”,后端说“数据库问题”,最后谁都没动。直到熔断报警才想起来查,早干嘛去了?我原以为大家都懂“优胜劣汰”的道理——性能差的代码早晚会被淘汰,结果发现很多人觉得“能跑就行”,优化?那是闲得没事干才干的事儿。 手机亮了一下,是招聘群在发JD,要求“精通MySQL性能优化”。我冷笑一声,心说“精通”这玩意儿哪是靠看文档能学会的?得踩过坑、挨过骂、熬过夜才行。就像这次,要不是熔断报警,谁会去碰那个三年没动的SQL?说不定再过半年,它还能继续“优化中”呢。
文章配图,仅供参考 窗外天黑了,我揉了揉眼睛,继续看慢查询日志。发现还有个统计接口,用了GROUP BY+COUNT(DISTINCT),数据量一大就卡死。我原以为加个复合索引就能解决,结果一试,耗时只降了百分之二十——原来DISTINCT在MySQL里是先排序再去重的,数据量大时索引根本没用。最后没办法,只能把统计逻辑拆到应用层,用Java的Set去重,虽然代码丑了点,但至少能跑进两百毫秒。跟前端吵了一架,他们非要在订单列表里显示“最近浏览记录”,我说“这得查用户行为表,数据量太大,会拖慢查询”,他们说“用户需要这个功能”。我妥协了,加了个缓存,设置五分钟过期。结果第二天运维找我,说“缓存击穿了,数据库CPU飙到百分之九十”。我查了下日志,发现前端为了“实时性”,每秒都发请求刷新缓存——我骂道:“你们是嫌数据库太闲吗?缓存不是这么用的!”最后改成前端本地缓存+定时同步,才消停下来。 其实MySQL优化没啥高深的,就是“优胜劣汰”——性能差的SQL早晚会被淘汰,要么被重写,要么被缓存,要么被架构升级替代。但问题是,很多人连“劣”在哪儿都不知道。就像那个子查询,原作者可能觉得“JOIN太复杂,子查询直观”,结果没考虑数据量;或者觉得“模糊匹配影响不大”,结果没加索引。这些小细节堆在一起,就成了性能杀手。 键盘声很响,是小李在改代码。我凑过去看了眼,他在给一个WHERE条件加括号,说“之前漏了优先级,导致索引失效”。我点点头,心想“这小子终于开窍了”。其实优化MySQL,很多时候就是改这种小地方——加个索引、拆个子查询、调个括号、换个连接方式。但这些小改动,往往能带来数量级的性能提升。 下一步打算查查索引使用率,看看有没有“僵尸索引”——那些建了但从来没用过的索引,占空间还影响写入性能。不过我也不确定能不能找到,毕竟有些索引可能只在特殊场景下用,平时看不出来。算了,先查着,有总比没有强——性能优化这事儿,本来就是“优胜劣汰”,能优一点是一点,能汰一个是一个。 (编辑:站长网) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |

