位圖索引
同樣的,先說是什么,再說為什么。
上篇我們說過BTREE索引是將數據表的索引列和行號排序后以樹狀形式存在磁盤中。那位圖索引是什么樣的呢?
現有如下日志表,有操作類型字段op_type,該字段的取值只有“查看”、“查詢”、“新增”、“修改”和“刪除”。
如下表,當在op_type列建位圖索引,如果某數據行取值是“查看”則將該行的“查看”的單元格記為1,在其他取值上記0;如果取值是“修改”則在該行的“修改”的單元格記1,其他取值上記0:
同理,在操作用戶列建位圖索引如下:
查看如下計數語句在各種情況下的效率如何呢?
select count(*) from tsys_log_click;
未建索引:
建了BTREE索引:
在操作類型op_type列上建位圖索引之后:
我們在不同前提下進行了三次統計查詢,在未建索引的情況下,統計查詢走的全表掃描,代價是223,邏輯讀是838;在非空列r_id上建BTREE索引之后,統計查詢走的索引快速掃描,代價是46,邏輯讀是193;在操作類型列上建位圖索引之后,統計查詢走的位圖索引快速掃描,代價是3,邏輯讀是7。
所以,在統計查詢中,代價最高的是全表掃描;在非空列上建BTREE索引后,一般走BTREE索引的效率要比全表掃描高;最快的是在列取值高度重復的列上建位圖索引。
BTREE索引存儲的是列值,而位圖索引存儲的是比特位值,加入位圖索引所在的列只有一個取值,比如操作類別列只有“查看”,這時整個位圖索引的大小大致等於行數乘以這1個字節,如果操作類型有“查看”和“查詢”,最多也就是只有“查看”的情況下的兩倍大,所以位圖索引在重復度很低時,體積非常小,所以count(*)統計非常快。
當我們在做
where op_type = '刪除' and call_user = 'xrisk'
這樣的即席查詢時,如果在條件列上建了位圖索引,那么查詢其實就是個邏輯運算,在我們上面兩張表中,找出這兩列的取值都為1(與運算)的結果就是了。
但是建了位圖索引的數據表,在更新數據時會有很大的問題:
當位圖索引列的取值越少,更新數據時,維護索引影響的列數就越多,比如操作類別列如果取值只有“查看”和“查詢”的話,鍵值為“查看”的索引條目指向了數據庫一半以上的數據,更新就要影響了一半以上的數據。
所以,如果某列是被頻繁更新的,或者頻繁插入記錄的表,建位圖索引是不合適的。上面還有一條,這個列的取值是高度重復的,否則我們像上面構建的位圖索引的所謂的表也會很龐大。
函數索引
如果對索引列做運算會導致索引無法使用:
select * from tsys_log_click where upper(call_user) = 'TOM';
即使在call_user列上建了索引,這樣的查詢也不會走索引。但我們可以連帶着函數一起建索引,即函數索引:
函數索引的性能是在全表掃描和普通索引之間
所以應該盡可能避免函數索引。例如
select * from tsys_user where substr(user_name,1,3) = 'Tom';
可以改寫成 select * from tsys_user where user_name like 'Tom%';