当前位置: 代码迷 >> SQL >> SQL优化一则:CBO查询SQL1次比一次慢
  详细解决方案

SQL优化一则:CBO查询SQL1次比一次慢

热度:40   发布时间:2016-05-05 12:43:02.0
SQL优化一则:CBO查询SQL一次比一次慢
问题:
今天在开发的新上线一个功能后,下面SQL消耗了大量的CPU,数据库处一度处于满负荷进行,使用率达到100%.赶紧kill掉了正进行的SQL,以免对其它业务产生影响。
通过测试,发现一个很奇怪的问题,SQL在第一次执行时会很快,在连续执行几次后,一次比一次的慢。
甚至到最后一直挂起在那,产生等待。当停掉后,过会(10分钟左右)再执行,第一次又会变快,如果再连接执行几次,又会出现同样的慢直至挂起问题。


下面是测试结果:
--第一次执行会很快,逻辑读是8000多
--第二次执行会变慢,逻辑读是44513,如下:
SQL> Select * 
  2     From (Select Task.Order_Id As Od,
  3                                                      Task.Task_Type As Tp,
  4                                                      Task.Task_Status As Ts,
  5                                                      To_Char(Task.Create_Time, 'yyyy-mm-dd hh24:mi:ss') As Ct,
  6                                                      Task.Create_User As Cu,
  7                                                      To_Char(Task.Handle_Time, 'yyyy-mm-dd hh24:mi:ss') As Ht,
  8                                                      Task.Task_User As Sku,
  9                                                      Task.Id As Ind,
 10                                                      o.Channel_Id
 11                                     From Tk_Taskinfo_Dt Task, Tk_Orders_Dt o
 12                              Where 1 = 1
 13                                      And Task.Order_Id = o.Odrid
 14                                      And Task_Status <> '0002900001'
 15                                      And Task.Task_User = 'tianyh'
 16                                      And Task.Accept_Time >=
 17                                                      To_Date('2012-12-01 00:00:00', 'yyyy-mm-dd hh24:mi:ss')
 18                                      And ((o.Channel_Id = 'QD00002' And
 19                                                      Task.Task_Type In
 20                                                      ('0002000013', '0002000017', '0002000038', '0002000008',
 21                                                             '0002000031', '0002000004', '0002000012', '0002000016',
 22                                                             '0002000032', '0002000035', '0002000024', '0002000010',
 23                                                             '0002000014', '0002000019', '0002000006', '0002000034',
 24                                                             '0002000041')) Or
 25                                                      (o.Channel_Id = 'QD00003' And
 26                                                      Task.Task_Type In
 27                                                      ('0002000041', '0002000004', '0002000010', '0002000012',
 28                                                             '0002000034', '0002000006', '0002000019', '0002000008',
 29                                                             '0002000031', '0002000014', '0002000017', '0002000016',
 30                                                             '0002000024', '0002000013', '0002000035', '0002000038',
 31                                                             '0002000032')) Or
 32                                                      (o.Channel_Id = 'QD00001' And
 33                                                      Task.Task_Type In
 34                                                      ('0002000041', '0002000016', '0002000019', '0002000017',
 35                                                             '0002000035', '0002000008', '0002000004', '0002000034',
 36                                                             '0002000031', '0002000006', '0002000013', '0002000024',
 37                                                             '0002000032', '0002000014', '0002000038', '0002000010',
 38                                                             '0002000012')))
 39                             Union All
 40                             Select Task.Order_Id As Od,
 41                                                      Task.Task_Type As Tp,
 42                                                      Task.Task_Status As Ts,
 43                                                      To_Char(Task.Create_Time, 'yyyy-mm-dd hh24:mi:ss') As Ct,
 44                                                      Task.Create_User As Cu,
 45                                                      To_Char(Task.Handle_Time, 'yyyy-mm-dd hh24:mi:ss') As Ht,
 46                                                      Task.Task_User As Sku,
 47                                                      Task.Id As Ind,
 48                                                      o.Channel_Id
 49                                     From Tk_Taskinfo_Finish_Dt Task, Tk_Orders_Dt o
 50                              Where 1 = 1
 51                                      And Task.Order_Id = o.Odrid
 52                                      And Task.Task_User = 'tianyh'
 53                                      And Task.Accept_Time >=
 54                                                      To_Date('2012-12-01 00:00:00', 'yyyy-mm-dd hh24:mi:ss')
 55                                      And ((o.Channel_Id = 'QD00002' And
 56                                                      Task.Task_Type In
 57                                                      ('0002000013', '0002000017', '0002000038', '0002000008',
 58                                                             '0002000031', '0002000004', '0002000012', '0002000016',
 59                                                             '0002000032', '0002000035', '0002000024', '0002000010',
 60                                                             '0002000014', '0002000019', '0002000006', '0002000034',
 61                                                             '0002000041')) Or
 62                                                      (o.Channel_Id = 'QD00003' And
 63                                                      Task.Task_Type In
 64                                                      ('0002000041', '0002000004', '0002000010', '0002000012',
 65                                                             '0002000034', '0002000006', '0002000019', '0002000008',
 66                                                             '0002000031', '0002000014', '0002000017', '0002000016',
 67                                                             '0002000024', '0002000013', '0002000035', '0002000038',
 68                                                             '0002000032')) Or
 69                                                      (o.Channel_Id = 'QD00001' And
 70                                                      Task.Task_Type In
 71                                                      ('0002000041', '0002000016', '0002000019', '0002000017',
 72                                                             '0002000035', '0002000008', '0002000004', '0002000034',
 73                                                             '0002000031', '0002000006', '0002000013', '0002000024',
 74                                                             '0002000032', '0002000014', '0002000038', '0002000010',
 75                                                             '0002000012')))
 76                              Order By Ct Desc)
 77   Where Rownum <= 25;


