本文介紹如何在列存表中使用排序鍵結合粗糙集索引,從而提高查詢性能。

重要 本文適用于:
  • 存儲預留模式:數據庫內核版本為20200826版本之后的新建實例。
  • 存儲彈性模式:數據庫內核版本為20200906版本之后的新建實例。

背景信息

當您創建表的時候,可以定義一個或者多個列為排序鍵(SORTKEY)。數據寫入到表中之后,您可以對該表按照排序鍵進行排序重組。

表排序后可以加速范圍限定查詢,數據庫會對每固定行記錄每一列的min、max值。如果在查詢時使用范圍限定條件,ADBPG的查詢引擎可以根據min、max值在對表進行掃描(SCAN)時快速跳過不滿足限定條件的數據塊(Block)。

例如,假設一張表存儲了7年的數據,并且這張表的數據是按照時間字段排序存儲的,如果我們需要查詢一個月的數據,那么只需要掃描 1/(7*12) 的數據,也就是說有98.8%的數據塊在掃描(SCAN)時可以被過濾掉。但是如果數據沒有按照時間排序的話,可能所有的磁盤上的數據塊都要被掃描到。

ADBPG支持兩種排序方式:
  • 組合排序:適用于限定條件是查詢的前綴子集或者完全包含排序鍵,更適合于查詢包含首列限定條件的情況。
  • 多維排序:給每一個排序鍵分配相同的權重,更適合于查詢條件包含任意限定條件子集的場景。
更多詳情請參見組合排序和多維排序的性能對比
性能對比
本節以組合排序給粗糙集索引帶來的性能提升為例,展示粗糙集索引相比全表掃描的性能提升。

以TPCH Lineitem表為例,表中存儲了7年的數據,我們比較數據未按照l_shipdate字段排序和用l_shipdate字段作為排序鍵并進行排序的限定條件查詢的性能。

說明 本文的TPC的實現基于官方TPC的基準測試,并不能與已發布的TPC基準測試結果相比較,本文中的測試并不符合TPC基準測試的所有要求。
測試步驟
  1. 創建一個32節點的實例。
  2. 對Lineitem寫入130億行記錄。
  3. 查詢1997-09-01到1997-09-30的數據。
    • 數據未按照l_shipdate排序。數據未按照l_shipdate排序
    • 數據按照l_shipdate排序。按照l_shipdate排序

創建表時定義排序鍵

樣例

create table test(date text, time text, open float, high float, low float, volume int) with(APPENDONLY=true,ORIENTATION=column) ORDER BY (volume);

語法

CREATE [[GLOBAL | LOCAL] {TEMPORARY | TEMP}] TABLE table_name (
[ { column_name data_type  ...} ]
)
[ DISTRIBUTED BY (column, [ ... ] ) | DISTRIBUTED RANDOMLY ]
[ ORDER BY (column, [ ... ] )]

數據庫內核版本20210326之前,指定排序鍵語法為SORTKEY (column, [ ... ])

對表進行排序

對數據進行組合排序
SORT [tablename]

數據庫內核版本20210326之前可以使用以下語法的語句:

VACUUM SORT ONLY [tablename]
對數據進行多維排序
MULTISORT [tablename]

數據庫內核版本20210326之前可以使用以下語法的語句:

VACUUM REINDEX [tablename]

當您對一張表執行過SORT或者MULTISORT之后,當前的數據會組織為按照排序鍵全表有序,但隨著表中不斷寫入新數據,未排序的部分就會不斷增加,這將有可能影響粗糙集過濾的性能。因此您需要周期性地執行SORT或者VACUUM REINDEXMULTISORT操作來對表進行重排序,從而保證粗糙集過濾的性能。

修改排序鍵

您可以根據業務的變化修改已經創建的列存表的排序鍵,命令語法如下:

ALTER [[GLOBAL | LOCAL] {TEMPORARY | TEMP}] TABLE table_name SET ORDER BY (column, [ ... ] )

這個命令只會修改catalog,不會對數據立即排序,需要重新執行SORT table_name命令排序。

樣例

ALTER TABLE test SET ORDER BY(high,low);

數據庫內核版本20210326之前可以使用以下語法的語句:

ALTER TABLE test SET SORTKEY(high,low);

如何選擇排序鍵和排序方式

當您的查詢SQL經常包含某一個列或者某幾個列的等值或者范圍限定條件查詢時,比如時間列等,可以考慮使用這些列作為排序鍵,從而利用數據排序并結合粗糙索引,加速這類SQL的查詢速度。

