hint不当索引,影响多表链接方式,最终致使SQL执行缓慢

需求:一个SQL执行特别慢,没法返回结果,须要进行优化,最终返回结果便可。html

 

1、SQL分析java

 

2、尝试执行,观测执行计划sql

 

3、修改SQL服务器

 

4、问题总结session

 

1、SQL分析ide

1)SQL文本,执行时间,执行用户 用户brjljk sql执行时间,2935分钟 sql_text select c.hphm, c.ccdjrq, c.clpp1, c.clxh, c.zt, c.syr, c.wfsj, c.wfxw, c.dsr, c.xxly, c.syq, c.wfsj1, d.wfnr, e.dlmc, c.xxly1, c.dsr1 from (select /*+ index(b idx_violation_wfsj)*/ a.hphm, a.ccdjrq, a.clpp1, a.clxh, a.zt, a.syr, a.wfsj, a.wfxw, a.dsr, a.xxly, a.syq, b.wfsj wfsj1, b.wfxw wfxw1, b.wfdd wfdd1, b.xxly xxly1, b.dsr dsr1 from A a right join B b on a.hphm = b.hphm where a.wfsj <> b.wfsj and (b.wfsj < add_months(a.wfsj, 12) and b.wfsj > add_months(a.wfsj, -12)) and a.wfsj > to_date('2018-08-01', 'yyyy-mm-dd') and a.wfsj < to_date('2018-09-01', 'yyyy-mm-dd') order by a.hphm, a.wfsj, b.wfsj) c, D d, E e where c.wfxw1 = d.wfxw and c.wfdd1 = e.dldm

2)查询会话等待事件

SQL> select sid,serial#,event,sql_id,status,(sysdate-logon_time)*86400 as "s",oop

last_call_et,username,inst_id,MACHINE from gv$session where status='ACTIVE' and username is not null;测试

SID SERIAL# EVENT SQL_ID STATUS s LAST_CALL_ET USERNAME INST_ID MACHINE
---------- ---------- ------------------------------ ------------- -------- ---------- -------
1776 28345 db file sequential read 2vcdzpaknk46s ACTIVE 180100 176352 xxx 1 xx优化

 

3)查询sql文本spa

SQL> select sql_text from v$sqlarea where sql_id ='&a';
SQL> select sql_text from v$sqltext where sql_id ='2vcdzpaknk46s';
SQL> select sql_text from v$sqlstats where sql_id ='2vcdzpaknk46s';

4)查询执行计划

select * from table(dbms_xplan.display_cursor('&sql',null,'PEEKED_BINDS'));

SQL> select * from table(dbms_xplan.display_awr('2vcdzpaknk46s',null));

------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | | | 129M(100)| |
| 1 | SORT ORDER BY | | 309 | 56238 | 129M (1)|432:58:14 |
| 2 | HASH JOIN | | 309 | 56238 | 129M (1)|432:58:14 |
| 3 | HASH JOIN | | 309 | 50985 | 129M (1)|432:58:13 |
| 4 | HASH JOIN | | 309 | 37389 | 129M (1)|432:58:13 |
| 5 | TABLE ACCESS FULL | SJS20181022 | 10455 | 847K| 891 (2)| 00:00:11 |
| 6 | TABLE ACCESS BY INDEX ROWID| VIO_VIOLATION | 144M| 5226M| 129M (1)|432:57:51 |
| 7 | INDEX FULL SCAN | IDX_VIOLATION_WFSJ | 144M| | 583K (1)| 01:56:47 |
| 8 | TABLE ACCESS FULL | VIO_CODEWFDM | 1069 | 47036 | 13 (0)| 00:00:01 |
| 9 | TABLE ACCESS FULL | FRM_ROADITEM | 5212 | 88604 | 22 (0)| 00:00:01 |
------------------------------------------------------------------------------------------------------

5)查询SQL等待事件

select count(*),event,count(distinct session_id) from gv$active_session_history
where sql_id='2vcdzpaknk46s' group by event;
COUNT(*) EVENT COUNT(DISTINCTSESSION_ID)
---------- ------------------------------ -------------------------
27652 db file sequential read 1
22 gc cr block 2-way 1
258 gc cr disk read 1
293 1

 

6)查询执行计划最慢的步骤(failed)

select count(*),sql_plan_line_id
from gv$active_session_history
where sql_id='2vcdzpaknk46s'
group by sql_plan_line_id
order by 2;

