超高性能的物理机
从训练到推理,全栈GPU护航您的AI之旅
安全可靠且超五星的服务器托管服务
海量资源,提供多种线路可选
安全稳定、可弹性扩展的高性能云服务器
火数云3.2Ghz频率高性能独立服务器
快速、稳定、可靠的全球加速服务
从平均响应1.2秒到80毫秒,我只做了这三件事
接上篇《小程序服务器配置方案》发出后,后台收到大量留言:"配置按你说的改了,钱是省了,但数据库查询还是慢,用户天天反馈卡顿。"
说实话,这个问题我太有共鸣了。
去年我们的日活从5000涨到2万的过程中,数据库成了最大的瓶颈。某个核心接口在最严重的时候响应时间达到了1.2秒,用户打开小程序要转圈3-4秒才能看到内容,跳出率飙升了25%。
后来我花了整整两周,把慢查询日志里排名前20的SQL全部优化了一遍。最终效果是:接口平均响应时间从380ms降至80ms,数据库CPU利用率从85%降到25%。
今天就把这套实战方法完整公开。
很多人的做法是"凭感觉优化",觉得哪个表数据量大就建索引,结果往往事倍功半。
正确的做法是先开启慢查询日志,用数据说话。
-- 开启慢查询日志 SET GLOBAL slow_query_log = ON; -- 设置慢查询阈值,建议先设为1秒 SET GLOBAL long_query_time = 1; -- 查看慢查询日志位置 SHOW VARIABLES LIKE 'slow_query_log_file';
运行24小时后,执行这条命令查看慢查询TOP 10:
SELECT digest_text, count_star AS 执行次数, avg_timer_wait/1000000000 AS 平均耗时_秒, sum_timer_wait/1000000000 AS 总耗时_秒 FROM performance_schema.events_statements_summary_by_digest ORDER BY sum_timer_wait DESC LIMIT 10;
真实案例:我们排名第一的慢查询是一条订单列表查询,单次执行1.8秒,每天执行12万次,总耗时占数据库资源的43%。
错误示范:看到WHERE条件里的字段就建索引,结果一张表建了7-8个索引,写入性能直线下降。
正确做法:遵循最左前缀原则,联合索引比单列索引更高效。
实战案例:订单表查询
优化前的SQL:
SELECT order_id, user_id, amount, status, create_time FROM orders WHERE user_id = 12345 AND status = 1 AND create_time > '2026-01-01' ORDER BY create_time DESC LIMIT 20;
优化前,这张表只有user_id的单列索引。查询流程是:先通过user_id索引找到所有订单(可能有几百条),再逐条过滤status和create_time,最后排序取20条。耗时1.2秒。
优化后,建立联合索引:
ALTER TABLE orders ADD INDEX idx_user_status_time (user_id, status, create_time);
优化后的查询流程:联合索引直接定位到user_id=12345且status=1的数据,create_time在索引中已排序,直接取20条。耗时80ms,提升15倍。
关键原则:联合索引的字段顺序按等值查询 > 范围查询 > 排序字段排列。
什么是覆盖索引?就是查询需要的所有字段都在索引里,MySQL不需要回表查数据行。
实战案例:用户信息查询
SELECT user_id, nick_name, avatar, level FROM users WHERE mobile = '13800138000';
如果只在mobile上建索引,MySQL会先去索引找到user_id,再回表查nick_name、avatar、level三个字段,多了一次随机I/O。
优化方案:
ALTER TABLE users ADD INDEX idx_mobile_cover (mobile, nick_name, avatar, level);
这样查询的所有字段都在索引里,回表操作完全消除,速度提升2-3倍。
使用技巧:用EXPLAIN查看执行计划,如果Extra列显示Using index,说明触发了覆盖索引。
这是小程序列表页最常见的坑。
错误写法:
SELECT * FROM orders WHERE user_id = 12345 ORDER BY create_time DESC LIMIT 100000, 20; -- 翻到第5000页
MySQL需要扫描前100000条再丢弃,数据量越大越慢。
优化写法(游标分页):
SELECT * FROM orders WHERE user_id = 12345 AND create_time < '上一页最后一条的时间' -- 游标条件 ORDER BY create_time DESC LIMIT 20;
配合联合索引,每次只扫描20条,翻到100页以后性能依然稳定。
小程序端配合:用scroll-view的滚动加载代替传统分页器,使用create_time作为游标传给后端。
scroll-view
create_time
-- 假设phone字段是VARCHAR类型 SELECT * FROM users WHERE phone = 13800138000; -- 传入数字
MySQL会把phone字段转成数字再比较,索引失效,全表扫描。
正确写法:
SELECT * FROM users WHERE phone = '13800138000';
-- 极其慢的写法 SELECT * FROM orders WHERE user_id IN (SELECT user_id FROM users WHERE level > 5);
优化方案:改成JOIN
SELECT o.* FROM orders o INNER JOIN users u ON o.user_id = u.user_id WHERE u.level > 5;
ANALYZE TABLE
EXPLAIN
优化不是一锤子买卖,我每周会做这三件事:
慢查询日志周报:每周一查看上周慢查询TOP 10,新增的慢查询及时处理
索引使用率分析:通过sys.schema_unused_indexes查看从未使用的索引,及时删除
sys.schema_unused_indexes
数据归档:超过1年的订单数据迁移到历史表,减轻主表压力
慢查询优化不是玄学,是有章可循的技术活。核心就三句话:
慢查询日志是导航仪,先找到问题再动手
联合索引是最强武器,用好最左前缀原则
EXPLAIN是照妖镜,执行计划一看便知问题在哪
希望这份实战指南能帮你解决小程序卡顿的问题。
如果你在优化过程中遇到奇怪的慢查询,欢迎在评论区贴出SQL和表结构,我看到都会回复。
接上篇《小程序服务器配置方案》发出后,后台收到大量留言:"配置按你说的改了,钱是省了,但数据库查询还是慢,用户天天反馈卡顿。"
说实话,这个问题我太有共鸣了。
去年我们的日活从5000涨到2万的过程中,数据库成了最大的瓶颈。某个核心接口在最严重的时候响应时间达到了1.2秒,用户打开小程序要转圈3-4秒才能看到内容,跳出率飙升了25%。
后来我花了整整两周,把慢查询日志里排名前20的SQL全部优化了一遍。最终效果是:接口平均响应时间从380ms降至80ms,数据库CPU利用率从85%降到25%。
今天就把这套实战方法完整公开。
第一步:先找到真正的慢查询
很多人的做法是"凭感觉优化",觉得哪个表数据量大就建索引,结果往往事倍功半。
正确的做法是先开启慢查询日志,用数据说话。
MySQL慢查询配置(腾讯云/阿里云通用)
运行24小时后,执行这条命令查看慢查询TOP 10:
真实案例:我们排名第一的慢查询是一条订单列表查询,单次执行1.8秒,每天执行12万次,总耗时占数据库资源的43%。
第二步:三个必会的优化套路
套路一:索引不是越多越好,而是要"精准"
错误示范:看到WHERE条件里的字段就建索引,结果一张表建了7-8个索引,写入性能直线下降。
正确做法:遵循最左前缀原则,联合索引比单列索引更高效。
实战案例:订单表查询
优化前的SQL:
优化前,这张表只有user_id的单列索引。查询流程是:先通过user_id索引找到所有订单(可能有几百条),再逐条过滤status和create_time,最后排序取20条。耗时1.2秒。
优化后,建立联合索引:
优化后的查询流程:联合索引直接定位到user_id=12345且status=1的数据,create_time在索引中已排序,直接取20条。耗时80ms,提升15倍。
关键原则:联合索引的字段顺序按等值查询 > 范围查询 > 排序字段排列。
套路二:覆盖索引,让查询"不走回表"
什么是覆盖索引?就是查询需要的所有字段都在索引里,MySQL不需要回表查数据行。
实战案例:用户信息查询
如果只在mobile上建索引,MySQL会先去索引找到user_id,再回表查nick_name、avatar、level三个字段,多了一次随机I/O。
优化方案:
这样查询的所有字段都在索引里,回表操作完全消除,速度提升2-3倍。
使用技巧:用EXPLAIN查看执行计划,如果Extra列显示Using index,说明触发了覆盖索引。
套路三:分页查询深翻页优化
这是小程序列表页最常见的坑。
错误写法:
MySQL需要扫描前100000条再丢弃,数据量越大越慢。
优化写法(游标分页):
配合联合索引,每次只扫描20条,翻到100页以后性能依然稳定。
小程序端配合:用
scroll-view的滚动加载代替传统分页器,使用create_time作为游标传给后端。第三步:两个容易被忽略的"隐形杀手"
杀手一:隐式类型转换
MySQL会把phone字段转成数字再比较,索引失效,全表扫描。
正确写法:
杀手二:IN + 子查询
优化方案:改成JOIN
附:慢查询优化速查表
ANALYZE TABLE更新统计信息EXPLAIN检查,使用覆盖索引日常巡检三件事
优化不是一锤子买卖,我每周会做这三件事:
慢查询日志周报:每周一查看上周慢查询TOP 10,新增的慢查询及时处理
索引使用率分析:通过
sys.schema_unused_indexes查看从未使用的索引,及时删除数据归档:超过1年的订单数据迁移到历史表,减轻主表压力
写在最后
慢查询优化不是玄学,是有章可循的技术活。核心就三句话:
慢查询日志是导航仪,先找到问题再动手
联合索引是最强武器,用好最左前缀原则
EXPLAIN是照妖镜,执行计划一看便知问题在哪
希望这份实战指南能帮你解决小程序卡顿的问题。
如果你在优化过程中遇到奇怪的慢查询,欢迎在评论区贴出SQL和表结构,我看到都会回复。