openGauss/MogDB列存表的delta表測試

T1YSL發表於2024-01-02

列儲存格式是OLAP類資料庫系統最常用的資料格式,適合複雜查詢、範圍統計類查詢的線上分析型處理系統。cstore列儲存的主體資料檔案以CU為I/O單元,只支援追加寫操作,因此cstore只有讀共享緩衝區。CU間和CU內的可見性由對應的CUDESE表(astore表)決定,因此其可見性和併發控制原理與行儲存astore基本相同。

對於cstore表的單條插入以及更新操作,提供與每個cstore表對應的delta表(astore行儲存表),來接收單條插入或單條更新的元組,以降低CU檔案的碎片化,如下針對列存表的delta表進行部分測試。

1.1引數配置

資料庫裡開啟enable_delta_store引數。

opengauss=# select * from pg_settings where name like '%delta%';
-[ RECORD 1 ]-------------------------------
name       | enable_delta_store
setting    | on
unit       |
category   | Query Tuning
short_desc | Enable delta for column store.
extra_desc |
context    | postmaster
vartype    | bool
source     | configuration file
min_val    |
max_val    |
enumvals   |
boot_val   | off
reset_val  | on
sourcefile | /opt/mogdb/data/postgresql.conf
sourceline | 803

1.2 建立測試表

建立一張列存表test_cstore和一張行存表test_astore, 列存表test_cstore的deltarow_threshold指定為1000,壓縮級別為預設的low。

opengauss=# create table test_cstore(id int,name varchar(20),insert_time timestamptz not null default now()) with (orientation=column,deltarow_threshold=1000);
CREATE TABLE
opengauss=# create table test_astore(id int,name varchar(20),insert_time timestamptz not null default now()) with (orientation=row);
CREATE TABLE

未知.png

根據系統表查詢到兩張表對應的oid如下,列存表在建立的時候,會對應在cstore這個schema下自動生成兩張表,分別為pg_cudesc_${oid} 和pg_delta_${oid}兩張表,${oid}對應列存表原始表的oid。

opengauss=# select oid,relname from pg_class where relname like '%test_%';
  oid  |   relname
-------+-------------
 25150 | test_cstore
 25315 | test_astore
(2 rows)

image.png

根據函式可以查詢到表在資料目錄下的位置,對應的在系統上的表的物理檔案的名字和表的oid同名,列存表有幾個列,除了原始表,就會多生成幾個${oid}_c$的檔案。

opengauss=#select pg_relation_filepath('test_cstore');
 pg_relation_filepath
----------------------
 base/25149/25150
(1 row)
opengauss=#  select pg_relation_filepath('cstore.pg_cudesc_25150');
 pg_relation_filepath
----------------------
 base/25149/25157
(1 row)
 
opengauss=#  select pg_relation_filepath('cstore.pg_delta_25150');
 pg_relation_filepath
----------------------
 base/25149/25154
(1 row)
omm@ubuntu-linux-22-04-desktop:/opt/mogdb/data$ ll base/25149/25150*
-rw------- 1 omm dbgrp 0 Dec  7 21:45 base/25149/25150
-rw------- 1 omm dbgrp 0 Dec  7 21:45 base/25149/25150_C1.0
-rw------- 1 omm dbgrp 0 Dec  7 21:45 base/25149/25150_C2.0
-rw------- 1 omm dbgrp 0 Dec  7 21:45 base/25149/25150_C3.0
omm@ubuntu-linux-22-04-desktop:/opt/mogdb/data$ ll base/25149/25157*
-rw------- 1 omm dbgrp 0 Dec  7 21:45 base/25149/25157
omm@ubuntu-linux-22-04-desktop:/opt/mogdb/data$ ll base/25149/25154*
-rw------- 1 omm dbgrp 0 Dec  7 21:45 base/25149/25154

如下是行存表的檔案情況。

 pg_relation_filepath
----------------------
 base/25149/25315
(1 row)
 
opengauss=# \q
omm@ubuntu-linux-22-04-desktop:/opt/mogdb/data$ ll base/25149/25178*
-rw------- 1 omm dbgrp 0 Dec  7 21:46 base/25149/25315

1.3    插入資料測試

1.3.1   插入單條資料

1.3.1.1        列存表