--生产环境10.2.0.5,11g才有的字段

7)表碎片

表碎片会致使全表扫描更消耗资源,本次慢不是因为全表扫描的问题

 

8)数据量

经过dba_tables,num_rows,dba_segments,bytes查询获得信息以下

a表 hash join 驱动表,30万条记录

B表 hash join 被驱动表,1亿条记录,表100G大小

 

 

 

2、尝试执行,观测执行计划

 

1)确认优化重点四个表中,从执行计划看,重点为
| 6 | TABLE ACCESS BY INDEX ROWID| VIO_VIOLATION | 144M| 5226M| 129M (1)|432:57:51 |
| 7 | INDEX FULL SCAN | IDX_VIOLATION_WFSJ | 144M| | 583K (1)| 01:56:47 |
该SQL是a +b 的集合,转换为c最后与其它表进行关联查询

2)对a+b表的查询进行优化及测试

思路A,是否因为时间取值范围致使的问题
 and (b.wfsj < add_months(a.wfsj, 12) and
               b.wfsj > add_months(a.wfsj, -12))
           and a.wfsj > to_date('2018-08-01', 'yyyy-mm-dd')
           and a.wfsj < to_date('2018-09-01', 'yyyy-mm-dd')

explain plan for select a.hphm,a.wfsj,b.wfsj from A a right join B

b on a.hphm=b.hphm where b.wfsj > to_date('2017-01-01','yyyy-mm-dd')

and b.wfsj <to_date('2019-01-01','yyyy-mm-dd') and a.wfsj>to_date('2018-08-01','yyyy-mm-dd')

and a.wfsj<to_date('2018-09-01','yyyy-mm-dd') order by a.hphm,a.wfsj, b.wfsj;

1* select * from table(dbms_xplan.display())

PLAN_TABLE_OUTPUT
----------------------------------------------------------------------
Plan hash value: 1015943026

-----------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-----------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 18958 | 629K| 115K (1)| 00:23:12 |
| 1 | SORT ORDER BY | | 18958 | 629K| 115K (1)| 00:23:12 |
|* 2 | TABLE ACCESS BY INDEX ROWID| VIO_VIOLATION | 2 | 34 | 13 (0)| 00:00:01 |
| 3 | NESTED LOOPS | | 18958 | 629K| 115K (1)| 00:23:12 |
|* 4 | INDEX FAST FULL SCAN | SJS20181022_IND_HPHM | 10455 | 173K| 277 (2)| 00:00:04 |
|* 5 | INDEX RANGE SCAN | IDX_VIOLATION_HPHM | 12 | | 3 (0)| 00:00:01 |
-----------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

2 - filter("B"."WFSJ">TO_DATE(' 2017-01-01 00:00:00', 'syyyy-mm-dd hh24:mi:ss') AND
"B"."WFSJ"<TO_DATE(' 2019-01-01 00:00:00', 'syyyy-mm-dd hh24:mi:ss'))
4 - filter("A"."WFSJ">TO_DATE(' 2018-08-01 00:00:00', 'syyyy-mm-dd hh24:mi:ss') AND
"A"."WFSJ"<TO_DATE(' 2018-09-01 00:00:00', 'syyyy-mm-dd hh24:mi:ss'))
5 - access("A"."HPHM"="B"."HPHM")

 

 

select * from table(dbms_xplan.display_cursor('004nmwuabm1qr',null,'PEEKED_BINDS'))

PLAN_TABLE_OUTPUT
-----------------------------------------------------------------------------------------------
SQL_ID 004nmwuabm1qr, child number 0
-------------------------------------
select a.hphm, a.ccdjrq, a.clpp1, a.clxh, a.zt,
a.syr, a.wfsj, a.wfxw, a.dsr, a.xxly,
a.syq, b.wfsj wfsj1, b.wfxw wfxw1, b.wfdd wfdd1,
b.xxly xxly1, b.dsr dsr1 from A a right join
B b on a.hphm=b.hphm where (b.wfsj < add_months(a.wfsj, 12) and
b.wfsj >add_months(a.wfsj, -12)) and a.wfsj>to_date('2018-08-01','yyyy-mm-dd') and
a.wfsj<to_date('2018-09-01','yyyy-mm-dd') order by a.hphm,a.wfsj, b.wfsj

Plan hash value: 3321285990

