找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

6144

积分

0

好友

780

主题
发表于 6 天前 | 查看: 21| 回复: 0

业务人员反馈 GaussDB 里出现了一条慢 SQL,直接拖慢了功能验证流程。我们随后对这条 SQL 展开了排查和优化,过程比较典型,分享出来供有类似问题的读者参考。

性能分析

SQL 文本如下:

SELECT calc.header_id AS "dtlaccountId", acc.accentity_id AS "accentity", acc.accbook_id AS "accbook", acc.asset_id AS "assetId", acc.busi_date AS "busiDate" , calc.direction AS "direction", calc.bln_depr AS "isdepr" FROM FIEFA.fa_ledger_b_calc calc, FIEFA.fa_ledger_h acc WHERE calc.header_id = acc.id AND acc.accentity_id = '1873583679575949319' AND acc.accbook_id IN ('1892143719336378386') AND acc.asset_id IN ('1933638798553907250') AND acc.src_bill_type_id = '2314079661183488EFA' AND acc.ytenant_id = 'qgb2au5x' AND acc.busi_date < '2026-07-01' AND acc.busi_date IS NOT NULL ORDER BY acc.busi_date DESC

这条 SQL 的执行计划如下:

GaussDB慢SQL优化前执行计划可视化截图

问题诊断(瓶颈定位)

总耗时 1756.830 ms,其中 1552 ms 几乎全部耗费在 fa_ledger_hIndex Scan 上——这是典型的"索引前导列选对了,但选择性严重不足,被 Filter 大量过滤"的场景。

Index Scan using idx_fa_ledger_h_optimize on fa_ledger_h acc
(actual time=1552.152..1756.173 rows=1 loops=1)
Index Cond: ((ytenant_id)::text = 'qgb2au5x'::text AND (accentity_id)::text = '1873583679575949319'::text)
Filter: (busi_date IS NOT NULL) AND ((busi_date)::text < '2026-07-01'::text) AND ((accbook_id)::text = '1892143719336378386'::text) AND ((asset_id)::text = '1933638798553907250'::text) AND ((src_bill_type_id)::text = '2314079661183488EFA'::text)
Rows Removed by Filter: 480231  ← 关键证据

注意 Rows Removed by Filter: 480231 这一行,索引扫描返回了 48 万行候选记录,最终只有 1 行满足条件。剩下的 480,231 行全部被 Filter 丢弃,大量的无效扫描拖垮了整条查询。

处理结果

收集 fa_ledger_hfa_ledger_b_calc 两张表的统计信息之后,新的执行计划如下:

GaussDB慢SQL优化后执行计划可视化截图

优化效果:1756 ms → 0.614 ms,性能提升约 2860 倍。

新执行计划的关键部分解读:

Sort (cost=10.33..10.34 rows=1) (actual time=0.268..0.269 rows=2 loops=1)
Sort Key: acc.busi_date DESC
Sort Method: quicksort Memory: 25kB
-> Nested Loop (cost=0.00..10.32 rows=1) (actual time=0.205..0.224 rows=2 loops=1)
-> Index Scan using i_fa_ledger_h_asset_id on fa_ledger_h acc
(cost=0.00..8.03 rows=1) (actual time=0.174..0.185 rows=1 loops=1)
Index Cond: (asset_id = '1933638798553907250')
Filter: (busi_date IS NOT NULL) AND (busi_date < '2026-07-01')
AND (accentity_id = '1873583679575949319')
AND (accbook_id = '1892143719336378386')
Rows Removed by Filter: 31  ← 关键指标
-> Index Scan using i_fa_ledger_b_calc_header_id on calc
Index Cond: (header_id = acc.id)
Total runtime: 0.614 ms

新旧计划对比

维度 旧计划 新计划
驱动索引 idx_fa_ledger_h_optimize (ytenant_id, accentity_id) i_fa_ledger_h_asset_id
Rows Removed by Filter 480,231 31
驱动表耗时 1552 ms 0.185 ms
总耗时 1756.830 ms 0.614 ms

根因分析

其实根源不在索引本身,而在统计信息。旧统计信息下,优化器对 (ytenant_id, accentity_id) 组合的行数估算严重偏低,甚至认为只会返回 1 行。基于这个错误的估算,优化器判断走 idx_fa_ledger_h_optimize 最划算。结果索引实际扫出了 48 万行,只有 1 行匹配,这就是典型的"规划器估算偏差 → 选错索引 → 实际执行雪崩"。

执行 ANALYZE 刷新统计信息之后,优化器重新评估:asset_id 的基数远高于 (ytenant_id, accentity_id) 组合。更准确地说,asset_id 这一列的数据分布更加稀疏,每张资产在头表里基本只有一条记录,选择性反而最强。于是优化器迅速切换到 i_fa_ledger_h_asset_id,过滤后仅剩 31 行,再经 Nested Loop 关联,整体耗时直接降到亚毫秒级别。

这次调优也再次说明:统计信息过时是慢 SQL 的常见诱因之一。遇到优化器"固执"地选错索引时,不妨先确认统计信息是否过期,很多场景下会收到立竿见影的效果。

类似的技术实战案例与讨论,欢迎到 云栈社区 交流。




上一篇:Meta紧急叫停第二轮AI裁员:AI代码暴涨220%,事故与救火时间反增
下一篇:Oracle 在线重定义表结构:DBMS_REDEFINITION 底层依赖什么机制?
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2026-9-17 20:07 , Processed in 0.544680 second(s), 41 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2026 云栈社区.

快速回复 返回顶部 返回列表