bitmap index的優化案例

某系統存儲過程執行不過去,通過查詢長時間ACTIVE的SESSION定位到如下語句:

UPDATE AAA_BBBBBBBBB_ALM T SET KEY_BS='1111111111'
 WHERE T.PARTITION_KEY = 'S00000014097'
   AND T.KEY_BS_BK = 'ASSET_NS-INB-DMD'
   and SUBSTR(ATTRIBUTE_1,1,6) in ('101101', '101102', '101103','101104');  
Plan hash value: 2937027114

------------------------------------------------------------------------------------------------------
| Id  | Operation                     | Name                 | Rows  | Bytes | Cost (%CPU)| Time     |
------------------------------------------------------------------------------------------------------
|   0 | UPDATE STATEMENT              |                      |     1 |    74 |     3   (0)| 00:00:01 |
|   1 |  UPDATE                       | AAA_BBBBBBBBB_ALM    |       |       |            |          |
|*  2 |   TABLE ACCESS BY INDEX ROWID | AAA_BBBBBBBBB_ALM    |     1 |    74 |     3   (0)| 00:00:01 |
|   3 |    BITMAP CONVERSION TO ROWIDS|                      |       |       |            |          |
|*  4 |     BITMAP INDEX SINGLE VALUE | AAA_BBBBBBBBB_ALM_B1 |       |       |            |          |
------------------------------------------------------------------------------------------------------
 
Predicate Information (identified by operation id):
---------------------------------------------------
 
   2 - filter("T"."KEY_BS_BK"='ASSET_NS-INB-DMD' AND (SUBSTR("ATTRIBUTE_1",1,6)='101101' OR 
              SUBSTR("ATTRIBUTE_1",1,6)='101102' OR SUBSTR("ATTRIBUTE_1",1,6)='101103' OR 
              SUBSTR("ATTRIBUTE_1",1,6)='101104'))
   4 - access("T"."PARTITION_KEY"='S00000014097')

通過執行計劃可以得出,BITMAP INDEX SINGLE VALUE 先通過鍵值篩選,然後BITMAP CONVERSION TO ROWIDS轉換爲rowid,再通過ROWID去回表TABLE ACCESS BY INDEX ROWID,這個過程和普通的索引掃描類似,只是多了一個BITMAP CONVERSION TO ROWIDS的操作!查一下PARTITION_KEY字段的選擇性

SELECT PARTITION_KEY,COUNT(*) FROM AAA_BBBBBBBBB_ALM GROUP BY PARTITION_KEY;
1    S00000014097    796083

呵呵了!整個表distinct值就一個,BITMAP INDEX SINGLE VALUE篩選根本沒起作用,索引會取出所有行的ROWID,然後回表的時候回了讀取了表所有的數據塊,而且是單塊讀!

下面的圖片顯示了表上面所有的索引信息,而且都是位圖索引。

.

這裏我們詳細說一下位圖索引的原理和使用場景:

上圖中 一個表上面建立了10個bitmap索引,如果是b-tree索引,一般不會允許建如此多,維護和存儲花費的代價將很大。

而位圖索引,則沒有問題,這和他的存儲方式有關。比如一個表有1000w 行,性別列 男 999w 女1w

如果是b-tree索引,存儲的方式是這樣的

男 ,ROWID

.....

男,ROWID   ->第999w行

女,ROWID

.....

女,ROWID ->第1w行

索引的葉子節點裏面存儲的數據行數是1000w行。

如果是bitmap索引,存儲的方式是這樣的,把 男 女 分別用1,0表示

男 ,ROWID 1條  1010001......
女 ,ROWID 1條  0101110......    男人 女人 存2條其他地方 用1 0 代替

可見DISTINCT值越多,行數就越多!這也是爲什麼位圖索引最好建在基數低的列

如果一個表上面建多個位圖索引比如 婚姻狀態列(已婚、未婚、離異),級別(OCA、OCP、OCM) ........這樣一個表上面有多個位圖索引,存儲方式如下:

對於性別這個列,位圖索引形成兩個向量,男向量爲10100...,向量的每一位表示該行是否是男,如果是則位1,否爲0,同理,女向量位01011。

RowId

1

2

3

4

5

...

1

0

1

0

0

 

0

1

0

1

1

 

對於婚姻狀況這一列,位圖索引生成三個向量,已婚爲11000...,未婚爲00100...,離婚爲00010...。

RowId

1

2

3

4

5

...

已婚

1

1

0

0

0

 

未婚

0

0

1

0

1

 

離婚

0

0

0

1

0

 

對於級別這一列,同樣生成三個向量,OCA爲11000...,OCP爲00100...,OCM爲00010...。

RowId

1

2

3

4

5

...

OCA

1

1

0

0

0

 

OCP

0

0

1

0

1

 

OCM

0

0

0

1

0

 

