开发人员说后台查询某业务数据明细,非常慢,让我优化下,

然后我到公司平台点击页面上的查询发现花了6分钟,然后我把sql抓出来,发现是一个5个表关联的分页语句,

其中4个表只有几M,一个大表15G,小表就不用考虑了,然后我把大表拿出来单独分页查询发现同样花了6分钟,语句和执行计划如下:

SQL> explain plan for 
  2  SELECT *
  FROM (SELECT t.*, rownum AS rn
          FROM  (SELECT *
                   FROM tb_recharge
                  WHERE create_date >= to_date('2014-09-01 00:00:00','yyyy-mm-dd hh24:mi:ss')
                    AND create_date < to_date('2014-10-01 00:00:00','yyyy-mm-dd hh24:mi:ss')
                  ORDER BY id desc) t
         WHERE rownum <= 10
         )
 11   WHERE rn > 0
 12  /

Explained.


Elapsed: 00:00:00.15

SQL> set lines 200 pages 200;
SQL> select * from table(dbms_xplan.display);

PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Plan hash value: 4050647635

-------------------------------------------------------------------------------------------------------------------------
| Id  | Operation          | Name| Rows| Bytes | Cost (%CPU)| Time| Pstart| Pstop |
-------------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT               ||     1 |  2047 |     1 (100)| 00:00:01 |||
|*  1 |  VIEW                   ||     1 |  2047 |     1 (100)| 00:00:01 |||
|*  2 |   COUNT STOPKEY                ||||     ||||
|   3 |    VIEW                    ||     1 |  2034 |     1 (100)| 00:00:01 |||
|*  4 |     SORT ORDER BY STOPKEY           ||     1 |  2034 |     1 (100)| 00:00:01 |||
|   5 |      PARTITION RANGE SINGLE           ||     1 |  2034 |     0   (0)| 00:00:01 |    14 |    14 |
|   6 |       TABLE ACCESS BY LOCAL INDEX ROWID| TB_RECHARGE|     1 |  2034 |     0   (0)| 00:00:01 |    14 |    14 |
|*  7 |        INDEX RANGE SCAN            | TB_RECHARGE_I5 |     1 ||     0   (0)| 00:00:01 |    14 |    14 |
-------------------------------------------------------------------------------------------------------------------------

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

   1 - filter("RN">0)
   2 - filter(ROWNUM<=10)
   4 - filter(ROWNUM<=10)
   7 - access("CREATE_DATE">=TO_DATE(' 2014-09-01 00:00:00', 'syyyy-mm-dd hh24:mi:ss') AND
     "CREATE_DATE"<TO_DATE(' 2014-10-01 00:00:00', 'syyyy-mm-dd hh24:mi:ss'))

23 rows selected.

Elapsed: 00:00:05.38


这个语句我把order by 去掉发现非常快ms级别返回数据,那么可以断定是order by导致性能问题,然后我count了一把发现数据600多万行,

注意sql中是按照create_date过滤,id排序,所以CBO根本就无法判断过滤后的数据id是否有序,

所以执行计划 4中出现了SORT ORDER BY STOPKEY,没有走first_rows特性,这一步对600多万行进行了order by,因此速度就慢在这了,


可想而知如果按照create_date排序那么就可以走fist_rows,然后我问开发人员是否可以按照时间字段排序,开发说不行,因为在同一时间会有很多数据,排序会混乱,那么就只能是用其它方法了


下面是我的解决方式:

在create_date,id 创建组合index,order by id 改写为ORDER BY  create_date DESC, id DESC 创建index改写后的sql如下

SQL> explain plan for 
  2  SELECT *
  FROM (SELECT t.*, rownum AS rn
          FROM  (SELECT /*+ index_desc(tb_recharge IDX_CREATEDATE_ID)*/*
                   FROM tb_recharge
                  WHERE create_date >= to_date('2014-09-01 00:00:00','yyyy-mm-dd hh24:mi:ss')
                    AND create_date < to_date('2014-10-01 00:00:00','yyyy-mm-dd hh24:mi:ss')
                  ORDER BY  create_date DESC, id DESC ) t
         WHERE rownum <= 10
         )
 11   WHERE rn > 0
 12  /

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

PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Plan hash value: 3687185489

---------------------------------------------------------------------------------------------------------------------------
| Id  | Operation      | Name  | Rows  | Bytes | Cost (%CPU)| Time  | Pstart| Pstop |
---------------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT      |   |1 |  2047 |3  (67)| 00:00:01 |  |  |
|*  1 |  VIEW      |   |1 |  2047 |3  (67)| 00:00:01 |  |  |
|*  2 |   COUNT STOPKEY       |   |  |  |       |  |  |  |
|   3 |    VIEW       |   |1 |  2034 |3  (67)| 00:00:01 |  |  |
|   4 |     PARTITION RANGE SINGLE      |   |1 |  2034 |3  (67)| 00:00:01 |    14 |    14 |
|   5 |      TABLE ACCESS BY LOCAL INDEX ROWID| TB_RECHARGE  |1 |  2034 |3  (67)| 00:00:01 |    14 |    14 |
|*  6 |       INDEX RANGE SCAN DESCENDING     | IDX_CREATEDATE_ID |1 |  |    71   (0)| 00:00:01 |    14 |    14 |
---------------------------------------------------------------------------------------------------------------------------

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

   1 - filter("RN">0)
   2 - filter(ROWNUM<=10)
   6 - access("CREATE_DATE">=TO_DATE(' 2014-09-01 00:00:00', 'syyyy-mm-dd hh24:mi:ss') AND "CREATE_DATE"<TO_DATE('
     2014-10-01 00:00:00', 'syyyy-mm-dd hh24:mi:ss'))

21 rows selected.

Elapsed: 00:00:00.07


优化后ms级别返回,看执行计划中SORT ORDER BY STOPKEY消失了,第6步中走了INDEX RANGE SCAN DESCENDING,如果是用高级执行计划可以看到cbo只访问了10行

就stop返回数据了