GaussDB(for MySQL)慢查询定位实战

举报
yd_231746916 发表于 2026/10/28 08:15:03 2026/10/28
【摘要】 适用范围:华为云 GaussDB(for MySQL)(MySQL 8.0 兼容语法)。文中参数名以你所用实例版本的官方文档为准,下同。 背景:先看执行计划,再动手改 慢查询处理的常见误区是"感觉慢就加索引"。加错索引GaussDB 数据库 SQL优化

> 适用范围:华为云 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 完整输出贴到社区问答,比只贴一句"很慢"更容易获得有效回答。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。