当前位置: 首页 > news >正文

执行计划中的COLLECTION ITERATOR PICKLER FETCH导致的性能问题 (r5笔记第49天)

今天开发的同事找到我,让我评估一个sql语句。因为这条语句被应用监控组给抓取出来了,需要尽快进行性能调优。sql语句比较长,是由几个Union连接起来的子查询。xxxxxUNION SELECT /*+ leading (ar1_creditid_tab ar1_unapplied_credit) use_nl (ar1_creditid_tab ar1_unapplied_credit) */ UNIQUE 0, MAX (uc.credit_id) credit_id, 0, 0, 0, SUM (uc.unapplied_amount) allocated_amount, TO_DATE ('') due_date, 'Unapplied', '0', transaction_id FROM ar1_unapplied_credit uc, (SELECT COLUMN_VALUE AS credit_id FROM table(SELECT CAST (:5 AS ar1_numberarray_tp) credit_id FROM DUAL)) ar1_creditid_tab WHERE uc.reversal_trans_id IS NULL AND uc.credit_id = ar1_creditid_tab.credit_id AND uc.partition_id = NVL (:6, 0) AND uc.credit_type LIKE :7GROUP BY uc.transaction_id执行计划如下所示,可以看到资源消耗还是很高的。Plan hash value: 3920442503-----------------------------------------------------------------------------------------------------------------------------------------| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | Pstart| Pstop |-----------------------------------------------------------------------------------------------------------------------------------------| 0 | SELECT STATEMENT | | 2 | 184 | 368K (12)| 01:13:48 | | || 1 | SORT UNIQUE | | 2 | 184 | 368K (12)| 01:13:48 | | || 2 | UNION-ALL | | | | | | | || 3 | HASH GROUP BY | | 1 | 145 | 325K (1)| 01:05:04 | | || 4 | NESTED LOOPS | | | | | | | || 5 | NESTED LOOPS | | 1 | 145 | 325K (1)| 01:05:04 | | || 6 | NESTED LOOPS | | 1 | 130 | 325K (1)| 01:05:03 | | || 7 | NESTED LOOPS | | 1 | 80 | 325K (1)| 01:05:03 | | || 8 | NESTED LOOPS | | 606 | 27876 | 325K (1)| 01:05:03 | | || 9 | VIEW | | 8168 | 103K| 19 (0)| 00:00:01 | | || 10 | COLLECTION ITERATOR PICKLER FETCH| | 8168 | 16336 | 19 (0)| 00:00:01 | | || 11 | FAST DUAL | | 1 | | 2 (0)| 00:00:01 | | || 12 | PARTITION RANGE MULTI-COLUMN | | 1 | 33 | 40 (0)| 00:00:01 |KEY(MC)|KEY(MC)||* 13 | TABLE ACCESS BY LOCAL INDEX ROWID| AR1_CREDIT_DEBIT_LINK | 1 | 33 | 40 (0)| 00:00:01 |KEY(MC)|KEY(MC)||* 14 | INDEX RANGE SCAN | AR1_CREDIT_DEBIT_LINK_1IX | 1 | | 40 (0)| 00:00:01 |KEY(MC)|KEY(MC)||* 15 | TABLE ACCESS BY GLOBAL INDEX ROWID | AR1_CHARGE_GROUP | 1 | 34 | 1 (0)| 00:00:01 | ROWID | ROWID ||* 16 | INDEX UNIQUE SCAN | AR1_CHARGE_GROUP_PK | 1 | | 1 (0)| 00:00:01 | | || 17 | TABLE ACCESS BY GLOBAL INDEX ROWID | AR1_INVOICE | 1 | 50 | 1 (0)| 00:00:01 | ROWID | ROWID ||* 18 | INDEX UNIQUE SCAN | AR1_INVOICE_PK | 1 | | 1 (0)| 00:00:01 | | ||* 19 | INDEX UNIQUE SCAN | AR1_BILLING_ARRANGEMENT_PK | 1 | | 1 (0)| 00:00:01 | | || 20 | TABLE ACCESS BY INDEX ROWID | AR1_BILLING_ARRANGEMENT | 1 | 15 | 1 (0)| 00:00:01 | | || 21 | HASH GROUP BY | | 1 | 39 | 43675 (1)| 00:08:45 | | ||* 22 | HASH JOIN | | 1 | 39 | 43673 (1)| 00:08:45 | | || 23 | VIEW | | 8168 | 103K| 19 (0)| 00:00:01 | | || 24 | COLLECTION ITERATOR PICKLER FETCH | | 8168 | 16336 | 19 (0)| 00:00:01 | | || 25 | FAST DUAL | | 1 | | 2 (0)| 00:00:01 | | || 26 | PARTITION RANGE MULTI-COLUMN | | 3191 | 82966 | 43654 (1)| 00:08:44 |KEY(MC)|KEY(MC)||* 27 | TABLE ACCESS FULL | AR1_UNAPPLIED_CREDIT | 3191 | 82966 | 43654 (1)| 00:08:44 |KEY(MC)|KEY(MC)|-----------------------------------------------------------------------------------------------------------------------------------------而性能瓶颈就在于一个全表扫描。对于这条语句来说,从执行计划来看,在第24行出现了一个操作是COLLECTION ITERATOR PICKLER FETCH,相对比较陌生,查看了下,是对一个集合对象中的成员进行迭代取值,而这种操作在OTN中查看,被有些人评价为很糟糕的一种实现。THE ABSOLUTELY WORSE THING (other than an ORA-00600 or ORA-3113) that you can see. 参见https://community.oracle.com/thread/1009301?tstart=0哲学中说存在即合理,肯定是在特定的场景中使用才有一定的意义,主要在xml type的场景中会有所应用。这个场景肯定是不相关的。我们把问题进行简化,即排除其它的Union 子查询过滤,定位到其中的一个子查询,因为只有这个子查询使用到了AR1_UNAPPLIED_CREDIT 这个表。我们来看看这个子查询的执行计划情况。SELECT /*+ leading (ar1_creditid_tab ar1_unapplied_credit) use_nl (ar1_creditid_tab ar1_unapplied_credit) */ UNIQUE 0, MAX (uc.credit_id) credit_id, 0, 0, 0, SUM (uc.unapplied_amount) allocated_amount, TO_DATE ('') due_date, 'Unapplied', '0', transaction_id FROM ar1_unapplied_credit uc, (SELECT COLUMN_VALUE AS credit_id FROM table(SELECT CAST (:5 AS ar1_numberarray_tp) credit_id FROM DUAL)) ar1_creditid_tab WHERE uc.reversal_trans_id IS NULL AND uc.credit_id = ar1_creditid_tab.credit_id AND uc.partition_id = NVL (:6, 0) AND uc.credit_type LIKE :7GROUP BY uc.transaction_id执行计划如下,可见访问路径能够复现。Plan hash value: 981834188-----------------------------------------------------------------------------------------------------------------------------| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | Pstart| Pstop |-----------------------------------------------------------------------------------------------------------------------------| 0 | SELECT STATEMENT | | 1 | 39 | 43674 (1)| 00:08:45 | | || 1 | HASH GROUP BY | | 1 | 39 | 43674 (1)| 00:08:45 | | ||* 2 | HASH JOIN | | 1 | 39 | 43673 (1)| 00:08:45 | | || 3 | VIEW | | 8168 | 103K| 19 (0)| 00:00:01 | | || 4 | COLLECTION ITERATOR PICKLER FETCH| | 8168 | 16336 | 19 (0)| 00:00:01 | | || 5 | FAST DUAL | | 1 | | 2 (0)| 00:00:01 | | || 6 | PARTITION RANGE MULTI-COLUMN | | 3191 | 82966 | 43654 (1)| 00:08:44 |KEY(MC)|KEY(MC)||* 7 | TABLE ACCESS FULL | AR1_UNAPPLIED_CREDIT | 3191 | 82966 | 43654 (1)| 00:08:44 |KEY(MC)|KEY(MC)|-----------------------------------------------------------------------------------------------------------------------------细看这条sql语句,其中有一个子查询有些陌生,使用到了嵌套表。 (SELECT COLUMN_VALUE AS credit_id FROM table(SELECT CAST (:5 AS ar1_numberarray_tp) credit_id FROM DUAL)) ar1_creditid_tab对于这方面,自己也想开发讨教了下。大概知道了原委。首先定义的type是number类型。SQL> desc ar1_numberarray_tp ar1_numberarray_tp TABLE OF NUMBER然后可以嵌入多个值,比如我们类似向数组传入100,200,用sql语句就是下面的形式,得到的结果还是typeSQL> SELECT CAST (ar1_numberarray_tp(100,200) AS ar1_numberarray_tp) credit_id FROM DUAL;AR1_NUMBERARRAY_TP(100, 200)这个时候结合起来,就得到了一个结果集。SQL> SELECT COLUMN_VALUE AS credit_id FROM table(SELECT CAST (ar1_numberarray_tp(100,200) AS ar1_numberarray_tp) credit_id FROM DUAL); 100 200明白了这点,就能基本定位问题了,看来这条sql语句功能还是传入对应的id,做了一个类似的行列转换这个时候如果再能够进行简化。把SELECT COLUMN_VALUE AS credit_id FROM table(SELECT CAST (ar1_numberarray_tp(100,200) AS ar1_numberarray_tp) credit_id FROM DUAL); (SELECT :1 as credit_id from dual )性能如何呢?看看执行计划,可以看到资源消耗极低。比预想中要好得多。-----------------------------------------------------------------------------------------------------------------------------------------| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | Pstart| Pstop |-----------------------------------------------------------------------------------------------------------------------------------------| 0 | SELECT STATEMENT | | 2 | 158 | 60 (25)| 00:00:01 | | || 1 | SORT UNIQUE | | 2 | 158 | 60 (25)| 00:00:01 | | || 2 | UNION-ALL | | | | | | | || 3 | HASH GROUP BY | | 1 | 132 | 47 (5)| 00:00:01 | | || 4 | NESTED LOOPS | | | | | | | || 5 | NESTED LOOPS | | 1 | 132 | 45 (0)| 00:00:01 | | || 6 | NESTED LOOPS | | 1 | 117 | 44 (0)| 00:00:01 | | || 7 | NESTED LOOPS | | 1 | 67 | 43 (0)| 00:00:01 | | || 8 | NESTED LOOPS | | 1 | 33 | 42 (0)| 00:00:01 | | || 9 | FAST DUAL | | 1 | | 2 (0)| 00:00:01 | | || 10 | PARTITION RANGE MULTI-COLUMN | | 1 | 33 | 40 (0)| 00:00:01 |KEY(MC)|KEY(MC)||* 11 | TABLE ACCESS BY LOCAL INDEX ROWID| AR1_CREDIT_DEBIT_LINK | 1 | 33 | 40 (0)| 00:00:01 |KEY(MC)|KEY(MC)||* 12 | INDEX RANGE SCAN | AR1_CREDIT_DEBIT_LINK_1IX | 1 | | 40 (0)| 00:00:01 |KEY(MC)|KEY(MC)||* 13 | TABLE ACCESS BY GLOBAL INDEX ROWID | AR1_CHARGE_GROUP | 1 | 34 | 1 (0)| 00:00:01 | ROWID | ROWID ||* 14 | INDEX UNIQUE SCAN | AR1_CHARGE_GROUP_PK | 1 | | 1 (0)| 00:00:01 | | || 15 | TABLE ACCESS BY GLOBAL INDEX ROWID | AR1_INVOICE | 1 | 50 | 1 (0)| 00:00:01 | ROWID | ROWID ||* 16 | INDEX UNIQUE SCAN | AR1_INVOICE_PK | 1 | | 1 (0)| 00:00:01 | | ||* 17 | INDEX UNIQUE SCAN | AR1_BILLING_ARRANGEMENT_PK | 1 | | 1 (0)| 00:00:01 | | || 18 | TABLE ACCESS BY INDEX ROWID | AR1_BILLING_ARRANGEMENT | 1 | 15 | 1 (0)| 00:00:01 | | || 19 | HASH GROUP BY | | 1 | 26 | 12 (17)| 00:00:01 | | || 20 | NESTED LOOPS | | 1 | 26 | 10 (0)| 00:00:01 | | || 21 | FAST DUAL | | 1 | | 2 (0)| 00:00:01 | | || 22 | PARTITION RANGE MULTI-COLUMN | | 1 | 26 | 8 (0)| 00:00:01 |KEY(MC)|KEY(MC)||* 23 | TABLE ACCESS BY LOCAL INDEX ROWID | AR1_UNAPPLIED_CREDIT | 1 | 26 | 8 (0)| 00:00:01 |KEY(MC)|KEY(MC)||* 24 | INDEX RANGE SCAN | AR1_UNAPPLIED_CREDIT_1IX | 1 | | 8 (0)| 00:00:01 |KEY(MC)|KEY(MC)|-----------------------------------------------------------------------------------------------------------------------------------------和开发进一步沟通,得到的反馈是可以从业务上进行简化和改造。可以把原来的SELECT COLUMN_VALUE AS credit_id FROM table(SELECT CAST (ar1_numberarray_tp(100,200) AS ar1_numberarray_tp) credit_id FROM DUAL);改进为:(select CREDIT_ID from ar1_payment WHERE ACCOUNT_ID = :1)有了这些基础保证,再来看看整个sql语句的执行计划。Plan hash value: 416684901-----------------------------------------------------------------------------------------------------------------------------------------| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | Pstart| Pstop |-----------------------------------------------------------------------------------------------------------------------------------------| 0 | SELECT STATEMENT | | 11 | 524 | 254 (49)| 00:00:04 | | || 1 | SORT UNIQUE | | 11 | 524 | 254 (49)| 00:00:04 | | || 2 | UNION-ALL | | | | | | | || 3 | HASH GROUP BY | | 1 | 144 | 133 (2)| 00:00:02 | | || 4 | NESTED LOOPS | | | | | | | || 5 | NESTED LOOPS | | 1 | 144 | 131 (0)| 00:00:02 | | || 6 | NESTED LOOPS | | 1 | 129 | 130 (0)| 00:00:02 | | || 7 | NESTED LOOPS | | 1 | 79 | 129 (0)| 00:00:02 | | || 8 | NESTED LOOPS | | 3 | 135 | 128 (0)| 00:00:02 | | || 9 | PARTITION RANGE ALL | | 3 | 36 | 9 (0)| 00:00:01 | 1 | 41 || 10 | TABLE ACCESS BY LOCAL INDEX ROWID| AR1_CUSTOMER_CREDIT | 3 | 36 | 9 (0)| 00:00:01 | 1 | 41 ||* 11 | INDEX RANGE SCAN | AR1_CUSTOMER_CREDIT_3IX | 3 | | 8 (0)| 00:00:01 | 1 | 41 || 12 | PARTITION RANGE MULTI-COLUMN | | 1 | 33 | 40 (0)| 00:00:01 |KEY(MC)|KEY(MC)||* 13 | TABLE ACCESS BY LOCAL INDEX ROWID| AR1_CREDIT_DEBIT_LINK | 1 | 33 | 40 (0)| 00:00:01 |KEY(MC)|KEY(MC)||* 14 | INDEX RANGE SCAN | AR1_CREDIT_DEBIT_LINK_1IX | 1 | | 40 (0)| 00:00:01 |KEY(MC)|KEY(MC)||* 15 | TABLE ACCESS BY GLOBAL INDEX ROWID | AR1_CHARGE_GROUP | 1 | 34 | 1 (0)| 00:00:01 | ROWID | ROWID ||* 16 | INDEX UNIQUE SCAN | AR1_CHARGE_GROUP_PK | 1 | | 1 (0)| 00:00:01 | | || 17 | TABLE ACCESS BY GLOBAL INDEX ROWID | AR1_INVOICE | 1 | 50 | 1 (0)| 00:00:01 | ROWID | ROWID ||* 18 | INDEX UNIQUE SCAN | AR1_INVOICE_PK | 1 | | 1 (0)| 00:00:01 | | ||* 19 | INDEX UNIQUE SCAN | AR1_BILLING_ARRANGEMENT_PK | 1 | | 1 (0)| 00:00:01 | | || 20 | TABLE ACCESS BY INDEX ROWID | AR1_BILLING_ARRANGEMENT | 1 | 15 | 1 (0)| 00:00:01 | | || 21 | HASH GROUP BY | | 10 | 380 | 121 (2)| 00:00:02 | | || 22 | NESTED LOOPS | | | | | | | || 23 | NESTED LOOPS | | 10 | 380 | 119 (0)| 00:00:02 | | || 24 | PARTITION RANGE ALL | | 10 | 120 | 41 (0)| 00:00:01 | 1 | 201 || 25 | TABLE ACCESS BY LOCAL INDEX ROWID | AR1_PAYMENT | 10 | 120 | 41 (0)| 00:00:01 | 1 | 201 ||* 26 | INDEX RANGE SCAN | AR1_PAYMENT_1IX | 10 | | 40 (0)| 00:00:01 | 1 | 201 || 27 | PARTITION RANGE MULTI-COLUMN | | 1 | | 8 (0)| 00:00:01 |KEY(MC)|KEY(MC)||* 28 | INDEX RANGE SCAN | AR1_UNAPPLIED_CREDIT_1IX | 1 | | 8 (0)| 00:00:01 |KEY(MC)|KEY(MC)||* 29 | TABLE ACCESS BY LOCAL INDEX ROWID | AR1_UNAPPLIED_CREDIT | 1 | 26 | 8 (0)| 00:00:01 | 1 | 1 |-----------------------------------------------------------------------------------------------------------------------------------------可以看到性能的提升是非常大的。通过这个案例,我们可以看到,对于sql调优的很多关键点还是需要和开发配合,从业务上进行支持是很快捷的一种方式。这种调优方式可以从整体的角度来看待这个问题,而不单单是技术角度。这个时候调优工作就会轻松不少,清晰不少。在定位sql语句的性能瓶颈时,发现全表扫描相关的COLLECTION ITERATOR PICKLER FETCH操作在这个场景中是不合适的。能够用相关的索引扫描或者临时表来代替都是不错的选择。