從資料庫中看,在原始列存表test_cstore和pg_delta表裡分別有一條記錄。

opengauss=# insert into test_cstore values(1,'test1');
INSERT 0 1
opengauss=# select * from test_cstore;
 id | name  |          insert_time
----+-------+-------------------------------
  1 | test1 | 2023-12-07 22:17:53.571677+08
(1 row)
 
opengauss=# select * from cstore.pg_cudesc_25150;
 col_id | cu_id | min | max | row_count | cu_mode | size | cu_pointer | magic | extra
--------+-------+-----+-----+-----------+---------+------+------------+-------+-------
(0 rows)
 
opengauss=# select * from cstore.pg_delta_25150;
 id | name  |          insert_time
----+-------+-------------------------------
  1 | test1 | 2023-12-07 22:17:53.571677+08
(1 row)

從作業系統上檢視,資料情況如下,資料僅寫入了pg_delta表中。原始列存表中沒有資料。資料庫中讀取的表的資料來自於pg_delta表。

#原始列存表
omm@ubuntu-linux-22-04-desktop:/opt/mogdb/data$ du -sh  base/25149/25150*
0       base/25149/25150
0       base/25149/25150_C1.0
0       base/25149/25150_C2.0
0         base/25149/25150_C3.0
 
#pg_cudesc表
omm@ubuntu-linux-22-04-desktop:/opt/mogdb/data$ du -sh base/25149/25157*
0         base/25149/25157
 
#pg_delta表
omm@ubuntu-linux-22-04-desktop:/opt/mogdb/data$ du -sh  base/25149/25154*
8.0K    base/25149/25154

pg_delta表中的資料按行存方式儲存。

opengauss=# select * from test_cstore;
 id | name  |          insert_time
----+-------+-------------------------------
  1 | test1 | 2023-12-07 22:17:53.571677+08
(1 row) 
opengauss=# select * from cstore.pg_delta_25150;
 id | name  |          insert_time
----+-------+-------------------------------
  1 | test1 | 2023-12-07 22:17:53.571677+08
(1 row)
opengauss=#  select pg_relation_filepath('cstore.pg_delta_25150');
 pg_relation_filepath
----------------------
 base/25149/25154
(1 row)

使用hexdump分析表資料檔案中的內容。

omm@ubuntu-linux-22-04-desktop:/opt/mogdb/data$ hexdump -C base/25149/25184
00000000  00 00 00 00 e0 4d 22 1e  c6 ca 40 00 2c 00 d0 1f  |.....M"...@.,...|
00000010  00 20 06 20 00 00 00 00  67 3a 00 00 00 00 00 00  |. . ....g:......|
00000020  00 00 00 00 00 00 00 00  d0 9f 60 00 00 00 00 00  |..........`.....|
00000030  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*
00001fd0  03 00 00 00 00 00 00 00  0c 00 00 00 00 00 00 00  |................|
00001fe0  01 00 03 00 02 0b 18 00  01 00 00 00 0d 74 65 73  |.............tes|
00001ff0  74 31 00 00 00 00 00 00  5d 3f f1 e6 ea ae 02 00  |t1......]?......|
00002000

image.png

16進位制的01對應的10進位制的1
 
16進位制的74對應的10進位制的116,ascii 碼對應為t
16進位制的65對應的10進位制的101,ascii 碼對應為e
16進位制的73對應的10進位制的115,ascii 碼對應為s
16進位制的74對應的10進位制的115,ascii 碼對應為t
16進位制的31對應的10進位制的49,ascii 碼對應為1
 
5d 3f f1 e6 ea ae 02 00部分應該儲存的是表中的timestamp型別資料,具體轉化方式未深究,待後續分析。

1.3.1.2 行存表

行存表直接按照上述列存表的資料進行一個

opengauss=# insert into test_astore  select * from test_cstore ;                                                                      
INSERT 0 1
opengauss=# select * from test_astore;
 id | name  |          insert_time
----+-------+-------------------------------
  1 | test1 | 2023-12-07 22:17:53.571677+08
(1 row)
 
opengauss=# select pg_relation_filepath('test_astore');
 pg_relation_filepath
----------------------
 base/25149/25315
(1 row)
opengauss=# \q
 
omm@ubuntu-linux-22-04-desktop:/opt/mogdb/data$ du -sh base/25149/25315*
8.0K    base/25149/25315
 
omm@ubuntu-linux-22-04-desktop:/opt/mogdb/data$ du -sh base/25149/25315*
8.0K    base/25149/25315
omm@ubuntu-linux-22-04-desktop:/opt/mogdb/data$ hexdump -C base/25149/25315
00000000  00 00 00 00 28 ed 2e 1e  18 eb 40 00 2c 00 d0 1f  |....(.....@.,...|
00000010  00 20 06 20 00 00 00 00  7d 3a 00 00 00 00 00 00  |. . ....}:......|
00000020  00 00 00 00 00 00 00 00  d0 9f 60 00 00 00 00 00  |..........`.....|
00000030  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*
00001fd0  06 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00001fe0  01 00 03 00 02 09 18 00  01 00 00 00 0d 74 65 73  |.............tes|
00001ff0  74 31 00 00 00 00 00 00  5d 3f f1 e6 ea ae 02 00  |t1......]?......|
00002000

