GaussDB慢查询返工48小时记录

举报
yd_272744321 发表于 2026/10/21 10:30:08 2026/10/21
【摘要】 结算接口晚高峰抖到 2.6 秒,我在华为云 GaussDB(for MySQL) 连做两轮排查。第一轮误判导致索引返工,白白等了 17 分钟。第二轮改写 SQL、重建联合索引并拆分批量更新事务,48 小时把 P95 拉回 180ms,同时把临时扩容费用压回可接受范围。

事故发生时的环境

周二 19:12,结算服务开始抖,API 网关里 /settle/list 的 P95 从 118ms 冲到 2.6s,最高一笔 4.1s。业务跑在华为云 3 台 ECS,单机 4 vCPU / 8 GiB,Ubuntu 22.04.4,JDK 17.0.10,Spring Boot 3.2.5,MyBatis 3.5.15,连接池 HikariCP 5.1.0。数据库是 GaussDB(for MySQL) 8.0.26,主实例 4 vCPU / 16 GiB,300GB 超高 I/O 云盘,1 个只读节点;主库按量 2.73 元/小时,只读 1.41 元/小时。

我先排应用侧,怕是连接池打满后把问题伪装成慢 SQL。Hikari 实时值是 active=173、idle=5、pending=0,配置没变:maximumPoolSize=180、connectionTimeout=3000、validationTimeout=1000。Tomcat 业务线程池 maxThreads=400,那一小时拒绝数还是 0,队列峰值 37,没有出现线程池雪崩。我还翻了 JVM,19:00 到 20:00 Full GC 次数 0,老年代占用在 61% 到 66%,所以这条线很快排除了。

我那天值班到 01:40 才离开工位。

我抓到的第一批慢 SQL

19:18 到 19:26 这 8 分钟里,数据库 CPU 从 37% 拉到 91%,活跃连接从 140 涨到 423,网关 5xx 比例从 0.2% 跑到 3.7%。我没改业务代码,直接把慢日志阈值从 1 秒压到 0.2 秒,采样 30 分钟,目的是把次慢查询也抓出来,不只盯着最坏那几条。顺手开了 log_queries_not_using_indexes,我想确认到底是索引失效还是纯资源不足。

2026-09-17 19:14:08.731 ERROR o.h.e.j.s.SqlExceptionHelper
SQL Error: 3024, SQLState: HY000
Statement cancelled due to timeout

2026-09-17 19:16:41.205 WARN  com.zaxxer.hikari.pool.ProxyConnection
SQLState: HY000, ErrorCode: 1205
Lock wait timeout exceeded; try restarting transaction

# Time: 2026-09-17T19:20:11.332194+08:00
# Query_time: 5.874321  Lock_time: 0.000312 Rows_sent: 50  Rows_examined: 12604872
SELECT id,user_id,amount FROM bill_order ...;
SHOW VARIABLES LIKE 'long_query_time';
SET GLOBAL long_query_time = 0.2;
SET GLOBAL slow_query_log = ON;
SET GLOBAL log_queries_not_using_indexes = ON;

SELECT digest_text, count_star,
       avg_timer_wait/1000000000 AS avg_ms,
       max_timer_wait/1000000000 AS max_ms
FROM performance_schema.events_statements_summary_by_digest
ORDER BY avg_timer_wait DESC
LIMIT 5;

最慢的是订单筛选,30 分钟命中 12,842 次,平均 1834ms,最高 6127ms。EXPLAIN ANALYZE 给出的关键信息是 type=ALL、rows=12873456、filtered=1.71、key=NULL,执行计划里还有 Using where; Using filesort。表结构里明明有 idx_pay_time(pay_time),但 SQL 写成了 DATE(pay_time)=?,函数把索引可用性直接砍掉,这个是我评审漏掉的,没借口。

我做错的一次索引变更

20:25 我判断太急,先补了 status 单列索引,想靠 status IN (1,2) 缩扫描范围。DDL 用了在线参数,命令是 ALTER TABLE bill_order ADD INDEX idx_status(status), ALGORITHM=INPLACE, LOCK=NONE;。这条在 GaussDB 控制台显示执行了 17 分钟。期间 SHOW PROCESSLIST 反复出现 copy to tmp table 和 Waiting for table metadata lock,SHOW ENGINE INNODB STATUS\G 里 History list length 从 18,902 涨到 46,317,写入线程明显被拖慢。

