oracle 位圖索引(bitmap index)
今天研究下oracle中的位圖索引,通過這邊文章你會了解到,什么是位圖索引?,什么情況下應該使用位圖索引?位圖索引的檢索數據的過程,以及位圖索引的弊端?
一:什么是位圖索引?
位圖索引是從oracle 7.3版本開始引入的。目前oracle企業版和個人版都支持位圖索引,但是標準版不支持。位圖索引是這樣一種結構,其中用一個索引鍵條目存儲指向多行的指針,這與B*樹結構不同,在b*樹結構中,索引鍵和表中的行存在著對應關系。在位圖索引中,可能只有很少的索引條目,每個索引條目指向多行,而在傳統的B*樹中,一個索引條目就指向一行那么什么是位圖索引呢?(借用網絡的例子講解)
有張表名為table的表,由三列組成,分別是姓名、性別和婚姻狀況,其中性別只有男和女兩項,婚姻狀況由已婚、未婚、離婚這三項,該表共有100w個記錄。現在有這樣的查詢:select * from table where Gender=‘男’ and Marital=“未婚”;
1)不使用索引
不使用索引時,數據庫只能一行行掃描所有記錄,然后判斷該記錄是否滿足查詢條件。
2)B樹索引
對于性別,可取值的范圍只有'男','女',并且男和女可能各站該表的50%的數據,這時添加B樹索引還是需要取出一半的數據, 因此完全沒有必要。相反,如果某個字段的取值范圍很廣,幾乎沒有重復,比如身份證號,此時使用B樹索引較為合適。事實上,當取出的行數據占用表中大部分(超過20%)的數據時,即使添加了B樹索引,數據庫如oracle、
mysql也不會使用B樹索引,很有可能還是一行行全部掃描。
接下來說下位圖索引原理:
如果在性別列上建立了位圖索引,對于性別這個列,針對每行的rowid(rowid可以理解為每行的物理位置),位圖索引形成兩個向量,男向量為10100...,向量的每一位表示該行是否是男,如果是則位1,否為0,同理,女向量位01011,(可以理解為給每行數據的性別列中為產生兩個向量分別為男向量和女向量:男向量中 值為男:用1表示,值不是男用0表示,同理女向量中 值為女:用1表示,值不是女:用0表示)
如果在婚姻狀況列上建立了位圖索引 對于婚姻狀況這一列,位圖索引生成三個向量,已婚為11000...,未婚為00100...,離婚為00010...。
二:oracle 位圖索引檢索數據的過程:
當我們使用查詢語句“select * from table where Gender=‘男’ and Marital=“未婚”;”的時候 首先取出男向量10100...,然后取出未婚向量00100...,將兩個向量做and操作,這時生成新向量00100...,可以發現rowid=3的and之后的結果為1,表示該表的rowid=3的這行數據就是我們需要查詢的結果(如下“and的結果”為1的就是需要查詢的結果),然后根據rowid找到需要的數據
三:什么情況下應該使用位圖索引?
位圖索引適合只有幾個固定值的列,如性別、婚姻狀況、行政區等等,而身份證號這種類型不適合用位圖索引,如果用戶查詢的列的相異基數非常的小, 要為這些相異基數值比較小的列建索引,就需要建立位圖索引。
那么何謂相異基數非常的小?可以認為行集中不同項的個數除以行數應該是一個很小的數(接近0),例如,某個列(性別)可能取值為M、F、null.如果一個表中有20000條數據,那么3/20000=0.00015,那么這就算是個相異基數很小的情況,類似的,如果有100000個不同的值,與10000000條結果相比,比值是0.01,同樣也很小,也可以認為是相異基數很小的情況,都可以建立位圖索引;
四:位圖索引的限制或者說是弊端?
位圖索引在讀密集的環境中能很好地工作,但是對于寫密集的環境則極不適合,原因在于,一個位圖索引鍵條目(可以理解為前面的男 、女、未婚、已婚等)指向多行。如果一個會話修改了有索引的列的數據,那么大多數情況下,這個索引條目只想的所有行都會被鎖定。oracle無法鎖定一個位圖索引條目中的單獨一位,而是會鎖定整個位圖索引條目,倘若其他會話修改也需要更新同樣的這個位圖索引條目,就會被“關在門外”,這樣就大大影響了并發性,因為每個更新都有可能鎖定數百行,不允許并發地更新他們的位圖列;
舉個例子說明:有這樣一個字段job,記錄各個員工的職位如:dba 、java、php等等 ,假設我們在這個job列上建立了位圖索引。假如rowid=100的員工職業為php,rowid=120的員工職業為php;
如果會話1使用update更新某個員工的職位(job),比如update table set table.job='dba' where rowid=100;,但還沒有commit,而會話2也使用update更新另一個員工的職位,update table set table.job='dba' where rowid=120; 這個時候會話2怎么也更新不了,需要等待會話1 commit。
原因:會話1更新rowid=100的這個員工的職位,假如這個員工原來是php,現在改成dba,那么在commit之前,就會鎖定所有job=php和job=dba的所有行,所以當會話2嘗試更新job=dba只能等待鎖,只有commit之后才解鎖。這樣就大大影響了并發性;
總結:
位圖索引是為數據倉庫(也就是查詢環境設計的),位圖索引特別不適合OLTP系統,位圖索引不適合與dml頻繁的環境,位圖索引適用于DSS系統,位圖索引不適合頻繁修改的系統,弊端是嚴重影響
并發性,因為update索引列值的時候,會鎖定新值和舊值指向的所有數據行,所以使用位圖索引需慎重。