1.3.1.3測試結果

1.經過測試,列存表test_cstore的deltarow_threshold指定為1000,壓縮級別為預設的low。後當插入單條資料,資料條數未達到deltarow_threshold指定值的情況下,系統上檢視物理檔案,可以看出資料僅儲存到了pg_delta表中。
 
2.原始列存表中沒有資料。資料庫中讀取的表的資料來自於pg_delta表。相當於在資料庫層面做了一個對映。pg_delta表和原列存表的資料均取自pg_delta對應的物理檔案。
 
3.產生的pg_delta表的資料儲存方式是行存,它的結構以及物理檔案儲存形式和單純以行存表儲存的資料完全一致。同樣資料的情況下,列存的pg_delta表和普通行存表資料檔案對比如下,資料塊的排布也是完全一致。

image.png

1.3.1.4 其他現象分析

測試過程,做過delete from並做了vacuum full的操作,列存表的原始表的物理檔案位置發生了變化。底層在vacuum full的時候,建立了新的物理檔案,整理完資料後,修改了表對應指向的物理檔案對映。雖然依舊原始表對應的物理檔案中沒有資料,但是由於列存表原表的物理檔案的名字和oid不一致,直接透過名字無法知道對應的cstore下的兩張表是哪兩張。

omm@ubuntu-linux-22-04-desktop:/opt/mogdb/data$ ll base/25149/25181
-rw------- 1 omm dbgrp 0 Dec  8 00:21 base/25149/25181
omm@ubuntu-linux-22-04-desktop:/opt/mogdb/data$ du -sh  base/25149/25181
0       base/25149/25181

image.png

但是可以透過pg_class裡的oid和relfilenode關聯。找到列存表原始表的新物理檔案對應的oid以及其對應的兩張cstore的相關的表。

opengauss=# select pg_relation_filepath('test_cstore');
 pg_relation_filepath
----------------------
 base/25149/25181
(1 row)
opengauss=# select oid,relfilenode from pg_class where relfilenode='25181';
  oid  | relfilenode
-------+-------------
 25150 |       25181
(1 row)
 
opengauss=# \dt cstore.*25150*
                        List of relations
 Schema |      Name       | Type  | Owner |        Storage
--------+-----------------+-------+-------+-----------------------
 cstore | pg_cudesc_25150 | table | omm   | {internal_mask=33031}
 cstore | pg_delta_25150  | table | omm   | {internal_mask=32768}
(2 rows)

image.png

1.3.2   繼續插入999條

1.3.2.1  列存表

在上述的基礎上插入999條資料,湊夠列存表test_cstore的deltarow_threshold的 1000閾值。

insert into test_cstore select generate_series(1,999),left(md5(random()::text),10);

image.png
可以發現1000條資料,達到了deltarow_threshold閾值的情況下,資料依然在資料庫裡原始表和pg_delta表裡能查到條數。
而去系統上看物理檔案,可以看到100條資料依舊存在pg_delta表中。
image.png

1.3.2.2 行存表

依舊和列存插入一樣的資料。
image.png

1.3.2.3測試結果

列存表pg_delta裡的資料在正好達到了deltarow_threshold閾值的時候,並沒有任何反應,pg_delta裡的資料並沒有如預期的轉換成列存形式儲存在cu裡。

1.3.3   繼續插入1條單條資料

