![](/img/trans.png)
[英]how to use distinct with multiple columns containing clause “order by” , “desc”, and “Limit”
[英]Multiple ORDER BY DESC will not use index in Postgres
我正在嘗試創建一些查詢,以便在 Postgres 上實現 cursor 分頁(類似於: https://shopify.engineering/pagination-relative-cursors )。 在我的實現中,即使對非唯一列進行排序,我也試圖實現高效的分頁。
我正在努力有效地做到這一點,特別是在給定特定 cursor 檢索上一頁的查詢上。
我用來測試這些查詢的表(>3M 記錄)非常簡單,它具有以下結構:
CREATE TABLE "placemarks" (
"id" serial NOT NULL DEFAULT,
"assetId" text,
"createdAt" timestamptz,
PRIMARY KEY ("id")
);
我在id
字段上有一個清晰的索引,在assetId
列上也有一個索引。
這是我用於檢索下一頁的查詢,給定由最新 ID 和最新 assetId 組成的 cursor:
SELECT
*
FROM
"placemarks"
WHERE
"assetId" > 'CURSOR_ASSETID'
or("assetId" = 'CURSOR_ASSETID'
AND id > CURSOR_INT_ID)
ORDER BY
"assetId",
id
LIMIT 5;
這個查詢實際上非常快,它使用索引並且它還允許通過使用唯一 ID 字段來處理assetId
上的重復值,以避免跳過具有相同CURSOR_ASSETID
值的重復行。
-> Sort (cost=25709.62..25726.63 rows=6803 width=2324) (actual time=0.128..0.138 rows=5 loops=1)
" Sort Key: ""assetId"", id"
Sort Method: top-N heapsort Memory: 45kB
-> Bitmap Heap Scan on placemarks (cost=271.29..25596.63 rows=6803 width=2324) (actual time=0.039..0.088 rows=11 loops=1)
" Recheck Cond: (((""assetId"")::text > 'CURSOR_ASSETID'::text) OR ((""assetId"")::text = 'CURSOR_ASSETID'::text))"
" Filter: (((""assetId"")::text > 'CURSOR_ASSETID'::text) OR (((""assetId"")::text = 'CURSOR_ASSETID'::text) AND (id > CURSOR_INT_ID)))"
Rows Removed by Filter: 1
Heap Blocks: exact=10
-> BitmapOr (cost=271.29..271.29 rows=6803 width=0) (actual time=0.030..0.034 rows=0 loops=1)
" -> Bitmap Index Scan on ""placemarks_assetId_key"" (cost=0.00..263.45 rows=6802 width=0) (actual time=0.023..0.023 rows=11 loops=1)"
" Index Cond: ((""assetId"")::text > 'CURSOR_ASSETID'::text)"
" -> Bitmap Index Scan on ""placemarks_assetId_key"" (cost=0.00..4.44 rows=1 width=0) (actual time=0.005..0.005 rows=1 loops=1)"
" Index Cond: ((""assetId"")::text = 'CURSOR_ASSETID'::text)"
Planning time: 0.201 ms
Execution time: 0.194 ms
問題是當我嘗試獲取相同的頁面但查詢應該返回上一頁時:
SELECT
*
FROM
placemarks
WHERE
"assetId" < 'CURSOR_ASSETID'
or("assetId" = 'CURSOR_ASSETID'
AND id < CURSOR_INT_ID)
ORDER BY
"assetId" desc,
id desc
LIMIT 5;
對於這個查詢,沒有使用索引,即使它會更快:
Limit (cost=933644.62..933644.63 rows=5 width=2324)
-> Sort (cost=933644.62..944647.42 rows=4401120 width=2324)
" Sort Key: ""assetId"" DESC, id DESC"
-> Seq Scan on placemarks (cost=0.00..860543.60 rows=4401120 width=2324)
" Filter: (((""assetId"")::text < 'CURSOR_ASSETID'::text) OR (((""assetId"")::text = 'CURSOR_ASSETID'::text) AND (id < CURSOR_INT_ID)))"
我注意到通過SET enable_seqscan = OFF;
查詢似乎正在使用索引,並且執行得更好更快。 查詢計划結果:
Limit (cost=12.53..12.54 rows=5 width=108) (actual time=0.532..0.555 rows=5 loops=1)
-> Sort (cost=12.53..12.55 rows=6 width=108) (actual time=0.524..0.537 rows=5 loops=1)
Sort Key: assetid DESC, id DESC
Sort Method: top-N heapsort Memory: 25kB
" -> Bitmap Heap Scan on ""placemarks"" (cost=8.33..12.45 rows=6 width=108) (actual time=0.274..0.340 rows=14 loops=1)"
" Recheck Cond: ((assetid < 'CURSOR_ASSETID'::text) OR (assetid = 'CURSOR_ASSETID'::text))"
" Filter: ((assetid < 'CURSOR_ASSETID'::text) OR ((assetid = 'CURSOR_ASSETID'::text) AND (id < 14)))"
Rows Removed by Filter: 1
Heap Blocks: exact=1
-> BitmapOr (cost=8.33..8.33 rows=7 width=0) (actual time=0.152..0.159 rows=0 loops=1)
" -> Bitmap Index Scan on ""placemarks_assetid_idx"" (cost=0.00..4.18 rows=6 width=0) (actual time=0.108..0.110 rows=12 loops=1)"
" Index Cond: (assetid < 'CURSOR_ASSETID'::text)"
" -> Bitmap Index Scan on ""placemarks_assetid_idx"" (cost=0.00..4.15 rows=1 width=0) (actual time=0.036..0.036 rows=3 loops=1)"
" Index Cond: (assetid = 'CURSOR_ASSETID'::text)"
Planning time: 1.319 ms
Execution time: 0.918 ms
優化第二個查詢以始終使用索引的任何線索?
Postgres 數據庫版本:10.20
您的第一個查詢的快速性能似乎取決於您的常量“CURSOR_ASSETID”在該列的分布中的位置。 或者也許這種運氣不是運氣而是它會一直如此嗎?
為了更普遍地獲得良好的性能,包括反向排序,您需要使用元組比較器而不是 OR 比較器來編寫查詢。
WHERE
("assetId",id) < ('something',500000)
如果您使用的是 v13 中引入增量排序之前的版本,或者如果“assetId”可以有大量的關系,那么您將需要一個多列索引("assetId",id)
以獲得最佳性能。
並且沒有理由用 DESC 修飾索引,因為 PostgreSQL 知道如何向后讀取索引。 當兩列的順序彼此不同時,需要裝飾索引,因為那時您需要“螺旋式”讀取未裝飾的索引,而不是完全向前或完全向后讀取。 (但這在這里無論如何都行不通,因為元組比較器在列之間不能有不同的順序。)
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.