执行计划的偏差导致的性能问题

简介: 在生产环境中有一条sql语句,查看执行计划来看,效果还是可以接受的。 sql语句类似下面的样子,可以看到里面还使用了比较纠结的外连接。从执行计划来说,默认是走nested loop join,数据的查取中会走索引,从oracle的分析来说这样的效果要好一些。
在生产环境中有一条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秒左右。这样看来实际效果比期望的要好。
---------------------------------------------------------------------------------------------------------------------------------
| Id  | Operation                              | Name                   | Rows  | Bytes | Cost (%CPU)| Time     | Pstart| Pstop |
---------------------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                       |                        |  2152 |   138K|  8254   (1)| 00:01:40 |       |       |
|   1 |  NESTED LOOPS                          |                        |       |       |            |          |       |       |
|   2 |   NESTED LOOPS                         |                        |  2152 |   138K|  8254   (1)| 00:01:40 |       |       |
|*  3 |    FILTER                              |                        |       |       |            |          |       |       |
|   4 |     NESTED LOOPS OUTER                 |                        |  2152 |   119K|  8039   (1)| 00:01:37 |       |       |
|   5 |      PARTITION RANGE SINGLE            |                        |  4304 |   109K|  6317   (1)| 00:01:16 |   KEY |   KEY |
|   6 |       TABLE ACCESS BY LOCAL INDEX ROWID| CYCLE_CUSTOMERS        |  4304 |   109K|  6317   (1)| 00:01:16 |   KEY |   KEY |
|*  7 |        INDEX FULL SCAN                 | CYCLE_CUSTOMERS_PK     |    24 |       |  5944   (1)| 00:01:12 |   KEY |   KEY |
|   8 |      PARTITION RANGE SINGLE            |                        |     1 |    31 |     1   (0)| 00:00:01 |   KEY |   KEY |
|*  9 |       TABLE ACCESS BY LOCAL INDEX ROWID| CYC_PAYER_POP          |     1 |    31 |     1   (0)| 00:00:01 |   KEY |   KEY |
|* 10 |        INDEX RANGE SCAN                | CYC_PAYER_POP_1IX      |     3 |       |     1   (0)| 00:00:01 |   KEY |   KEY |
|* 11 |    INDEX UNIQUE SCAN                   | CUSTOMER_PK            |     1 |       |     1   (0)| 00:00:01 |       |       |
|  12 |   TABLE ACCESS BY INDEX ROWID          | CUSTOMER               |     1 |     9 |     1   (0)| 00:00:01 |       |       |
---------------------------------------------------------------------------------------------------------------------------------

但是在生产环境中执行的时候,是通过一个程序来触发的,会并行的执行多个这样的查询,结果性能是越来越糟糕,最后本来执行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的并发查询的情况下,速度还是很稳定。

Plan hash value: 4044834379


-------------------------------------------------------------------------------------------------------------------------------------------------------
| Id  | Operation                          | Name                | Rows  | Bytes | Cost (%CPU)| Time     | Pstart| Pstop |    TQ  |IN-OUT| PQ Distrib |
-------------------------------------------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                   |                     |   567 | 37422 | 45236   (1)| 00:09:03 |       |       |        |      |            |
|   1 |  PX COORDINATOR                    |                     |       |       |            |          |       |       |        |      |            |
|   2 |   PX SEND QC (ORDER)               | :TQ10003            |   567 | 37422 | 45236   (1)| 00:09:03 |       |       |  Q1,03 | P->S | QC (ORDER) |
|   3 |    SORT ORDER BY                   |                     |   567 | 37422 | 45236   (1)| 00:09:03 |       |       |  Q1,03 | PCWP |            |
|   4 |     PX RECEIVE                     |                     |       |       |            |          |       |       |  Q1,03 | PCWP |            |
|   5 |      PX SEND RANGE                 | :TQ10002            |       |       |            |          |       |       |  Q1,02 | P->P | RANGE      |
|   6 |       NESTED LOOPS                 |                     |       |       |            |          |       |       |  Q1,02 | PCWP |            |
|   7 |        NESTED LOOPS                |                     |   567 | 37422 | 45235   (1)| 00:09:03 |       |       |  Q1,02 | PCWP |            |
|*  8 |         FILTER                     |                     |       |       |            |          |       |       |  Q1,02 | PCWC |            |
|*  9 |          HASH JOIN OUTER           |                     |   567 | 32319 | 45219   (1)| 00:09:03 |       |       |  Q1,02 | PCWP |            |
|  10 |           PX RECEIVE               |                     |  1134 | 29484 |  8563   (1)| 00:01:43 |       |       |  Q1,02 | PCWP |            |
|  11 |            PX SEND HASH            | :TQ10001            |  1134 | 29484 |  8563   (1)| 00:01:43 |       |       |  Q1,01 | P->P | HASH       |
|  12 |             PX BLOCK ITERATOR      |                     |  1134 | 29484 |  8563   (1)| 00:01:43 |   171 |   171 |  Q1,01 | PCWC |            |
|* 13 |              TABLE ACCESS FULL     | CYCLE_CUSTOMERS     |  1134 | 29484 |  8563   (1)| 00:01:43 |   171 |   171 |  Q1,01 | PCWP |            |
|  14 |           BUFFER SORT              |                     |       |       |            |          |       |       |  Q1,02 | PCWC |            |
|  15 |            PX RECEIVE              |                     |  1355 | 42005 | 36656   (1)| 00:07:20 |       |       |  Q1,02 | PCWP |            |
|  16 |             PX SEND HASH           | :TQ10000            |  1355 | 42005 | 36656   (1)| 00:07:20 |       |       |        | S->P | HASH       |
|  17 |              PARTITION RANGE SINGLE|                     |  1355 | 42005 | 36656   (1)| 00:07:20 |   171 |   171 |        |      |            |
|* 18 |               TABLE ACCESS FULL    | CYC_PAYER_POP       |  1355 | 42005 | 36656   (1)| 00:07:20 |   171 |   171 |        |      |            |
|* 19 |         INDEX UNIQUE SCAN          | CUSTOMER_PK         |     1 |       |     1   (0)| 00:00:01 |       |       |  Q1,02 | PCWP |            |
|  20 |        TABLE ACCESS BY INDEX ROWID | CUSTOMER            |     1 |     9 |     1   (0)| 00:00:01 |       |       |  Q1,02 | PCWP |            |
-------------------------------------------------------------------------------------------------------------------------------------------------------


