背景
一個這樣的問題:
為什麼
select x from tbl offset x limit x;
兩次查詢連續的OFFSET,會有重複資料呢?
select ctid,* from tbl where ... offset 0 limit 10;
select ctid,* from tbl where ... offset 10 limit 10;
為什麼多數時候offset會推薦用order by?
不使用ORDER BY的話,傳回順序到底和什麼有關?
答案是:
資料庫的掃描方法。
資料庫掃描方法,具體的原理可以到如下文檔中找到PDF,PDF内有詳細的掃描方法圖文介紹。
《阿裡雲 PostgreSQL 産品生态;案例、開發管理實踐、原理、學習資料、視訊;PG天天象上沙龍記錄 - 珍藏級》掃描方法
1、全表掃描, seqscan
從第一個資料塊開始掃描,傳回複合條件的記錄。
2、并發全表掃描, concurrently seqscan
如果有多個會話,對同一張表進行全表掃描時,後發起的會話會與前面正在掃描的會話進行BLOCK對齊步調,也就是說,後面發起的會話,可能是從表的中間開始掃的,掃描到末尾再轉回去,避免多會話同時對一個表全表掃描時的IO浪費。
例如會話1已經掃到了第99個資料塊,會話2剛發起這個表的全表掃描,則會從第99個資料塊開始掃描,掃完在到第一個資料塊掃,一直掃到第98個資料塊。
3、索引掃描, index scan
按索引順序掃描,并回表。
4、索引ONLY掃描, index only scan
按索引順序掃描,根據VM檔案的BIT位判斷是否需要回表掃描。
5、位圖掃描, bitmap scan
按索引取得的BLOCKID排序,然後根據BLOCKID順序回表掃描,然後再根據條件過濾掉不符合條件的記錄。
這種掃描方法,主要解決了離散資料(索引字段的邏輯順序與記錄的實際存儲順序非常離散的情況),需要大量離散回表掃描的情況。
6、并行掃描, parallel xx scan
并行的全表、索引、索引ONLY、位圖掃。首先會FORK出若幹個WORKER,每個WORKER負責一部分資料塊,一起掃描,WORKER的結果(FILTER後的)發給下一個GATER WORKER節點。
7、hash join
哈希JOIN,
8、nest loop join
嵌套循環
9、merge join
合并JOIN(排序JOIN)。
更多掃描方法,請參考PG代碼。
掃描方法決定了資料傳回順序
根據上面的這些掃描方法,我們可以知道一條QUERY下去,資料的傳回順序是怎麼樣的。
select * from tbl where xxx offset 10 limit 100;
1、如果是全表掃描,那麼傳回順序就是資料的實體存放順序,然後偏移10條有效記錄,取下100條有效記錄。
2、如果是索引掃描,則是依據索引的順序進行掃描,然後偏移10條有效記錄,取下100條有效記錄。
不再贅述。
保證絕對的連續
如何保證第一次請求,第二次請求,第三次請求,。。。每一次偏移(offset)固定值,傳回的結果是完全有序,無空洞的。
1、使用rr隔離級别(repeatable read),并且按PK(唯一值字段、字段組合)排序,OFFSET
使用rr級别,保證一個事務中的每次發起的SQL讀請求是絕對視角一緻的。
使用唯一字段或字段組合排序,可以保證每次的結果排序是絕對一緻的。加速每次偏移的資料一樣,是以可以保證資料傳回是絕對連續的。
select * from tbl where xx order by a,b offset x limit xx;
2、使用遊标
使用遊标,可以保證視角一緻,資料絕對一緻。
postgres=# \h declare
Command: DECLARE
Description: define a cursor
Syntax:
DECLARE name [ BINARY ] [ INSENSITIVE ] [ [ NO ] SCROLL ]
CURSOR [ { WITH | WITHOUT } HOLD ] FOR query
begin;
declare a cursor for select * from tbl where xx;
fetch x from a;
... 每一次請求,遊标向前移動
end;
參考
《PostgreSQL 資料離散性 與 索引掃描性能(btree & bitmap index scan)》 《PostgreSQL 11 preview - 分頁核心層優化 - 索引掃描offset優化(使用vm檔案skip heap scan)》 《PostgreSQL 範圍過濾 + 其他字段排序OFFSET LIMIT(多字段區間過濾)的優化與加速》 《PostgreSQL Oracle 相容性之 - TZ_OFFSET》 《PostgreSQL 索引掃描offset核心優化 - case》 《PostgreSQL 資料通路 offset 的質變 case》 《論count與offset使用不當的罪名 和 分頁的優化》 《PostgreSQL offset 原理,及使用注意事項》 《妙用explain Plan Rows快速估算行 - 分頁數估算》 《分頁優化 - order by limit x offset y performance tuning》 《分頁優化, add max_tag column speedup Query in max match enviroment》 《PostgreSQL's Cursor USAGE with SQL MODE - 分頁優化》PostgreSQL 許願連結
您的願望将傳達給PG kernel hacker、資料庫廠商等, 幫助提高資料庫産品品質和功能, 說不定下一個PG版本就有您提出的功能點. 針對非常好的提議,獎勵限量版PG文化衫、紀念品、貼紙、PG熱門書籍等,獎品豐富,快來許願。
開不開森.