GaussDB(for MySQL)慢查询定位实战
> 适用范围:华为云 GaussDB(for MySQL)(MySQL 8.0 兼容语法)。文中参数名以你所用实例版本的官方文档为准,下同。
背景:先看执行计划,再动手改
慢查询处理的常见误区是"感觉慢就加索引"。加错索引不仅不提速,还会拖慢写入、占用空间。正确顺序是:先让慢 SQL 浮出来 → 读执行计划定位瓶颈层 → 单变量修复 → 复测对比。本文给一份可照抄的排查清单,以及 5 种典型索引失效场景的 SQL 反例。
第一步:让慢 SQL 浮出来
确认慢日志开启与阈值(在测试实例上先验证,再在生产低峰期调整):
SHOW VARIABLES LIKE 'slow_query_log%';
SHOW VARIABLES LIKE 'long_query_time';
-- 临时调低阈值观察(示例 1 秒),观察完记得改回生产值
SET GLOBAL long_query_time = 1;
如果实例接入了数据管理服务(DAS),也可以在它的 SQL 窗口执行上面的语句;本文只依赖 SQL 本身,不依赖具体控制台入口。
拿到慢日志后,按 出现频率 × 单次耗时 排序,优先处理总耗时贡献最大的那几条,而不是单次最慢的。
第二步:读懂 EXPLAIN 的三个关键字段
EXPLAIN SELECT o.order_id, u.user_name
FROM orders o JOIN users u ON o.user_id = u.user_id
WHERE o.created_at >= '2026-10-01' AND o.status = 2;
重点看三列:
· type:访问类型。`ALL` 全表扫描;`index` 扫整棵索引树;`ref`/`eq_ref` 走索引查找。线上查询的 JOIN 内表出现 `ALL`,通常就是要处理的目标。 · rows:预估扫描行数。和表真实行数量级对比,误差大说明统计信息过期。 · Extra:`Using filesort`(排序没吃到索引)、`Using temporary`(临时表,常见于 GROUP BY/DISTINCT)、`Using index`(覆盖索引,好信号)。
第三步:五种典型索引失效场景
-- 1. 对索引列做函数或运算 → 失效
SELECT * FROM orders WHERE DATE(created_at) = '2026-10-27'; -- ✗
SELECT * FROM orders WHERE created_at >= '2026-10-27 00:00:00'
AND created_at < '2026-10-28 00:00:00'; -- ✓ 改为范围
-- 2. 隐式类型转换 → 失效(user_id 是 VARCHAR 时传数字)
SELECT * FROM users WHERE user_id = 12345; -- ✗
SELECT * FROM users WHERE user_id = '12345'; -- ✓ 类型对齐
-- 3. 前导模糊匹配 → 失效
SELECT * FROM users WHERE user_name LIKE '%wang'; -- ✗
SELECT * FROM users WHERE user_name LIKE 'wang%'; -- ✓ 前缀可走索引
-- 4. 联合索引不满足最左前缀 → 失效(索引为 (status, created_at))
SELECT * FROM orders WHERE created_at >= '2026-10-01'; -- ✗ 跳过了 status
SELECT * FROM orders WHERE status = 2
AND created_at >= '2026-10-01'; -- ✓
-- 5. OR 两侧有非索引列 → 整体放弃索引;改用 UNION
SELECT * FROM orders WHERE order_no = 'A001' OR remark = 'vip'; -- ✗ remark 无索引
SELECT * FROM orders WHERE order_no = 'A001'
UNION
SELECT * FROM orders WHERE remark = 'vip'; -- ✓
第四步:一个完整修复案例
订单表 800 万行,运营侧查询近 30 天已支付订单,原语句:
SELECT * FROM orders
WHERE DATE(created_at) >= '2026-09-28' AND status = 2;
修复前 EXPLAIN:`type=ALL, rows≈810万, Extra=Using where`。两个问题:`DATE()` 包裹索引列导致失效;`SELECT *` 拉回全部列。
修复后:
-- 联合索引:等值列在前、范围列在后
ALTER TABLE orders ADD INDEX idx_status_created (status, created_at);
SELECT order_id, user_id, amount, created_at -- 只取需要的列
FROM orders
WHERE status = 2
AND created_at >= '2026-09-28 00:00:00';
修复后 EXPLAIN:`type=range, rows≈27万, Extra=Using index condition`,实测该查询从 4.2s 降到 0.3s 左右。注意:这两个数字来自我当时的测试环境,你的表结构与数据分布不同,结果不可直接套用。
边界与备注
1. 统计信息过期会让 `rows` 严重失真,必要时执行 `ANALYZE TABLE orders;` 之后再看执行计划。 2. 生产加索引用低峰期执行,注意锁与长事务风险;GaussDB(for MySQL) 对在线 DDL 有自己的实现,操作前查官方文档确认行为。 3. 改完一处立刻复测,不要同时改多个变量;改不动时把 EXPLAIN 完整输出贴到社区问答,比只贴一句"很慢"更容易获得有效回答。
- 点赞
- 收藏
- 关注作者
评论(0)