所以执行计划只能作为大家调优的一个参考而已,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








目录
相关文章
|
1月前
|
SQL 关系型数据库 分布式数据库
在PolarDB中,行数评估是通过对表的统计数据、基数估计以及算子代价模型来进行估算的。
【2月更文挑战第14天】在PolarDB中,行数评估是通过对表的统计数据、基数估计以及算子代价模型来进行估算的。
82 1
|
9月前
|
缓存 负载均衡 固态存储
MySQL优化心得:提升性能的关键步骤
MySQL作为最常用的关系型数据库管理系统之一,优化性能是每个开发者和管理员必须掌握的技能。
117 0
|
SQL 存储 算法
PostgreSQL 执行计划,成本公式解说,代价因子校准,自动跟踪SQL执行计划(二)|学习笔记
快速学习PostgreSQL 执行计划,成本公式解说,代价因子校准,自动跟踪SQL执行计划(二)
285 0
PostgreSQL 执行计划,成本公式解说,代价因子校准,自动跟踪SQL执行计划(二)|学习笔记
|
SQL 关系型数据库 MySQL
Mysql索引降维 优化查询 提高效率
数据的选择度越大,则维度越大。 降维,按我个人的理解是:在大量的数据中,一层一层地筛选过滤,维度也会逐渐减低。 点线面中,共有黑红两种颜色。 目标:筛选出所有红色的点 步骤:选出所有带有红色点的面 –> 选出所有带有红色点的线 –> 在线上选出所有红色点
89 0
|
SQL 移动开发 关系型数据库
PostgreSQL 执行计划,成本公式解说,代价因子校准,自动跟踪SQL执行计划(三)|学习笔记
快速学习PostgreSQL 执行计划,成本公式解说,代价因子校准,自动跟踪SQL执行计划(三)
544 0
PostgreSQL 执行计划,成本公式解说,代价因子校准,自动跟踪SQL执行计划(三)|学习笔记
|
SQL XML JSON
PostgreSQL 执行计划,成本公式解说,代价因子校准,自动跟踪SQL执行计划(一)|学习笔记
快速学习PostgreSQL 执行计划,成本公式解说,代价因子校准,自动跟踪SQL执行计划(一)
482 0
PostgreSQL 执行计划,成本公式解说,代价因子校准,自动跟踪SQL执行计划(一)|学习笔记
|
存储 缓存 分布式计算
性能估算-汇总【转】
假期重新把之前在新浪博客里面的文字梳理了下,搬到这里。
530 0
|
SQL Oracle 关系型数据库
SQL为什么预估执行计划与真实执行计划会有差异?
SQL为什么预估执行计划与真实执行计划会有差异?http://www.bieryun.com/3149.html 一 问题概要 对同一个 SQL 语句的 ExplainPlan 里显示的预估执行计划与通过 V$SQL_PLAN 视图获取的 Runtime Plan 真实执行计划,偶尔会发现两边有不一致的情况,为什么呢?为什么预估执行计划会不准确?怎样才能避免这种情况的发生? 二 问题解答 这是执行计划相关中会被经常问道的问题,也是困扰自己很长时间的问题。
1443 0