單表查詢某列最大最小值的性能問題

在oracle 10g中,有一個單表查詢的SQL語句,它沒有where子句,只是簡單地同時求某列最大值和最小值。
按照理解,它應該走全索引掃描,但它卻走了全表掃描。單表的數據量有點大,組成也有點複雜,LOB字段很多,索引有點多,加lob的索引一起有13個。這下性能就差很多,本來預計毫秒級別的操作變成了分鐘。在其他同版本的庫上,索引較少時,會走全索引掃描,但性能也不好,查詢時的一致性讀也很大。

SQL是這樣:select  max(updateid),min(updateid) from dbcenter.TABLE_NAME ;
很簡單,而且updateid列上有一個唯一索引。索引也分析過,但現在執行起來卻性能差的很,致命的全表掃描。


首先,使用set autotrace trace exp stat得到真實的執行計劃。
SQL> set timing on
SQL> set autotrace trace exp stat
SQL> set linesize 300

-------------------------------------------------------------------------------------
| Id  | Operation          | Name           | Rows  | Bytes | Cost (%CPU)| Time     |
-------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |                |     1 |     7 |   373K  (1)| 01:14:42 |
|   1 |  SORT AGGREGATE    |                |     1 |     7 |            |          |
|   2 |   TABLE ACCESS FULL| TABLE_NAME |  8665K|    57M|   373K  (1)| 01:14:42 |
-------------------------------------------------------------------------------------

Statistics
----------------------------------------------------------
          1  recursive calls
          0  db block gets
    1700621  consistent gets
    1506260  physical reads
          0  redo size
        602  bytes sent via SQL*Net to client
        492  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
          1  rows processed

SQL>

從結果中可以看到走的就是全表掃描。從統計值看,也是真正的全表掃描了,從頭掃到尾巴的那種,沒辦法,表中這個字段的值又不是排序的,不全部掃完不知道最大最小值的。

很顯然,這不是最優的結果。我認爲最理想應該是走updateid列的索引,一個索引快速全掃描就行。

猜測,會不會是索引多了不知道如何選擇。在select子句中是不主動選擇索引的?

但是,我使用hint也沒有效果,優化器依然沒有選擇走這個索引。


select/*+index_ffs(TABLE_NAME IDX55021287)*/ MAX(updateid), MIN(updateid) from dbcenter.TABLE_NAME;

Elapsed: 00:03:28.77

Execution Plan
----------------------------------------------------------


-------------------------------------------------------------------------------------
| Id  | Operation          | Name           | Rows  | Bytes | Cost (%CPU)| Time     |
-------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |                |     1 |     7 |   373K  (1)| 01:14:42 |
|   1 |  SORT AGGREGATE    |                |     1 |     7 |            |          |
|   2 |   TABLE ACCESS FULL| TABLE_NAME |  8665K|    57M|   373K  (1)| 01:14:42 |
-------------------------------------------------------------------------------------
Statistics
----------------------------------------------------------
          1  recursive calls
          0  db block gets
    1701902  consistent gets
    1497285  physical reads
          0  redo size
        602  bytes sent via SQL*Net to client
        492  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
          1  rows processed

但是,如果只查max或min時,會走索引。

select  MIN(updateid) from dbcenter.TABLE_NAME ;

Execution Plan
----------------------------------------------------------
Plan hash value: 3935799349

------------------------------------------------------------------------------------------
| Id  | Operation                  | Name        | Rows  | Bytes | Cost (%CPU)| Time     |
------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT           |             |     1 |     7 |   373K  (1)| 01:14:42 |
|   1 |  SORT AGGREGATE            |             |     1 |     7 |            |          |
|   2 |   INDEX FULL SCAN (MIN/MAX)| IDX55021287 |  8665K|    57M|            |          |
------------------------------------------------------------------------------------------


Statistics
----------------------------------------------------------
          0  recursive calls
          0  db block gets
          3  consistent gets
          0  physical reads
          0  redo size
        524  bytes sent via SQL*Net to client
        492  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
          1  rows processed

性能也好的很,一致性讀只有3。這樣的結果也很好理解。索引是唯一索引,已經排序好的,求一個最大值,肯定只要掃描索引的開始或者結束部分的數據塊即可。
          
                    
因此,需要分析一下這個SQL的執行計劃產生的過程。我使用event 10053 trace name context forever ,level 1方法來完成這個操作。

alter system flush shared_pool;
alter session set "_optimizer_search_limit"=15;
oradebug setmypid;
oradebug event 10053 trace name context forever ,level 1;
explain plan for select  max(updateid),min(updateid) from dbcenter.TABLE_NAME ;

***************************************
SINGLE TABLE ACCESS PATH
  -----------------------------------------
  BEGIN Single Table Cardinality Estimation
  -----------------------------------------
  Table: TABLE_NAME  Alias: TABLE_NAME     
    Card: Original: 8663996  Rounded: 8663996  Computed: 8663996.00  Non Adjusted: 8663996.00
  -----------------------------------------
  END   Single Table Cardinality Estimation
  -----------------------------------------
  Access Path: TableScan
    Cost:  373495.00  Resp: 373495.00  Degree: 0
      Cost_io: 372211.00  Cost_cpu: 18442053762
      Resp_io: 372211.00  Resp_cpu: 18442053762
******** Begin index join costing ********
  ****** trying bitmap/domain indexes ******
  Access Path: index (FullScan)
    Index: IDX242025
    resc_io: 25019.00  resc_cpu: 1911171307
    ix_sel: 1  ix_sel_with_filters: 1
    Cost: 2515.21  Resp: 2515.21  Degree: 0
  Access Path: index (FullScan)
    Index: IDX94341804
    resc_io: 31023.00  resc_cpu: 1953914433
    ix_sel: 1  ix_sel_with_filters: 1
    Cost: 3115.90  Resp: 3115.90  Degree: 0
  Access Path: index (FullScan)
    Index: PK_TABLE_NAME
    resc_io: 25217.00  resc_cpu: 1912567352
    ix_sel: 1  ix_sel_with_filters: 1
    Cost: 2535.02  Resp: 2535.02  Degree: 0
  Access Path: index (FullScan)
    Index: IDX242025
    resc_io: 25019.00  resc_cpu: 1911171307
    ix_sel: 1  ix_sel_with_filters: 1
    Cost: 2515.21  Resp: 2515.21  Degree: 0
  ****** finished trying bitmap/domain indexes ******
******** End index join costing ********
  Best:: AccessPath: TableScan
         Cost: 373495.00  Degree: 1  Resp: 373495.00  Card: 8663996.00  Bytes: 0
***************************************

從結果看,優化器在index join costing操作時,並沒有將IDX55021287索引計算進來。

即使我使用了alter session set "_optimizer_search_limit"=15;將限制值從5提升到15也沒有效果。或許,index join costing操作時引入的索引數量不是這個參數控制。


最大最小值的查詢操作,就不應該在SQL中一步完成,應該分步驟實現。很顯然,oracle的查詢重寫沒有那麼智能,沒有將其分開。即使在11g也不行,我測試過了。




發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章