为提高效率,提问时请提供以下信息,问题描述清晰可优先响应。
【DM版本】:8
【操作系统】:LINUX
【CPU】:
【问题描述】*:应用执行和数据库执行效率不一致。
同一条sql 在mybatis中用动态参数执行下来效率很慢,最开始以为是隐式转换的问题,便把查询条件字段都对应上后确实变快了,但是在晚上统计信息收集和执行计划缓存清除的定时任务执行后,第二天早上访问页面这个业务sql又回到70秒的查询时间,排查结果数据库版本和驱动都是一致的。今天又把查询条件的and给去掉了,发布后效率又恢复正常。何意味?
收集一下统计信息,动态传参,和数据库里边可能执行计划不太一样

楼主这个 case 太典型了,而且你描述得特别到位——应用 70s、库里 1s,尤其那个"去掉一个 and 就恢复"简直是破案级别的线索。先给个方向:大概率是应用侧走的是绑定变量、用不上直方图,被缓存里的一条坏执行计划拖住了,不是 SQL 本身慢。下面一步步帮你坐实。
一、先说根因方向:字面量 vs 绑定变量的计划差
你在库里直跑的是"字面量"(比如
col = 123),优化器能命中该列直方图,按真实数据分布选计划,所以 1 秒。而 mybatis
#{}走的是 PreparedStatement 绑定变量(col = ?)。达梦对绑定变量默认不用直方图,只能用(总行数 - 空值) / 不同值个数这种均匀假设来估。过滤列一旦有数据倾斜、或条件一多,很容易把大结果集估成"很小",于是错选"嵌套循环 + 大量回表"(NLJ 回表风暴),直接拖到 70 秒。二、为什么"每晚定时任务后第二天又慢"
你每晚做"统计信息收集 + 清执行计划缓存",方向本身没错——收集完统计本来就该清缓存。但清完之后,第二天这条 SQL 重新硬解析时,应用还是用
?绑定变量,照样吃不上刚收集好的直方图,于是又生成一个"看着合理实则糟糕"的计划并立刻被缓存。等于每晚都在重新撞一次坏计划。三、"去掉一个 and 就变快"恰恰证明了问题所在
SQL 文本一改 → 新的 SQL ID → 不再命中旧缓存 → 强制重解析,这次条件变少、估算改变,恰好选对了。这反过来说明:问题在缓存锁定的坏计划,不在 SQL 逻辑。
四、建议先照这三步验证(结果贴回来我帮你看)
第 1 步|抓应用真实计划(开 SQL 日志,跑一下那个业务请求)
CALL SP_SET_PARA_VALUE(1,'SVR_LOG',1);CALL SP_SET_PARA_VALUE(1,'SVR_LOG_PLN_STR',1);到 SQL 日志里看这条 SQL 落的是
CSCN2/SSEK2、NLJ还是HASH JOIN,以及预估行数对不对。排查完记得关回SVR_LOG=0,别让它一直开着。第 2 步|看缓存里这条计划
SELECT CACHE_ITEM, SQLSTR FROM V$CACHEPLN WHERE SQLSTR LIKE '%你的业务表名%';第 3 步|对比字面量计划
在库里用字面量
EXPLAIN同一条 SQL(把?换成具体值),看计划是否明显不同。五、解决方向(按推荐度来)
应急:低峰期
CALL SP_CLEAR_PLAN_CACHE();临时缓解——但会复发,别把它当根治。根治 A(倾斜列首选):给这条 SQL 加 HINT,让绑定变量也每次用直方图重算、且不缓存坏计划:
SELECT /*+ PLAN_NO_CACHE BEXP_CALC_ST_FLAG(128) */ ... WHERE 倾斜列 = ? AND ...;倾斜严重、并发不高的关键查询特别适合。
根治 B:如果这条 SQL 是分析型 / 大结果集查询,本来就不适合绑定变量。可考虑 mybatis 改用
${}字面量拼接(注意防 SQL 注入),或把验证过的正确计划用SP_SET_PLN_BINDED固化下来,避免被坏计划覆盖。顺带:对高度倾斜的过滤列,确认收集了直方图(
STAT 100 SIZE 桶数 ON 表(列),最多 10000),让优化器拿到真实分布。先把第 1 步抓到的计划贴回来,我帮你判断是 NLJ 回表风暴还是全表扫误判,再决定走 A 还是 B。