执行计划的偏差导致的性能问题(r3笔记第12天)

时间:2022-05-04
本文章向大家介绍执行计划的偏差导致的性能问题(r3笔记第12天),主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

在生产环境中有一条sql语句,查看执行计划来看,效果还是可以接受的。 sql语句类似下面的样子,可以看到里面还使用了比较纠结的外连接。从执行计划来说,默认是走nested loop join,数据的查取中会走索引,从oracle的分析来说这样的效果要好一些。 SELECT /*+ leading(cyc_cust,cyc_pay) */ CUST.CUSTOMER_ID, CUST.WEIGHT, CYC_CUST.STATUS, CYC_CUST.DB_STATUS, CYC_CUST.UNDO_REQ_TYPE, CYC_PAY.BA_NO AS BA_NO, CYC_PAY.STATUS AS PAY_STATUS, CYC_PAY.DB_STATUS AS PAY_DB_STATUS, CYC_PAY.UNDO_REQ_TYPE AS PAY_UNDO_REQ_TYPE, CYC_CUST.CYCLE_SEQ_RUN FROM CUSTOMER CUST, CYCLE_CUSTOMERS CYC_CUST, CYC_PAYER_POP CYC_PAY WHERE CYC_CUST.PERIOD_KEY = 57 AND CYC_CUST.CUSTOMER_KEY = 87 AND CYC_CUST.CYCLE_SEQ_NO =3325 AND CUST.CUSTOMER_ID = CYC_CUST.CUSTOMER_NO AND CYC_PAY.PERIOD_KEY(+) = CYC_CUST.PERIOD_KEY AND CYC_PAY.CUSTOMER_KEY(+) = CYC_CUST.CUSTOMER_KEY AND CYC_PAY.CUSTOMER_NO(+) = CYC_CUST.CUSTOMER_NO AND CYC_PAY.CYCLE_SEQ_NO(+) = CYC_CUST. CYCLE_SEQ_NO AND (CYC_PAY.UNDO_REQ_TYPE <> 'N' OR CYC_CUST.UNDO_REQ_TYPE <> 'N') ORDER BY CUSTOMER_ID; 预计时间在1分半左右,在生产环境中单个执行的时候差不多控制在20~30秒左右。这样看来实际效果比期望的要好。 但是在生产环境中执行的时候,是通过一个程序来触发的,会并行的执行多个这样的查询,结果性能是越来越糟糕,最后本来执行20秒的语句现在又变成了1分半了。 在尝试加入4个并行之后,效果没有任何的改观。 在排查这个问题的时候有两个方面的考虑,也是在调优中可能比较纠结的问题。 一个是nested loop join和hash join的取舍 这个问题按照理论来说是比较好判断的,如果大表,小表关联,是建议采用nested loop join的。 如果大表,大表关联,是建议采用hash join的。 但是在一些sql的执行过程中,可能根据执行计划走了索引,结果按照预期,过滤出的数据应该很少,在解析的时候就走了nested loop join,但是实际中可能数据分布很不均匀,有时候过滤出的数据多,有时候过滤出的数据少。 这样一来,如果过滤出的数据量大的时候,走了nested loop join 结果就会发送大量的io请求,然后不停的去走关联,瓶颈都消耗在io等待上了。 第二个问题是关于索引和全表扫描 这个问题,可能开发人员的普遍认识就是全表扫描要比索引要慢,大多数情况是,但是dba碰到的更多情况是希望走全表扫描,因为走索引的代价其实更大。如果表很小的情况下,走索引和全表扫描来说,全表扫描要更好一些, 如果表很大的情况下,但是通过索引过滤出的数据也比较多,那么也就建议还是走全表吧。 现在生产环境中碰到的这个sql问题就有如上的两种情况。 这几张表都是上千万的大表,有的表中的数据分布确实不透均匀,结果就在处理某一批数据的时候,性能还可以接受,但是在处理另外一批数据的时候性能抖动就很明显。 如果同时运行多个查询的情况下。可能等待时间都在io上了。 在查看awr报告中的一些指标之后,我尝试修改表的访问路径,把原本的两个表关联由nested loop join变为hash join。 然后两个表都坚持走全表扫描,当然了考虑到了执行的效率,加了4个并行。这样来说就算比较笼统的平衡了数据不均匀的问题。 修改hint为/*+ use_hash(CYC_CUST,CYC_PAY) parallel(CYC_CUST 4) */ 从执行计划来看是有些得不偿失的,cpu消耗要高很多。但是在实际执行的时候效果却很好。平均执行时间为10~15秒,执行的很稳定。在做了多个session的并发查询的情况下,速度还是很稳定。 所以执行计划只能作为大家调优的一个参考而已,dba需要对于数据需要有更加深入的了解。自己做到心中有数。 最后分享一下两种方案的执行效果。 采用nested loop join+索引扫描

new1.log:Elapsed: 00:00:56.30

new1.log:Elapsed: 00:00:23.51

new1.log:Elapsed: 00:00:01.46

new1.log:Elapsed: 00:00:01.29

new1.log:Elapsed: 00:00:01.29

new1.log:Elapsed: 00:00:01.27

new1.log:Elapsed: 00:00:01.28

new1.log:Elapsed: 00:00:01.26

new1.log:Elapsed: 00:00:01.28

new2.log:Elapsed: 00:00:42.79

new2.log:Elapsed: 00:00:23.51

new2.log:Elapsed: 00:00:01.29

new2.log:Elapsed: 00:00:01.28

new2.log:Elapsed: 00:00:01.29

new2.log:Elapsed: 00:00:01.27

new2.log:Elapsed: 00:00:01.27

new2.log:Elapsed: 00:00:01.27

new2.log:Elapsed: 00:00:01.28

采用hash join+全表扫描

new1.log:Elapsed: 00:00:14.95

new1.log:Elapsed: 00:00:13.70

new1.log:Elapsed: 00:00:12.81

new1.log:Elapsed: 00:00:13.89

new1.log:Elapsed: 00:00:14.56

new1.log:Elapsed: 00:00:11.61

new1.log:Elapsed: 00:00:11.45

new1.log:Elapsed: 00:00:10.84

new1.log:Elapsed: 00:00:11.31

new2.log:Elapsed: 00:00:12.13

new2.log:Elapsed: 00:00:11.85

new2.log:Elapsed: 00:00:14.44

new2.log:Elapsed: 00:00:15.60

new2.log:Elapsed: 00:00:12.55

new2.log:Elapsed: 00:00:11.09

new2.log:Elapsed: 00:00:11.11

new2.log:Elapsed: 00:00:11.04

new2.log:Elapsed: 00:00:11.14