最近在看postgresql的查詢計划,在查詢中對表的掃描計划大概有如下幾種:
Seq Scan
Index Scan
Bitmap Heap Scan
Index Only Scan
這里就把自己的理解寫下來,歡迎大家批評指正:
1)對於Seq Scan很好理解,就是按照表的記錄的排列順序從頭到尾依次檢索掃描,每次掃描要取到所有的記錄。這也是最簡單最基礎的掃表方式,掃描的代價比較大;
2)對於Index Scan,我們也很熟悉,對於給定的查詢,我們先掃描一遍索引,從索引中找到符合要求的記錄的位置(指針),再定位到表中具體的Page去取。等於是兩次I/O,先走索引,再走取表記錄,不同於全表掃描的是只取所需數據對應的Page,I/O量較小;
3)對於Bitmap Heap Scan不是很常見,我翻閱了postgresql的手冊,找到以下幾句話:
A plain indexscan fetches one tuple-pointer at a time from the index, and immediately visits that tuple in the table. A bitmap scan fetches all the tuple-pointers from the index in one go, sorts them using an in-memory "bitmap" data structure, and then visits the table tuples in physical tuple-location order. The bitmap scan improves locality of reference to the table at the cost of more bookkeeping overhead to manage the "bitmap" data structure --- and at the cost that the data is no longer retrieved in index order, which doesn't matter for your query but would matter if you said ORDER BY.
上面的意思是說,普通的索引掃描( index scan)一次只讀一條索引項,那么一個 PAGE 面有可能被多次訪問;而 bitmap scan 一次性將滿足條件的索引項全部取
出,並在內存中進行排序, 然后根據取出的索引項訪問表數據。當 PostgreSQL 需要合並索引訪問的結果子集時 會用到這種方式 ,通常情況是在用到 "or",“and”時會出現"Bitmap heap scan"。
4)所謂index only scan ,就是因為 建立 index時,所包含的字段集合,囊括了我們查詢語句中的字段,這樣,提取出相應的index ,就不必再次提取數據塊了。
舉個例子:對於表:
create table test(id int, name text, age int);
insert into test select generate_series(1,100000),'test'::text,generate_series(1,100000);
我們對id和age建立復合索引:
create index test_id_age on test(id ,age);
然后,執行查詢:
explain select id, age from test where id < 20 and age >0;
查詢結果為:
postgres=# explain select id ,age from test where id < 20 and age >0;
QUERY PLAN
-------------------------------------------------------------------------------
Index Only Scan using test_id_age on test (cost=0.29..41.94 rows=20 width=8)
Index Cond: ((id < 20) AND (age > 0))
(2 rows)
這個查詢里查詢的id和age就在索引test_id_age上,在我們取出索引的時候,我們已經獲取了(id,age)值的序列,因此就不必再去表中獲取記錄了,在Index上我們就獲得了我們需要的數據,因此稱為Index Only Scan。
那么這幾種表的掃描的應用場景呢?是不是走索引就一定比全表掃描好呢?也不盡然。
我們知道全表掃描是直接掃描全表,而Index Scan是走一次索引再定位表所在的Page,那么我們可以推斷:
當獲取的數據分布很大(比如70%以上)時,用index scan 已經沒有意義了,因為數據太多了,走索引再走表的代價已經超過了單純走表的代價了。就不如用全表掃描了。
而數據分布較小(比如 1.7%),則索引的優勢就體現出來了。可能bitmap index scan的性能就更好(相比於index scan,因為它減少了index的重復掃描)。
當數據更少的時候,用index scan可能就更好(索引重復的可能性較小且回避了在內存中排序的代價)。
需要引起注意的是, bitmap index scan也可以用在where 條件單一的時候。
而對於Index Only Scan,由於不需要掃描表的數據塊,只走索引,那么在能滿足條件的情況下幾乎是最快的了(當然我也沒有數據驗證)。
對於索引的討論一直是熱門話題,這里只是簡單的提到,下次遇到再詳細的寫寫吧。