PLAN_TABLE_OUTPUT
-------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | | | 136K(100)| |
| 1 | SORT ORDER BY | | 309 | 37389 | 136K (1)| 00:27:23 |
|* 2 | TABLE ACCESS BY INDEX ROWID| VIO_VIOLATION | 1 | 38 | 13 (0)| 00:00:01 |
| 3 | NESTED LOOPS | | 309 | 37389 | 136K (1)| 00:27:23 |
|* 4 | TABLE ACCESS FULL | SJS20181022 | 10455 | 847K| 891 (2)| 00:00:11 |
|* 5 | INDEX RANGE SCAN | IDX_VIOLATION_HPHM | 12 | | 3 (0)| 00:00:01 |
---------------------------------------------------------------------------------
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------

2 - filter(("B"."WFSJ"<ADD_MONTHS(INTERNAL_FUNCTION("A"."WFSJ"),12) AND
"B"."WFSJ">ADD_MONTHS(INTERNAL_FUNCTION("A"."WFSJ"),-12)))
4 - filter(("A"."WFSJ">TO_DATE(' 2018-08-01 00:00:00', 'syyyy-mm-dd hh24:mi:ss')
 AND
"A"."WFSJ"<TO_DATE(' 2018-09-01 00:00:00', 'syyyy-mm-dd hh24:mi:ss')))
5 - access("A"."HPHM"="B"."HPHM")


32 rows selected.

 

结论A的考虑是错误的,add_months并不会致使执行计划消耗更多的资源

 

思路B:多表链接的问题? 让sql从hash join 转换为nest loop试试,本次sql 取消hint便可,
为了避免增长服务器负担,
使用explain plan for 方式

SQL> explain plan for select a.hphm,a.wfsj,b.wfsj from A a
 right join B b on a.hphm=b.hphm 
where (b.wfsj < add_months(a.wfsj, 12) and b.wfsj >add_months(a.wfsj, -12))
 and a.wfsj>to_date('2018-08-01','yyyy-mm-dd')
 and a.wfsj<to_date('2018-09-01','yyyy-mm-dd') order by a.hphm,a.wfsj, b.wfsj;

Explained.

SQL> select * from table(dbms_xplan.display());

PLAN_TABLE_OUTPUT
---------------------------------------------------------------------------
Plan hash value: 1015943026
---------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 309 | 10506 | 115K (1)| 00:23:12 |
| 1 | SORT ORDER BY | | 309 | 10506 | 115K (1)| 00:23:12 |
|* 2 | TABLE ACCESS BY INDEX ROWID| VIO_VIOLATION | 1 | 17 | 13 (0)| 00:00:01 |
| 3 | NESTED LOOPS | | 309 | 10506 | 115K (1)| 00:23:12 |
|* 4 | INDEX FAST FULL SCAN | SJS20181022_IND_HPHM | 10455 | 173K| 277 (2)| 00:00:04 |
|* 5 | INDEX RANGE SCAN | IDX_VIOLATION_HPHM | 12 | | 3 (0)| 00:00:01 |
---------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

2 - filter("B"."WFSJ"<ADD_MONTHS(INTERNAL_FUNCTION("A"."WFSJ"),12) AND
"B"."WFSJ">ADD_MONTHS(INTERNAL_FUNCTION("A"."WFSJ"),-12))
4 - filter("A"."WFSJ">TO_DATE(' 2018-08-01 00:00:00', 'syyyy-mm-dd hh24:mi:ss') AND
"A"."WFSJ"<TO_DATE(' 2018-09-01 00:00:00', 'syyyy-mm-dd hh24:mi:ss'))
5 - access("A"."HPHM"="B"."HPHM")

21 rows selected.

 

发现nest loop 方式挺快的,继续测试(上述测试只测试3个字段)

使用原SQLa+b的两个表SQL不作变更,执行测试,发现执行计划未改变;

使用须要优化的SQL文本,删除hint,进行explain plan for 进行测试,执行计划未改变

  

 

  

  

 

3、修改SQL