已选择25行。






执行计划
----------------------
Plan hash value: 523130039


-----------------------------------------------------------
| Id  | Operation                                 | Name                  | Rows  | Bytes | Cost (%CPU)| Time     | Pstart| Pstop |
-----------------------------------------------------------
|   0 | SELECT STATEMENT                          |                       |    25 |  2650 |   110   (1)| 00:00:02 |       |       |
|*  1 |  COUNT STOPKEY                            |                       |       |       |            |          |       |       |
|   2 |   VIEW                                    |                       |    26 |  2756 |   110   (1)| 00:00:02 |       |       |
|*  3 |    SORT ORDER BY STOPKEY                  |                       |    26 |  3496 |   109  (97)| 00:00:02 |       |       |
|   4 |     UNION-ALL                             |                       |       |       |            |          |       |       |
|   5 |      NESTED LOOPS                         |                       |       |       |            |          |       |       |
|   6 |       NESTED LOOPS                        |                       |     1 |   121 |     4   (0)| 00:00:01 |       |       |
|*  7 |        TABLE ACCESS BY INDEX ROWID        | TK_TASKINFO_DT        |     1 |    96 |     2   (0)| 00:00:01 |       |       |
|*  8 |         INDEX RANGE SCAN                  | IDX_TK_TASKINFO3      |     1 |       |     1   (0)| 00:00:01 |       |       |
|*  9 |        INDEX UNIQUE SCAN                  | PK_TK_ORDERS_ODRID    |     1 |       |     1   (0)| 00:00:01 |       |       |
|* 10 |       TABLE ACCESS BY INDEX ROWID         | TK_ORDERS_DT          |     1 |    25 |     2   (0)| 00:00:01 |       |       |
|  11 |      NESTED LOOPS                         |                       |       |       |            |          |       |       |
|  12 |       NESTED LOOPS                        |                       |    25 |  3375 |   105   (0)| 00:00:02 |       |       |
|  13 |        INLIST ITERATOR                    |                       |       |       |            |          |       |       |
|  14 |         TABLE ACCESS BY GLOBAL INDEX ROWID| TK_TASKINFO_FINISH_DT |    84 |  9240 |    47   (0)| 00:00:01 | ROWID | ROWID |
|* 15 |          INDEX RANGE SCAN                 | IDX$$_08700001        |    84 |       |    20   (0)| 00:00:01 |       |       |
|* 16 |        INDEX UNIQUE SCAN                  | PK_TK_ORDERS_ODRID    |     1 |       |     1   (0)| 00:00:01 |       |       |
|* 17 |       TABLE ACCESS BY INDEX ROWID         | TK_ORDERS_DT          |     1 |    25 |     2   (0)| 00:00:01 |       |       |
-----------------------------------------------------------


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


