一条异常诡异的 SQL 引起的性能问题

1. 案情描述

2015年9月末的一天,客户接连通过邮件、电话不断找我,急迫的寻求帮助。在联系上之后,告知其核心数据库突发发生了一个诡异现象,甚至导致业务系统该功能无法正常处理。

经过简单询问,发现仅仅是一条 SQL 导致的,而很诡异的是,这条 SQL 在第二次执行时,执行计划会发生了变化,导致执行效率极低,影响业务运行。

2. 案情分析

根据客户的陈述,该问题可随时重现,无论换个会话还是换个客户端工具都不会受到影响。即使把共享池 flush 掉,再次执行 SQL 仍然会发生同样的现象。

下面我们就来看看案情现场重现:

1)  一条 SQL 在同一个会话中执行两次,第一次执行时间为10秒,但第二次执行时效率很低,执行时间超过1分钟。下面是 SQL 文本:

SELECT /*bbbbb*/A.C_DOC_NO ASC_PLY_APP_NO,

       A.C_PLY_NO AS C_PLY_NO,

       B.N_PRM AS N_PRM,

NVL(TO_CHAR(A.T_APP_TM,'YYYY-MM-DD HH24:MI:SS'), CHR(0)) AS T_APP_TM,

       A.C_BLG_DPT_CDE AS C_DPT_CDE,

       A.C_PROD_NO AS C_PROD_NO,

NVL(B.C_APP_NME, CHR(0)) ASC_APP_NME,

NVL(B.C_APP_TEL, CHR(0)) ASC_APP_TEL

 FROM T_PLY_UNDRMSG A, T_PLY_BASE B, T_FIN_PLYEDR_COLDUE C

 WHERE 1 = 1

  AND ROWNUM < 1000

  AND A.C_DOC_NO = B.C_PLY_APP_NO

  AND A.C_DOC_NO = C.C_PLY_APP_NO(+)

  AND A.C_SOURCE = '1'

  AND A.C_SEND_MRK NOT IN ('2')

  AND DECODE(TRIM(C.C_OPT_NO), CHR(0), NULL, TRIM(C.C_OPT_NO)) IS NULL

  AND (NVL(C.C_PRM_TYP, CHR(0)) IN(CHR(0), 'R1'))

  AND (NVL(C.C_ARP_FLAG, CHR(0)) IN (CHR(0), '0', '3', '4'))

  AND (NVL(C.N_TMS, 0) IN (0, 1))

  AND B.C_HAND_PER = '1012337'

  AND A.T_APP_TM BETWEEN

       TO_DATE('2015-09-29 00:00:00','YYYY-MM-DD HH24:MI:SS') AND

       TO_DATE('2015-09-30 23:59:59','YYYY-MM-DD HH24:MI:SS')

  AND A.T_INPUT_TM BETWEEN

       TO_DATE('2015-09-29 00:00:00','YYYY-MM-DD HH24:MI:SS') AND

       TO_DATE('2015-09-30 23:59:59','YYYY-MM-DD HH24:MI:SS')

2)观察该 SQL 执行计划信息,发现第二次执行计划发生了变化,其中 T_PLY_BASE 表的索引扫描变成了分区表扫描,而且驱动表和被驱动表也发生了改变,第二次执行计划中的 COST 也是在这里出现了明显增高。


第一次执行计划如下:

53271be72dd1e363cf398053081b7f27.png

第二次执行计划:

07c55a0057a7b1f3158b45e9388e25eb.png

很明显,的确是第二个执行计划出现了问题,导致了性能的严重下降。但是问题是,为什么同一个 SQL 第二次执行时执行计划会变呢?甚至同一个 SQL 连续两次执行也是如此?

3)  理论上同一个会话上执行的同一个 SQL,第二次执行为软解析(或者软软解析),此时数据库应该重用执行计划,而不是产生新的执行计划。

在 11G 上出现了一些新特性,而其中一个典型会导致 SQL 执行计划发生改变的场景就是 ACS—自适应游标(adaptive_cursor_sharing)。但 ASC 典型出现的场景应该是使用了绑定变量的 SQL,但该 SQL 并未使用绑定,数据库中也并未通过 corsor_sharing 参数强制绑定,看起来应该不是 ACS,那么是什么原因呢?

通过查看资料我们发现,11G 不但出现了ACS这样的自动优化新特性,还出现了另一个自动优化特性—Cardinality Feedback 基数反馈(详细描述见附件)。而通过执行计划中的信息与基数反馈特性的对比,基本可以推断该问题是由11g 新特性统计信息 feedback 导致的 bug,只需要关闭该特性再做验证即可确认。

f5a5ac9f217d42bf9e0d21725710d964.png

4)  参考 MOS Statistics (Cardinality) Feedback - Frequently Asked Questions (文档 ID 1344937.1),文档有对 11GR2 Statistics Feedback 新功能引起执行计划变化的描述、如何确认及解决方法。