删除Hint,让SQL走nest loop 方式,10s内返回结果
SQL> select c.hphm,
        c.ccdjrq,
        c.clpp1,
        c.clxh,
        c.zt,
        c.syr,
        c.wfsj,
        c.wfxw,
        c.dsr,
        c.xxly,
        c.syq,
        c.wfsj1,
        d.wfnr,
        e.dlmc,
        c.xxly1,
        c.dsr1
   from (select 
          a.hphm,
          a.ccdjrq,
          a.clpp1,
          a.clxh,
          a.zt,
          a.syr,
          a.wfsj,
          a.wfxw,
          a.dsr,
          a.xxly,
          a.syq,
          b.wfsj   wfsj1,
          b.wfxw   wfxw1,
          b.wfdd   wfdd1,
          b.xxly   xxly1,
          b.dsr    dsr1
           from A a
          right join B b
             on a.hphm = b.hphm
          where a.wfsj <> b.wfsj
            and (b.wfsj < add_months(a.wfsj, 12) and
                b.wfsj > add_months(a.wfsj, -12))
            and a.wfsj > to_date('2018-08-01', 'yyyy-mm-dd')
            and a.wfsj < to_date('2018-09-01', 'yyyy-mm-dd')
          order by a.hphm, a.wfsj, b.wfsj) c,
        D d,
        E e
  where c.wfxw1 = d.wfxw
    and c.wfdd1 = e.dldm;
52519 rows selected.
Elapsed: 00:00:05.08

Execution Plan
----------------------------------------------------------
Plan hash value: 2181500870

-----------------------------------------------------------------------------------------------------
| Id  | Operation                      | Name               | Rows  | Bytes | Cost (%CPU)| Time     |
-----------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT               |                    |   309 | 56238 |   136K  (1)| 00:27:24 |
|   1 |  SORT ORDER BY                 |                    |   309 | 56238 |   136K  (1)| 00:27:24 |
|*  2 |   HASH JOIN                    |                    |   309 | 56238 |   136K  (1)| 00:27:24 |
|*  3 |    HASH JOIN                   |                    |   309 | 50985 |   136K  (1)| 00:27:23 |
|*  4 |     TABLE ACCESS BY INDEX ROWID| VIO_VIOLATION      |     1 |    38 |    13   (0)| 00:00:01 |
|   5 |      NESTED LOOPS              |                    |   309 | 37389 |   136K  (1)| 00:27:23 |
|*  6 |       TABLE ACCESS FULL        | SJS20181022        | 10455 |   847K|   891   (2)| 00:00:11 |
|*  7 |       INDEX RANGE SCAN         | IDX_VIOLATION_HPHM |    12 |       |     3   (0)| 00:00:01 |
|   8 |     TABLE ACCESS FULL          | VIO_CODEWFDM       |  1069 | 47036 |    13   (0)| 00:00:01 |
|   9 |    TABLE ACCESS FULL           | FRM_ROADITEM       |  5212 | 88604 |    22   (0)| 00:00:01 |
-----------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   2 - access("B"."WFDD"="E"."DLDM")
   3 - access("B"."WFXW"="D"."WFXW")
   4 - filter("A"."WFSJ"<>"B"."WFSJ" AND "B"."WFSJ"<ADD_MONTHS(INTERNAL_FUNCTION("A"."WFSJ"),
              12) AND "B"."WFSJ">ADD_MONTHS(INTERNAL_FUNCTION("A"."WFSJ"),-12))
   6 - filter("A"."WFSJ">TO_DATE(' 2018-08-01 00:00:00', 'syyyy-mm-dd hh24:mi:ss') AND
              "A"."WFSJ"<TO_DATE(' 2018-09-01 00:00:00', 'syyyy-mm-dd hh24:mi:ss'))
   7 - access("A"."HPHM"="B"."HPHM")

Statistics
----------------------------------------------------------
          1  recursive calls
          0  db block gets
     211168  consistent gets
        108  physical reads
        116  redo size
    3555229  bytes sent via SQL*Net to client
      39003  bytes received via SQL*Net from client
       3503  SQL*Net roundtrips to/from client
          1  sorts (memory)
          0  sorts (disk)
      52519  rows processed

  

 

  

4、问题总结

 

1)使用Nest loop方式,被驱动表及时循环查询30万次,比想象中的快不少不少
2)本次sql未优化前走hash join方式的缘由是,hint 索引是时间列,
而Nest loop方式须要驱动表的查询结果输出身份证,被驱动表拿着身份证,
去被驱动表中索取记录;驱动表在本次执行计划无变化,被驱动表从时间字段索引,转换走
IDX_VIOLATION_HPHM, 也就是说,因为索引的选择度的问题,Oracle认为 hash join的链接方式
优于  date索引(hint)找到对应的rowid,而后找到hphm字段值
3)从此,在使用hint前,经过测试,选择合适的hint

  

  

  

 

 

  

原文出处:https://www.cnblogs.com/lvcha001/p/10229827.html

相关文章
相关标签/搜索