在這個案例中位圖索引只使用了一個條件"T"."PARTITION_KEY"='S00000014097',這個條件BITMAP INDEX SINGLE VALUE篩選的結果就是 向量11111111111....11111再通過對應的位置,把向量轉成ROWID(BITMAP CONVERSION TO ROWIDS)。所以切記 不要把位圖索引當做B樹索引來使用。

位圖索引的使用場景:通常是SQL語句必須有多個含有位圖索引字段的過濾條件,CBO去解析時,先通過這個條件再位圖內部做邏輯運算(與 或 非)。比如:select * from table where 性別='男' and 婚姻狀況='未婚',首先取出男向量10100...,然後取出未婚向量00100...,將兩個向量做and操作,這時生成新向量00100...,可以發現第三位爲1,表示該表的第三行數據就是我們需要查詢的結果。

RowId

1

2

3

4

5

...

1

0

1

0

0

 

AND

 

 

 

 

 

 

未婚

0

0

1

0

1

 

結果

0

0

1

0

0

 

我們得到的向量00100,表示第三個ROWID是我們需要,通過位圖轉換出ROWID的值即可!如果條件裏面再加上AND 級別='OCM',可以進一步縮小行數,縮小轉換出的ROWID個數。

總結一下:位圖索引的適用場景是表上有多個選擇性不好的列,而對於這個表的查詢語句條件裏多次使用這些列,就可以通過在這些列上面建位圖索引的方式來提高查詢效率(注:OLTP系統嚴禁使用位圖索引)

根據上面的分析,需要優化的SQL完全符合使用位圖索引的條件。條件列裏面有4個邏輯或3個邏輯與,只是SQL沒有按照位圖的原理去走邏輯或和邏輯與,而是把位圖索引當成普通的B樹索引來用。這時候我們需要使用HINT強制讓位圖索引進行位運算

The INDEX_COMBINE hint instructs the optimizer to use a bitmap access path for the table. If indexspec is omitted from the INDEX_COMBINE hint, then the optimizer uses whatever Boolean combination of indexes has the best cost estimate for the table. If you specify indexspec, then the optimizer tries to use some Boolean combination of the specified indexes. 

使用HINT後的執行計劃如下:
UPDATE /*+ index_combine(t AAA_BBBBBBBBB_ALM_B1 AAA_BBBBBBBBB_ALM_B2 AAA_BBBBBBBBB_ALM_B7) */AAA_BBBBBBBBB_ALM T SET KEY_BS='1111111111'
 WHERE T.PARTITION_KEY = 'S00000014097'
   AND T.KEY_BS_BK = 'ASSET_NS-INB-DMD'
   and SUBSTR(ATTRIBUTE_1,1,6) in ('101101', '101102', '101103','101104');              

Plan hash value: 82554262
 
-------------------------------------------------------------------------------------------------------
| Id  | Operation                      | Name                 | Rows  | Bytes | Cost (%CPU)| Time     |
-------------------------------------------------------------------------------------------------------
|   0 | UPDATE STATEMENT               |                      |     1 |    74 |    18   (0)| 00:00:01 |
|   1 |  UPDATE                        | AAA_BBBBBBBBB_ALM    |       |       |            |          |
|   2 |   TABLE ACCESS BY INDEX ROWID  | AAA_BBBBBBBBB_ALM    |     1 |    74 |    18   (0)| 00:00:01 |
|   3 |    BITMAP CONVERSION TO ROWIDS |                      |       |       |            |          |
|   4 |     BITMAP AND                 |                      |       |       |            |          |
|*  5 |      BITMAP INDEX SINGLE VALUE | AAA_BBBBBBBBB_ALM_B1 |       |       |            |          |
|*  6 |      BITMAP INDEX SINGLE VALUE | AAA_BBBBBBBBB_ALM_B2 |       |       |            |          |
|   7 |      BITMAP OR                 |                      |       |       |            |          |
|*  8 |       BITMAP INDEX SINGLE VALUE| AAA_BBBBBBBBB_ALM_B7 |       |       |            |          |
|*  9 |       BITMAP INDEX SINGLE VALUE| AAA_BBBBBBBBB_ALM_B7 |       |       |            |          |
|* 10 |       BITMAP INDEX SINGLE VALUE| AAA_BBBBBBBBB_ALM_B7 |       |       |            |          |
|* 11 |       BITMAP INDEX SINGLE VALUE| AAA_BBBBBBBBB_ALM_B7 |       |       |            |          |
-------------------------------------------------------------------------------------------------------
 
Predicate Information (identified by operation id):
---------------------------------------------------
 
   5 - access("T"."PARTITION_KEY"='S00000014097')
   6 - access("T"."KEY_BS_BK"='ASSET_NS-INB-DMD')
   8 - access(SUBSTR("ATTRIBUTE_1",1,6)='101101')
   9 - access(SUBSTR("ATTRIBUTE_1",1,6)='101102')
  10 - access(SUBSTR("ATTRIBUTE_1",1,6)='101103')
  11 - access(SUBSTR("ATTRIBUTE_1",1,6)='101104')

 

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