3. 结案陈词

在全民升级 11G 的这个时代,很多客户都面临着同样的问题:升?或不升?

10g 已经明确告知了官方支持已经到期,因此升级到 11g 新版本的建议已经是众所周知了。然而,仍然有很多用户还在犹豫,其根源就在于他们不能确定在升级到 11g 之后,是否业务运行会遇到这样那样的致命故障,谁也不愿意拿自己的核心系统来冒险。

其实不愿升级的根源在于对新版本新特性的不熟识,既然如此,如果有了成熟版本的最佳实践,能够让你的系统在升级的同时规避这些新特性带来的风险,那还有什么可担心的呢。

本次问题就是典型 11g 新特性——统计信息 Feedback 导致的 bug,这样的问题相对比较常见,我们一般推荐关闭自适应游标共享和统计信息回馈(事实上我们已经总结了不少应该关闭或者调整的新特性),通过两个参数就可以动态关闭它们,在我们很多其他客户的核心库中均已进行过设置,不会对系统造成损害,建议大多数 11g 核心系统最好关闭。

在执行了以下处理后,SQL 执行不但恢复正常,而且运行效率进一步得到了提高。

1)  关闭自适应游标共享和统计信息回馈11g 新特性。

alter system set"_optimizer_use_feedback" = false scope = both;

alter system set"_optimizer_adaptive_cursor_sharing" = false scope = both;

2)同时我们也对这条 SQL 的执行计划进行了优化,建议在表 T_UND_RMSG的T_INPUT_TM 列上的创建单列索引,这样就避免了出现跳扫的执行计划(同时还可将该 SQL 执行计划强制失效)。或者通过comment命令使相关 SQL 强制重新解析(注意,这两种方法都会将该表的所有 SQL 执行计划全部过期失效,代价较高)。

Create index xxxx on T_UND_RMSG(T_INPUT_TM);

Comment on table T_UND_RMSG is ‘xxxx’;

当然,在11g上有一个更为推荐的方法 - DBMS_SHARED_POOL.PURGE,这种方法将只失效特定执行计划异常的子游标,下面给出个参考样例:

select address, hash_value from v$sqlarea where sql_id = 'a6aqkm30u7p90';

ADDRESS          HASH_VALUE

---------------- ----------

C000000EB7ED3420 3248739616

exec dbms_shared_pool.purge('C000000EB7ED3420,3248739616','C');

附件:【11g 新特性】Cardinality Feedback 基数反馈介绍

Cardinality Feedback 基数反馈是版本11.2中引入的关于 SQL 自动性能优化的新特性,该特性用于针对统计信息陈旧、缺失直方图或虽然有直方图但 Cardinality 基数计算不准确的情况,它的出现主要是为了帮助 ORACLE 优化器依据更精准的 Cardinality 产生出更加正确的执行计划。当然,这也意味着 Cardinality 基数的评估准确与否,对于优化器异常重要,直接影响到后续的 JOIN COST 等重要的成本计算评估,从而影响 CBO 下执行计划的选择。

该新特性对于仅执行一次的 SQL 无效,主要用于多次执行的 SQL 执行计划校正。同时也要注意,sys 用户被默认禁用该特性。

在 SQL 第一次执行时,记录存储实际的 Cardinality 和评估的 Cardinality 之间的差异,如果差异较大,在第二次执行时,优化器会依据实际的 Cardinality 重新决策生成执行计划。但是需要注意的是,当使用更准确的 Cardinality 重新生成执行计划时,生成的执行计划有可能与第一次时完全相同。 这个技术的出现是由于优化器在一些情况下不能很好的计算 Cardinality 的数值,比如:统计信息缺失或陈旧、多谓词、直方图缺失或者缺少直方图等等,在这些情况下,Cardinality Feedback 可能会帮上忙。

下面来看看 Cardinality Feedback 是如何发挥作用的(使用普通用户测试):

3949c6444b997c1dfde51112327c6bfd.png

在 order_items 表上有2个谓词:o.unit_price= 15 and quantity > 1,由于优化器对于联合谓词评估不准,导致 ORACLE 优化器认为经过2个联合谓词的过滤,order_items 表只返回一条记录,进而优化器选择了 nest loop 的执行计划:


f13ac790fa66dfbb04158491b255b8d5.png

如果 Cardinality Feedback 被开启,在 SQL 第一次执行结束后,ORACLE 会把实际的 Cardinality 与评估的 Cardinality 做比较,如果差异较大,这些实际的 Cardinality 会被存储以期待 SQL 再次被执行时这些 Cardinality 被优化器所使用。

来看看第二次执行的情况:


543731ddf932eaeb0d07fe597e3604fd.png

很明显执行计划发生了改变,CBO 依据第一次执行所记录的 Cardinality,重新评估执行计划,在第二次执行时,已经选择了 HASH JOIN 的执行计划,在执行计划的 NOTE 部分也看到了 cardinality feedback used for thisstatement 字样。

