SQL瓶颈分析,以及适应最佳执行计划的探讨

年纪大了,慢慢的写技术文档也不皮了。一五一十的说说清楚, 问题是什么, 解决方案又是什么。 中规中矩的写。

 

SQL_ID:  1m6akd1s2144u, 执行成本: 40万次/h  3000逻辑读/次。

TOP10SQL的问题类似, 现在分析优化d4wcdsskp0f47

d4wcdsskp0f47  SQL和这个SQL类似。

SELECT * FROM (SELECT XX.*, ROWNUM AS RN FROM (select count(*) from CUST_YC_APP.PARTY_CERT P inner join CUST_YC_APP.CUSTOMER C on P.PARTY_ID = C.PARTY_ID and C.STATUS_CD = '1100' where P.PARTY_ID in (:1 ) and P.STATUS_CD in (:2 ) and P.IS_DEFAULT = '1') XX WHERE ROWNUM <= 1000 ) XXX WHERE RN > 0

执行计划:

执行计划中有笛卡尔积, 但是问题并不在笛卡尔积中。(好多DBA看到笛卡尔集就说笛卡尔集有性能问题,其实是不严谨的,的确很多笛卡尔集是有性能问题,但是此处不是最主要的问题)

查询中绑定变量的值如:B1:15151722241919  B2: ‘1000’  

带入到SQL中查询 只9逻辑读。

查询性能瓶颈:

通过采样分析,更多的性能消耗在执行计划的第9步。也就在C表(CUSTOMER)的回表上。

SQL中得出C表用到两个字段 C.PARTY_ID,C.STATUS_CD。 PARTY_ID上建有索引,回表就是为了访问STATUS_CD字段。

因此建议建立索引index  C ( PARTY_ID, STATUS_CD ); 这样可以避免回表。

针对该SQL 优化建议建立索引,即可。

猜测根本原因

是否是部分由于C表  PARTY_ID某些特定值过多,造成大量会表。

查询表中的数据分布以及绑定变量中的值。

查询数据分布:C表 PARTY_ID字段的选择性 98%,但是有个值,重复率非常高。

查询数据分布以及关联绑定变量。

(这个SQL只表示这些值在绑定变量中出现过, 但是出现的频率未知。。。。但能说明问题)

这个SQL表明,部分不均匀的

果然当PARTY_ID = 15151723602037, 回表需要 回10万次。

用auto trace 中单次执行需要消耗逻辑读7770。 针对该问题已经有相关建议。

 

下文是探讨 针对该表数据分布以及SQL 如何进一步优化。

 

既然数据分布不均衡,是否可以通过收集直方图来改善性能? 答案是否定的。

 

建立测试表: CUSTOMER_test

 

  create table  CUSTOMER_test tablespace TBS_CUST_DATA as

  select * from   CUST_YC_APP.CUSTOMER C ;

  create index  idx_pid on CUSTOMER_test(PARTY_ID);

 

收集直方图:

 begin

  DBMS_STATS.GATHER_TABLE_STATS(OWNNAME          => 'CHECKDB',

     TABNAME          => 'CUSTOMER_TEST',

      ESTIMATE_PERCENT => 50,

      METHOD_OPT       => 'for columns PARTY_ID size skewonly',

       DEGREE           => 2,

       GRANULARITY      => 'ALL',

       CASCADE          => TRUE);

END;

执行SQL:SQL效率更差。 15万逻辑读/次

回到SQl中。关键部分:

需要确认是否可以改成半连接, ( 业务那边是要确认是否可以等价改写,此处讨论这种数据分布情况下如何实施优化 )

因为针对数据分布不均衡半连接效果比较好。

改写SQL:

 SELECT *

   FROM (SELECT XX.*, ROWNUM AS RN

           FROM (select count(1)

                   from CUST_YC_APP.PARTY_CERT P

                   where P.PARTY_ID in (15151723602037)

                   and  P.PARTY_ID in  ( select /*+ nl_sj index(c) */   C.PARTY_ID from  checkdb.CUSTOMER_test  C where C.STATUS_CD = '1100' )

                    and P.STATUS_CD in ('1000')

                    and P.IS_DEFAULT = '1') XX

          WHERE ROWNUM <= 1000) XXX

  WHERE RN > 0;

9 逻辑读/次. (不添加hints 就会走全表,成本1286次/S )

不添加hints 就会走全表,成本1286次/S

SQL无法自动走用好的执行计划, 需要绑定hints才走。 如何自动走好的执行计划??

答案  

1 删除直方图。

2设置数据选择性, 综合比较选择性设置成 30% 比较好。

(选择性设置成30, 只要数据总量 * 30% 即可)

删除直方图, 设置选择性。

  begin

dbms_stats.delete_column_stats(OWNNAME          => 'CHECKDB',

                                TABNAME          => 'CUSTOMER_TEST',

                                colname =>'PARTY_ID',

                                col_stat_type=>'HISTOGRAM' );

   end;

 begin

  DBMS_STATS.set_column_stats(OWNNAME          => 'CHECKDB',

                                TABNAME          => 'CUSTOMER_TEST',

                                colname =>'PARTY_ID',distcnt => 1645919);

end;

这次测试不添加hints的情况下, 是否能走最好的执行计划。

 果然走nested_loop 半连接  并且走 C(PARTY_ID) 索引 (图就不贴了)。9逻辑读

另外也测试了SQL

  select count(1)

  from CHECKDB.CUSTOMER_TEST t

 where PARTY_ID = 15151723602037 and t.status_cd  in ('1100');

收集直方图: 走全表扫描  156935逻辑读/次

删除直方图: 走索引扫描  7903逻辑读/次

至于什么情况下收集直方图,删除直方图,收集统计信息。 这个不多说了。   看我之前博客设置统计信息优化SQL的案例。

 

 

 

 

 

猜你喜欢

转载自blog.csdn.net/daiqiulong2/article/details/86546446