统计信息
----------------------
          1  recursive calls
          0  db block gets
      44513  consistent gets
          0  physical reads
        116  redo size
       4125  bytes sent via SQL*Net to client
        531  bytes received via SQL*Net from client
          3  SQL*Net roundtrips to/from client
          1  sorts (memory)
          0  sorts (disk)
         25  rows processed




--第三次的执行计划,逻辑读是257654


执行计划
----------------------
Plan hash value: 523130039


-----------------------------------------------------------
| Id  | Operation                                 | Name                  | Rows  | Bytes | Cost (%CPU)| Time     | Pstart| Pstop |
-----------------------------------------------------------
|   0 | SELECT STATEMENT                          |                       |    25 |  2650 |   110   (1)| 00:00:02 |       |       |
|*  1 |  COUNT STOPKEY                            |                       |       |       |            |          |       |       |
|   2 |   VIEW                                    |                       |    26 |  2756 |   110   (1)| 00:00:02 |       |       |
|*  3 |    SORT ORDER BY STOPKEY                  |                       |    26 |  3496 |   109  (97)| 00:00:02 |       |       |
|   4 |     UNION-ALL                             |                       |       |       |            |          |       |       |
|   5 |      NESTED LOOPS                         |                       |       |       |            |          |       |       |
|   6 |       NESTED LOOPS                        |                       |     1 |   121 |     4   (0)| 00:00:01 |       |       |
|*  7 |        TABLE ACCESS BY INDEX ROWID        | TK_TASKINFO_DT        |     1 |    96 |     2   (0)| 00:00:01 |       |       |
|*  8 |         INDEX RANGE SCAN                  | IDX_TK_TASKINFO3      |     1 |       |     1   (0)| 00:00:01 |       |       |
|*  9 |        INDEX UNIQUE SCAN                  | PK_TK_ORDERS_ODRID    |     1 |       |     1   (0)| 00:00:01 |       |       |
|* 10 |       TABLE ACCESS BY INDEX ROWID         | TK_ORDERS_DT          |     1 |    25 |     2   (0)| 00:00:01 |       |       |
|  11 |      NESTED LOOPS                         |                       |       |       |            |          |       |       |
|  12 |       NESTED LOOPS                        |                       |    25 |  3375 |   105   (0)| 00:00:02 |       |       |
|  13 |        INLIST ITERATOR                    |                       |       |       |            |          |       |       |
|  14 |         TABLE ACCESS BY GLOBAL INDEX ROWID| TK_TASKINFO_FINISH_DT |    84 |  9240 |    47   (0)| 00:00:01 | ROWID | ROWID |
|* 15 |          INDEX RANGE SCAN                 | IDX$$_08700001        |    84 |       |    20   (0)| 00:00:01 |       |       |
|* 16 |        INDEX UNIQUE SCAN                  | PK_TK_ORDERS_ODRID    |     1 |       |     1   (0)| 00:00:01 |       |       |
|* 17 |       TABLE ACCESS BY INDEX ROWID         | TK_ORDERS_DT          |     1 |    25 |     2   (0)| 00:00:01 |       |       |
-----------------------------------------------------------


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




统计信息
----------------------
          1  recursive calls
          0  db block gets
     257654  consistent gets
          0  physical reads
         64  redo size
       4125  bytes sent via SQL*Net to client
        531  bytes received via SQL*Net from client
          3  SQL*Net roundtrips to/from client
          1  sorts (memory)
          0  sorts (disk)
         25  rows processed






--第三次的执行计划,逻辑读是538909
执行计划
----------------------
Plan hash value: 523130039