在原始列存表test_cstore和pg_delta表裡分別再插入一條記錄。

1.3.3.1 列存表

列存表裡再插入一條,滿足1001條,超過deltarow_threshold閾值,看是否有反應。發現也沒有觸發行轉列。資料依舊儲存在pg_delta表裡。

insert into test_cstore values(1001,'test1001');
select count(*) from test_cstore;
select count(*) from cstore.pg_cudesc_25150;
select count(*) from cstore.pg_delta_25150;

image.png
如下是列存的三張表物理檔案的大小。以及cu檔案的大小
image.png
image.png

1.3.3.2 行存表

行存表預期正常,此處無需做對比。
image.png

1.3.3.3 測試結果

列存表裡再插入一條,滿足1001條,超過deltarow_threshold閾值,看是否有反應。發現也沒有觸發行轉列。資料依舊儲存在pg_delta表裡。

1.3.3.4 手動做一下vacuum deltamerge操作

手動對原始列存表做一次vacuum merge操作之後。
發現pg_delta表裡的資料已經清空了,而pg_cudesc表裡有了四條記錄,但是原始列存表的條數是不變的,此外原始列存表對應的物理檔案也是自始至終不會儲存資料的,資料只存在pg_detla表或者cu對應的列存表裡。
image.png
這個時候查詢
image.png

相當於col_id的1,2,3分別對應三個列。-10這個id對應的的暫時未知。三列分別在對原始列存表做了vacuum DELTAMERGE後,從pg_delta中把資料按列分別放到不同的cu檔案裡。此外,原始的pg_delta表會在做完vacuum DELTAMERGE後,拋棄原來的物理檔案,直接使用新的物理檔案,pg_delta表的relfilenode會發生變化。
 
更換relfilenode,使用新的物理檔案的原因猜想可能是為了併發操作,儘可能減少行列轉換過程,pg_delta表對更新或者插入的影響。可以一邊用舊的檔案進行行轉列,一邊用新的物理檔案繼續追加新的資料到pg_delta表裡,vacuum DELTAMERGE過程申請AccessExclusiveLock。(待驗證)
image.png
image.png
image.png

 
其中,生成的C2檔案內容如下,轉成規範十六進位制+ASCII顯示便於觀察,這一個檔案預期應該儲存第二列name列的資料。
image.png
 
查詢到之前插入的test1001。可以看到它相鄰的都是這一列上對應的資料。列上的資料是堆積在一起的。
image.png
可參照cu檔案的結構進行後續分析。
image.png

1.3.4   單點查詢,資料分別在pg_delta和cu裡區別

870f6110e6的資料在cu裡,test1002的資料在pg_delta表裡,可以看到,沒有索引,僅做單點查詢的情況下,無論資料是在cu表裡還是在pg_delta裡,執行計劃是一樣的,都顯示cstore scan 訪問原始列存表。
image.png
資料庫重啟不會影響資料在cu檔案和pg_delta裡的分佈,也不會進行行列轉換。
image.png

1.3.5 行轉列的最大記錄數限制測試(MAX_BATCHROW)

因為建立表的時候沒有指定MAX_BATCHROW選項,預設為60000,所以在資料載入過程中一個儲存單元可以容納記錄的最大數目是60000。

1.3.5.1 列存表一次性插入60000條資料

一次性插入60000條資料,發現會產生一個行數為60000的cu_id。

image.png
image.png

1.3.5.2 列存表一次性插入61000條資料

而插入61000,則會把61000分成兩個cu_id。一個包含60000行,一個包含1000行。
image.png image.png

1.3.5.3 測試結果

1.一次性插入數條數在進行行轉列的時候,有最大值限制,60000是最大值,超出的會拆分成另一個cu_id,然後把記錄寫入到pg_cudesc裡。
2.這次因為單次的插入值大於deltarow_threshold,直接觸發了行轉列,而不需要手動vacuum deltamerge操作。deltarow_threshol是指定列存表匯入時小於多少行的資料進入delta表,而並不是一個delta中資料累積觸發的,是單次插入的行數閾值。跟單次寫入的行數有關。


來自 “ ITPUB部落格 ” ,連結:https://blog.itpub.net/69990629/viewspace-3002420/,如需轉載,請註明出處,否則將追究法律責任。

相關文章