一般情況下建議使用組合排序,因為多維排序在排序過程中還需要做一些額外的數據組織工作,多維排序VACUUM REINDEX的時間會長于組合排序VACUUM SORT ONLY的時間。

如果您的查詢SQL包含的限定條件經常不是總是包含某些列的,可以使用多維排序來加速查詢。多維排序最多支持8列。

組合排序和多維排序的性能對比

我們會對同一張表分別做組合排序和多維排序,從而比較兩種排序方式在不同的場景下,對不同查詢的性能影響。

在這個場景中,我們創建一張表test,其包含4列(id, num1, num2, value)。使用(id,num1,num2)作為排序鍵。這張表一共包含一千萬條記錄。對于ADBPG來說并不算是一張特別大的表,但是其可以顯示出組合排序和多維排序的性能差異,在更大的數據集中,兩者的性能差異也會更明顯。

測試步驟:
  1. 創建測試表并設置表的排序鍵。
  2. 寫入測試數據。
  3. 分別對這張表做組合排序和多維排序。
  4. 對比同樣的SQL場景,組合排序和多維排序的點查性能。
  5. 對比同樣的SQL場景,組合排序和多維排序的范圍查詢性能。
創建測試表并設置表的排序鍵
CREATE TABLE test(id int, num1 int, num2 int, value varchar) 
with(APPENDONLY=TRUE, ORIENTATION=column)
DISTRIBUTED BY(id)
ORDER BY(id, num1, num2);

CREATE TABLE test_multi(id int, num1 int, num2 int, value varchar) 
with(APPENDONLY=TRUE, ORIENTATION=column)
DISTRIBUTED BY(id)
ORDER BY(id, num1, num2);
寫入一千萬行數據
INSERT INTO test(id, num1, num2, value) select g, 
(random()*10000000)::int, 
(random()*10000000)::int,
(array['foo', 'bar', 'baz', 'quux', 'boy', 'girl', 'mouse', 'child', 'phone'])[floor(random() * 10 +1)]
FROM generate_series(1, 10000000) as g;

INSERT INTO test_multi SELECT * FROM test;

adbpgadmin=# SELECT count(*) FROM test;
  count
----------
 10000000
(1 row)

adbpgadmin=# SELECT count(*) FROM test_multi;
  count
----------
 10000000
(1 row)
對兩張表分別進行組合排序和多維排序
SORT test;
MULTISORT test_multi;
點查詢比較性能
  • 包含首列排序鍵限定條件。
    -- Q1 包含首列限定條件
    select * from test where id = 100000;
    select * from test_multi where id = 100000;
  • 包含第二列限定條件。
    -- Q2 包含第二列限定條件
    select * from test where num1 = 8766963;
    select * from test_multi where num1 = 8766963;
  • 包含二三列限定條件。
    -- Q3 包含二三列限定條件
    select * from test where num1 = 100000 and num2=2904114;
    select * from test_multi where num1 = 100000 and num2=2904114;
表 1. 性能對比結果
排序方式 Q1 Q2 Q3
組合排序 0.026s 3.95s 4.21s
多維排序 0.55s 0.42s 0.071s
范圍查詢比較性能
  • 包含首列排序鍵限定條件。
    -- Q1 包含首列限定條件
    select count(*) from test where id>5000 and id < 100000;
    select count(*) from test_multi where id>5000 and id < 100000;
  • 包含第二列限定條件。
    -- Q2 包含第二列限定條件
    select count(*) from test where num1 >5000 and num1 <100000;
    select count(*) from test_multi where num1 >5000 and num1 <100000;
  • 包含二三列限定條件。
    -- Q3 包含二三列限定條件
    select count(*) from test where num1 >5000 and num1 <100000; and num2 < 100000;
    select count(*) from test_multi where num1 >5000 and num1 <100000 and num2 < 100000;
表 2. 性能對比結果
排序方式 Q1 Q2 Q3
組合排序 0.07s 3.35s 3.64s
多維排序 0.44s 0.28s 0.047s
結論
  • 對于Q1場景,由于包含排序鍵的首列,所以組合排序的效果非常好,而多維排序則會相對性能弱一些。
  • 對于Q2場景,由于不包含排序鍵的首列,組合排序基本上失效了,而多維排序依然能維持比較穩定的性能提升。
  • 對于Q3場景,由于不包含排序鍵的首列,組合排序依然起不到很好的效果,并且由于比較條件的增加,需要額外的比較開銷,時間更長,而多維排序表現出更好的性能,這是因為在查詢時,限定條件包含的多維排序鍵越多,性能越好。