昨天我們了解到 Index-Only Scan 可以省去 Heap Fetch 的步驟,減少 I/O 讓查詢速度變快。不過當中還有一個很重要的概念沒有提到,就是 Visibility Map。
當查詢時發現資料都在 Index 找得到的時候,就不會多做 Heap Fetch 而直接回傳。不過在回傳之前, PostgreSQL 還有一件很重要的事情要做,就是去確認 Visibility Map。
An index-only scan, after finding a candidate index entry, checks the visibility map bit for the corresponding heap page. If it's set, the row is known visible and so the data can be returned with no further work.
官方文件有提到他會去檢查 Visibility Map,看對應的 Heap Page 是否標記為 all_visible
,如果是的話, PostgreSQL 就能直接使用 Index 內的資料。如果不是的話呢?那就要回到 Heap Fetch,完成一般 Index Scan 的流程,才會回傳。
If it's not set, the heap entry must be visited to find out whether it's visible, so no performance advantage is gained over a standard index scan.
為什麼多了查詢 Visibility Map 能加速查詢?
你可能會想說,這樣還要多查詢 Visibility Map 再決定要不要 Heap Fetch,這樣真的會比較快嗎?
文件裡面也有提到 Visibility Map 比 Heap 小 10⁴ 倍(4 個數量級 = 10000 倍),所以讀取 Visibility Map 的 I/O 負擔非常小。也是因為體積小的關係,通常可以完全存放在記憶體內,所以存取它幾乎不會有延遲。
Even in the successful case, this approach trades visibility map accesses for heap accesses; but since the visibility map is four orders of magnitude smaller than the heap it describes, far less physical I/O is needed to access it.
In most situations the visibility map remains cached in memory all the time.
實際查看 Visibility Map
了解 Visibility Map 後,我們有辦法可以直接查詢到他嗎?PostgreSQL 有提供 pg_visibility
的 extension,安裝完之後就可以查到裡面的資料了。
CREATE EXTENSION pg_visibility
SELECT * FROM pg_visibility_map('products')
從 table 當中可以看到每一頁(page)都會紀錄它的 all_visible
狀態。但是為何 Index-Only Scan 要做這個步驟,檢查所有資料都是 visible,才能回傳資料啊?
這就與 PostgreSQL 的回收機制有關了,明天我們要來更深入的了解回收機制,以及如何維護 Visibility Map 這個東西。因為可能在沒有特別注意的情況下,導致 all_visible
為 false,如此就會造成 Index-Only Scan 無法完全發揮優勢了。
https://www.postgresql.org/docs/current/storage-vm.html