索引建完后,接口 P95 还是 2.3s 左右,数据库 CPU 从 91% 又抬到 97%。我又灰度了 10 分钟 FORCE INDEX(idx_status),想强行让优化器走新索引,结果单次查询均值从 1.8s 变成 2.0s,Rows_examined 从 1260 万降到 910 万但回表次数暴增,buffer pool 命中率从 99.1% 掉到 96.8%,体感更差。我当场回滚 SQL hint,保留索引但不再强制使用。

这个锅我自己背。

真正起作用的修改

21:50 我回到 SQL 本身,分两步改。第一步是去掉函数条件,把日期判断改成时间范围;第二步是把排序从 id DESC 改成 pay_time DESC,id DESC,让排序顺序和联合索引一致,减少 filesort。这里我还踩了个小坑,第一版我把结束时间写成 23:59:59,结果漏了 37 条 pay_time 带毫秒的数据,验数直接对不上。后来改成服务层算边界:start=2026-09-17 00:00:00.000、end=2026-09-17 23:59:59.999,时区固定 Asia/Shanghai,并在 mapper 参数里明确传 Timestamp,不用字符串比较,才把边界问题关掉。

-- 旧 SQL
SELECT id, user_id, amount
FROM bill_order
WHERE tenant_id = ?
  AND status IN (1,2)
  AND DATE(pay_time) = ?
ORDER BY id DESC
LIMIT 50;

-- 新 SQL
SELECT id, user_id, amount
FROM bill_order
WHERE tenant_id = ?
  AND status IN (1,2)
  AND pay_time BETWEEN ? AND ?
ORDER BY pay_time DESC, id DESC
LIMIT 50;

ALTER TABLE bill_order
ADD INDEX idx_tenant_paytime_status_id(tenant_id, pay_time, status, id),
ALGORITHM=INPLACE, LOCK=NONE;
ANALYZE TABLE bill_order;

联合索引 23:02 开始构建,23:36 完成,耗时 34 分钟。业务没中断,但那 34 分钟写入延迟抬了一段,监控里 P99 从 80ms 到 210ms,持续 11 分钟。索引生效后同一条查询 EXPLAIN 变成 type=range,rows=4213,extra=Using index condition,压测 5000 次的均值 146ms,95 分位 212ms,和线上回落趋势一致。

另一个慢点在批量退款任务,原来一笔事务更新 2 万行,事务时长 11.8s,和在线查询抢行锁。我没改 SQL,改的是事务边界和批次参数:每批 500 行,REQUIRES_NEW 独立提交,批次之间 sleep(20ms),失败批次重试 2 次,重试间隔 300ms。改完后单事务 620ms 左右,锁等待告警当晚清零,ErrorCode 1205 在日志里从每分钟 40+ 降到 0 到 2。

成本和回滚

21:10 我做过一次临时升配,把主库从 4c16g 升到 8c32g,持续 6 小时 20 分,读节点没动。升配后超时比例从 3.7% 降到 1.9%,但查询本身还是慢,说明规格只是缓冲,不是根因。账单额外支出 56.9 元,和控制台估算差 0.4 元,差额来自 I/O 计费。我把这笔钱记在事故复盘里了,避免下次同样的试错成本没人认领。

  • 19:12 告警触发,P95 2.6s
  • 19:40 开慢日志采样,阈值改 0.2s
  • 20:25 错误索引上线,17 分钟
  • 21:10 临时升配到 8c32g
  • 23:02 联合索引开始构建,23:36 完成
  • 次日 10:00 P95 回到 180ms,CPU 44% 到 52%

次日 11:30 我在低峰把规格降回 4c16g,降配窗口 9 分钟,应用侧没有连接失败,Hikari 等待队列峰值 7,恢复过程比我预想平稳。

还没解决的点

现在还有一个隐患,周日凌晨导入历史账单后,优化器偶尔会选错计划,这条查询会从 180ms 漂到 400ms。虽然没再到秒级,对账页面还是会卡一下,客服那边能感知。

我准备在导入任务尾部强制加 ANALYZE TABLE bill_order,再试 SQL Plan Baseline 固化执行路径;如果下个周末还漂,我会把导入和在线查询拆到不同实例,避免统计信息和缓存互相干扰。

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

评论(0)

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

全部回复

上滑加载中

设置昵称

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

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

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