11GR2 中可以通过 V$SQL_SHARED_CURSOR 视图的 USE_FEEDBACK_STATS 字段来表示该 SQL 是否使用了 Cardinality Feedback 特性。


通过 10053 trace 两次执行过程,可以很容易发现 Cardinality Feedback 是如何发挥作用的。例如:

aa2e632887b032944844ac2815a22d90.png

很明显可以看到在 E-Rows 列中,SQL 第一次运行时,由于未收集 T1 表上的统计信息且关闭了动态采样(optimizer_dynamic_sampling=0),所以基数评估值和实际值有着较大的差距。

cardinality feedback used for this statement 说明第二次执行时使用了 Cardinality Feedback 基数反馈,且其基数评估也十分精确了,这是因为第二次执行时考虑到第一次执行时的基数反馈。

我们换个角度,用10053来看看更详细的 cardinality feedback:

261e3e933633b08967c81dea354e8522.png

可以看到第二次执行时 SQL 最终转换加入了 OPT_ESTIMATE (TABLE"T1" ROWS=1920.000000 )的 HINT ,OPT_ESTIMATE HINT 一般由 kestsaFinalRound (内核函数)生成。该 HINT 用以纠正优化器评估,最终反应出优化的不足或者导致 BUG。

可以通过 V$SQL_SHARED_CURSOR 和来找出现有系统 shared pool 中仍存在的使用了 Cardinality Feedback 基数反馈的子游标:

c2ad02273115a728319cbbe98a2ca0ef.png

可以使用 cardinality HINT 来强制使用 Cardinality Feedback 。

select /*+ cardinality(t1,  1) */ count(*) from t1;

新特性总是听起来很美,真的好用吗,其实并不见得。Cardinality Feedback 基数反馈在一些场景中也会导致错误的执行计划,所以我们可能会遇到第一次执行很快,第二次反而慢的情况。 cardinality feedback 技术给优化器提供了更加精准的 cardinality 来帮助优化器更容易产生优秀的执行计划,但 ORACLE 里也有其他的一些技术来提升评估 cardinality 的精准性,如:dynamic sampling,多列联合的统计信息收集等,如果合理的采用了这些技术,cardinalityfeedback 技术其实并没有太大的必要性。

  • 如何禁用 Cardinality Feedback 基数反馈

考虑到 stable,往往考虑关闭该特性。

可以通过多种方法禁用该特性:

1. 使用 _optimizer_use_feedback 隐藏参数

session 级别

SQL>alter session set “_optimizer_use_feedback”=false;

system级别

SQL>alter system set “_optimizer_use_feedback”=false;

2. 使用opt_param(‘_optimizer_use_feedback’ ‘false’) HINT

select /*+opt_param(‘_optimizer_use_feedback’ ‘false’) cardinality(t1,1) */ count(*)

from t1;

  • 已知的典型cardinatilty feedback问题     

Fixed in 11.2.0.2                                  
Note 8608703.8 Bug 8608703 - SubOptimal Execution Plancreated by Cardinality Feedback        
Note 9465425.8 Bug 9465425 - New cursors generated after cardinality feedback                
Note 9342979.8 Bug 9342979 Suboptimal plan change with cardinatilty feedback      
                                                                                                
Fixed in 12g                          
Note 8521689.8 Bug 8521689 - SubOptimal execution plan onsecond execution of GROUP BY query  
Note 8729064.8 Bug 8729064 Adaptive cursor sharing fails to share /USE_FEEDBACK_STATS not set

------ The End

如何加入云和恩墨大讲堂微信群

搜索盖国强(Eygle)微信号:eyygle,或者扫描下面二维码,备注:云和恩墨大讲堂,即可入群。每周与千人共享免费技术分享,与讲师在线讨论。

81c4a6099708c67a40ee98c29d565f35.png

云和恩墨

数据驱动,成就未来。整合业界顶尖的技术与合作伙伴资源,围绕数据及相关领域,提供解决方案和专业服务。

IT基础架构

zData一体机 - 分布式存储解决方案

数据架构

Oracle DB2 MySQL NoSQL

专项服务:架构 / 安全 / 高可用 / 容灾 / 优化 / SQL 质量管控

运维服务:运维服务  | 代维服务

人才培养:个人认证 | 企业内训

软件产品:SQL审核 - Z3 | 监控 - Zone | 数据恢复 - ODU

应用架构

应用软件开发:数据建模 | SQL审核和优化 | 中间件服务

业务架构

电子渠道(网络销售)分析系统 | 数据治理

恩墨学院

恩墨学院是云和恩墨(北京)信息技术有限公司旗下的培训事业部,创业数年专注于数据库认证、技能培训,以专业的讲师塑造品牌,以专业的训练保证就业,目前已经发展成为国内数据库领域培训领导品牌。

86fbb3b7a9bac929855519aa0bc446e6.png

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值