-----------------------------------------------------------
| Id  | Operation                                 | Name                  | Rows  | Bytes | Cost (%CPU)| Time     | Pstart| Pstop |
-----------------------------------------------------------
|   0 | SELECT STATEMENT                          |                       |    25 |  2650 |   110   (1)| 00:00:02 |       |       |
|*  1 |  COUNT STOPKEY                            |                       |       |       |            |          |       |       |
|   2 |   VIEW                                    |                       |    26 |  2756 |   110   (1)| 00:00:02 |       |       |
|*  3 |    SORT ORDER BY STOPKEY                  |                       |    26 |  3496 |   109  (97)| 00:00:02 |       |       |
|   4 |     UNION-ALL                             |                       |       |       |            |          |       |       |
|   5 |      NESTED LOOPS                         |                       |       |       |            |          |       |       |
|   6 |       NESTED LOOPS                        |                       |     1 |   121 |     4   (0)| 00:00:01 |       |       |
|*  7 |        TABLE ACCESS BY INDEX ROWID        | TK_TASKINFO_DT        |     1 |    96 |     2   (0)| 00:00:01 |       |       |
|*  8 |         INDEX RANGE SCAN                  | IDX_TK_TASKINFO3      |     1 |       |     1   (0)| 00:00:01 |       |       |
|*  9 |        INDEX UNIQUE SCAN                  | PK_TK_ORDERS_ODRID    |     1 |       |     1   (0)| 00:00:01 |       |       |
|* 10 |       TABLE ACCESS BY INDEX ROWID         | TK_ORDERS_DT          |     1 |    25 |     2   (0)| 00:00:01 |       |       |
|  11 |      NESTED LOOPS                         |                       |       |       |            |          |       |       |
|  12 |       NESTED LOOPS                        |                       |    25 |  3375 |   105   (0)| 00:00:02 |       |       |
|  13 |        INLIST ITERATOR                    |                       |       |       |            |          |       |       |
|  14 |         TABLE ACCESS BY GLOBAL INDEX ROWID| TK_TASKINFO_FINISH_DT |    84 |  9240 |    47   (0)| 00:00:01 | ROWID | ROWID |
|* 15 |          INDEX RANGE SCAN                 | IDX$$_08700001        |    84 |       |    20   (0)| 00:00:01 |       |       |
|* 16 |        INDEX UNIQUE SCAN                  | PK_TK_ORDERS_ODRID    |     1 |       |     1   (0)| 00:00:01 |       |       |
|* 17 |       TABLE ACCESS BY INDEX ROWID         | TK_ORDERS_DT          |     1 |    25 |     2   (0)| 00:00:01 |       |       |
-----------------------------------------------------------


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




统计信息
----------------------
          1  recursive calls
          0  db block gets
     538909  consistent gets
          0  physical reads
          0  redo size
       4125  bytes sent via SQL*Net to client
        531  bytes received via SQL*Net from client
          3  SQL*Net roundtrips to/from client
          1  sorts (memory)
          0  sorts (disk)
         25  rows processed


SQL> 


--如果再次一直快速连续读,SQL就会挂起,产生长时间的等待。
--但每次的执行计划都是一样的。


解决:
1.尝试对所涉及的表的索引进行rebuild,重新分析表,收缩表,但是效果不大。
2.最终在SQL上加上/*+ rule*/提示,执行计划虽然还是一样,但是不会出现上面多次连续执行SQL产生等待的问题。
如下:
SQL> Select /*+ rule*/ * 
  2     From (Select Task.Order_Id As Od,
  3                                                      Task.Task_Type As Tp,
  4                                                      Task.Task_Status As Ts,
  5                                                      To_Char(Task.Create_Time, 'yyyy-mm-dd hh24:mi:ss') As Ct,
  6                                                      Task.Create_User As Cu,
  7                                                      To_Char(Task.Handle_Time, 'yyyy-mm-dd hh24:mi:ss') As Ht,
  8                                                      Task.Task_User As Sku,
  9                                                      Task.Id As Ind,
 10                                                      o.Channel_Id
 11                                     From Tk_Taskinfo_Dt Task, Tk_Orders_Dt o
......
......


结论:
通过oracle CBO查询SQL会产生等待
通过oracle RBO查询SQL会得到很好效果。

不过具体原因还有待研究,而且通过使用RBO后,随着数据量的变量,不知会不会效率变得低下,还有待观察。

通过查找参考,发现有位网友也出现过同样的问题,只不过他第二次查询的执行计划是变了,我这里还是同样的执行计划。

http://blog.sina.com.cn/s/blog_62e039f00100yvs1.html