PostgreSQL中的漸進式子表掃描?
我們有一個僅附加
events
表,其中正在收集來自不同設備的事件。我們使用 PostgreSQL 9.6 和pg_partman將表拆分為每月分區。使用列
對數據進行分區。 每個月表有大約 100M 行。
event_time
應用程序端有一個查詢,用於顯示設備的最新事件。它看起來像這樣(簡化):
SELECT event_time, event_data FROM events WHERE device_id = 'zzz' ORDER BY event_time DESC LIMIT 10
每個搜尋條件都被索引覆蓋,因此 PostgreSQL 只進行索引掃描。
問題是它會掃描所有子表,然後才會掃描
LIMIT 10
.所有這些表的索引都很大,不適合記憶體,因此完成此查詢最多需要 20 秒。
但在大多數情況下,最近的 10 個事件可從一個名為
events_p<current_year>_<current_month>
.有沒有辦法在不更改應用程式碼的情況下對 PostgreSQL 中的子表實施“漸進式”掃描?
例如:
events events_p2019_02 <- and so on... events_p2019_03 <- scan this if less than 10 rows found in _04 and _05 events_p2019_04 <- scan this if less than 10 rows found in events_p2019_05 events_p2019_05 <- scan this first
我們事先不知道這 10 個事件的可能日期範圍。某些設備可能在最後一天擁有它們,而其他設備可能跨越幾個月(在許多子表中也是如此)。
問題是它掃描所有子表,然後才執行 LIMIT 10。所有這些表的索引都很大,不適合記憶體,因此完成此查詢最多需要 20 秒。
如果您有正確的索引,在這種情況下是
on events (device_id,event_time)
,那麼它只會命中每個索引的一小部分(指向包含每個分區的指定 device_id 的最高 event_time 的葉頁的路徑),並且該部分將可能很容易記憶體,具體取決於存在多少不同的 device_id 並定期查詢。當版本 12 發佈時,如果您使用聲明性範圍分區而不是通過繼承進行分區,系統將足夠智能以適當的順序命中分區,並且一旦 LIMIT 已經滿足,就不會在更多分區上啟動索引掃描。但是,這可能只是您的情況的一個小改進。如果你有正確的索引,它應該已經很快了,不需要改進,如果你有錯誤的索引,即使有了這個改進,它也可能會慢得令人討厭。
如果您不能等待版本 12 並重構您的分區,並且無法建構適當的索引,那麼手動迭代分區的集合返回函式可能是可行的方法,儘管它很乏味、醜陋和錯誤易於。