相关文章:

  • 关于Oracle重启数据库的一个bug(r5笔记第50天)
  • 一个清理脚本的改进思路(r5笔记第51天)
  • 我身边的一些数据库事故 (r5笔记第52天)
  • 关于网络捐款 (r5笔记第53天)
  • 11g Active DataGuard初探(r5笔记第54天)
  • 半自动化运维之动态添加数据文件(一) (r5笔记第55天)
  • 半自动化运维之动态添加数据文件(二) (r5笔记第56天)
  • 无法运行的update问题解析(r5笔记第57天)
  • shell中echo的显示格式 (r5笔记第58天)
  • 人的力量和大自然的力量 (r5笔记第59天)
  • 半自动化运维之快速连接到指定环境(一) (r5笔记第61天)
  • 巧用外部表备份历史数据(r5笔记第62天)
  • pl/sql中的forall简单测试(r5笔记第63天)
  • 一次ORA-00600问题的排查和分析(上)(r5笔记第64天)
  • 一次ORA-00600问题的排查和分析(下)(r5笔记第65天)
  • [译]如何构建服务器端web组件,为何要构建?
  • CentOS7 安装JDK
  • exif信息对照
  • express如何解决request entity too large问题
  • FineReport中如何实现自动滚屏效果
  • input的行数自动增减
  • Java深入 - 深入理解Java集合
  • linux安装openssl、swoole等扩展的具体步骤
  • Linux链接文件
  • MYSQL如何对数据进行自动化升级--以如果某数据表存在并且某字段不存在时则执行更新操作为例...
  • Python学习笔记 字符串拼接
  • Redis 中的布隆过滤器
  • Redis的resp协议
  • RxJS 实现摩斯密码(Morse) 【内附脑图】
  • SAP云平台里Global Account和Sub Account的关系
  • ⭐ Unity 开发bug —— 打包后shader失效或者bug (我这里用Shader做两张图片的合并发现了问题)
  • VUE es6技巧写法(持续更新中~~~)
  • vue从创建到完整的饿了么(18)购物车详细信息的展示与删除
  • webpack项目中使用grunt监听文件变动自动打包编译
  • 关键词挖掘技术哪家强(一)基于node.js技术开发一个关键字查询工具
  • 关于字符编码你应该知道的事情
  • 回流、重绘及其优化
  • 两列自适应布局方案整理
  • 模仿 Go Sort 排序接口实现的自定义排序
  • 浅谈web中前端模板引擎的使用
  • 说说动画卡顿的解决方案
  • 译米田引理
  • 如何用纯 CSS 创作一个菱形 loader 动画
  • 我们雇佣了一只大猴子...
  • ​Linux Ubuntu环境下使用docker构建spark运行环境(超级详细)
  • # 再次尝试 连接失败_无线WiFi无法连接到网络怎么办【解决方法】
  • $(selector).each()和$.each()的区别
  • (1)(1.13) SiK无线电高级配置(六)
  • (7)STL算法之交换赋值
  • (pojstep1.1.1)poj 1298(直叙式模拟)
  • (二)WCF的Binding模型
  • (附源码)springboot 个人网页的网站 毕业设计031623
  • (亲测)设​置​m​y​e​c​l​i​p​s​e​打​开​默​认​工​作​空​间...
  • (详细版)Vary: Scaling up the Vision Vocabulary for Large Vision-Language Models
  • (转)四层和七层负载均衡的区别