iT邦幫忙

2025 iThome 鐵人賽

DAY 10
1

https://ithelp.ithome.com.tw/upload/images/20250909/20177885IIqEvCj5ZE.png

在 Day 5 提到的 Index Scan & Bitmap Scan 中,我們知道透過 Index 讀取資料時,通常會有兩個步驟:

  1. Index Lookup - 在 B-tree 找到符合條件的 tuple(資料的位置)
  2. Heap Fetch - 根據 tuple,再去 Heap(實際存放資料的地方) 讀取資料

但每一次查詢,都需要 Heap Fetch 嗎? 其實並不一定。今天要介紹的 Index-Only Scan,就只需要 Index Lookup ,就能返回查詢結果啦。

An Index-Only Scan is a type of index scan where PostgreSQL retrieves all required columns directly from the index without accessing the actual table (heap). This improves performance by avoiding unnecessary disk I/O.

這是因為 Index 內存的內容剛好是查詢需要的結果時,就不用再多一步 Heap Fetch 的步驟,可以解少 I/O 讓效能變快。

測試:用 Index-Only Scan 加速查詢

  1. 這裡我們用一個新的 product table 來舉例:
CREATE TABLE products (
	id SERIAL PRIMARY KEY,
	category TEXT NOT NULL,
	name TEXT NOT NULL,
	price NUMERIC NOT NULL,
	stock INT NOT NULL
);
  1. 假設我們常常用 category 來篩選資料,但查詢時只需要 price 這個欄位,可以用 INCLUDE 來建立(INCLUDE 是什麼意思我們待會會來看看)
CREATE INDEX idx_category_price ON products(category) INCLUDE (price);
  1. 設定好 Index 後,來檢查一下是否能使用 Index-Only Scan 呢?(測試資料為 30000 筆)
EXPLAIN ANALYZE
SELECT price FROM products WHERE category = 'Electronics';

https://ithelp.ithome.com.tw/upload/images/20250809/20177885QA7BSC4gkd.png

這裡可以看到成功的使用到 Index-Only Scan 了!因為這樣查詢時,只使用到 categoryprice,都已經在 Index 中,避免了 Heap Fetch。

INCLUDE 是什麼意思?

當我們想有效利用 Index-Only Scan 的功能時,可以選擇建立 Covering Index。它是特別用來設計包含某個常用查詢所需要的所有欄位(在我們的舉例中需要的欄位為 price ,查詢常用欄位為 category)。由於查詢通常不只需要搜尋條件中的欄位,PostgreSQL 允許你在 Index 中額外加入一些「附帶」欄位,這些欄位不屬於搜尋(search key),純粹是為了讓查詢能直接從 Index 取得所需資料。

要這麼做,只需要在建立 Index 時使用 INCLUDE,列出那些額外的欄位即可。

To make effective use of the index-only scan feature, you might choose to create a covering index, which is an index specifically designed to include the columns needed by a particular type of query that you run frequently.

在這裡我們先記錄一下 Index 的大小,總共是 968 KB。

SELECT pg_size_pretty(pg_relation_size('idx_category_price'))

https://ithelp.ithome.com.tw/upload/images/20250809/20177885SbAIdT6UUM.png

比較:使用 INCLUDE vs 沒有使用 INCLUDE

如果我們沒有使用 INCLUDE 以及 Index-Only Scan,而是讓搜尋時需要 Heap Fetch,他的速度以及 Index 的大小會是如何呢?我們就接著來看看。

  1. 先把原本的 Index 刪除
DROP INDEX idx_category_price
  1. 再建立一次 Index,這次沒有用到 INCLUDE
CREATE INDEX idx_category_price ON products(category) 
  1. 檢查一下搜尋速度

https://ithelp.ithome.com.tw/upload/images/20250809/20177885zTumuTSlWQ.png

這次沒有 Index-Only Scan 了,使用的是 Bitmap Scan,因為 price 欄位沒有在 Index 中,所以要再回 Heap 找到資料。搜尋速度為 1.190 ms ,比第一種方法稍微慢一些些(0.744 ms)。

那麼再來看一下第二種方式的 Index 大小:

https://ithelp.ithome.com.tw/upload/images/20250809/20177885Hay86CByEL.png

Type INCLUDE 沒有 INCLUDE
Size 968 KB 224 KB

可以發現使用 INCLUDE 的 Index 比較大,看到這裡,大家應該都能知道為什麼了吧?因為第一個版本的方式將查詢資料的欄位也都存在 Index 內了,雖然少了 Heap Fetch 速度比較快,但是儲存空間就會比較大。我們又再一次驗證了 Index 是用「空間換取時間」的概念!也因此想要使用 Index-Only Scan 的話,就要記得使用時的這些取捨。

重點回顧

  • Index-Only Scan 可提升查詢效能,避免 Heap Fetch,減少 I/O 負擔
  • Index -Only Scan 使用方式:如果只需用欄位 x 找欄位 y 的資料,用 CREATE INDEX tab_x_y ON tab(x) INCLUDE (y)

參考資料

https://www.postgresql.org/docs/current/indexes-index-only-scans.html


上一篇
Day 9 - Partial Index:只幫必要資料加 Index
系列文
PostgreSQL 效能優化 30 天挑戰:從 Index 到 Transaction 的深入探索10
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言