Doris數據模型
在 Doris 中,數據以表(Table)的形式進行邏輯上的描述。 一張表包括行(Row)和列(Column)。Row 即用戶的一行數據。Column 用于描述一行數據中不同的字段。
Column 可以分為兩大類:Key 和 Value。從業務角度看,Key 和 Value 可以分別對應維度列和指標列。Doris的key列是建表語句中指定的列,建表語句中的關鍵字'unique key'或'aggregate key'或'duplicate key'后面的列就是 Key 列,除了 Key 列剩下的就是 Value 列。
Doris的數據模型一共有Aggregate、Unique、Duplicate(默認)三類,分別對應建表語句指定'aggregate key'、'unique key'、'duplicate key'。
一、Aggregate數據模型
Aggregate 模型可以通過預聚合,極大地降低聚合查詢時所需掃描的數據量和查詢的計算量。聚合模型的特點就是將表中的列分為了Key和Value兩種。 Key 就是數據的維度列,比如時間,地區等等。 Value 則是數據的指標列,比如點擊量,花費等。
支持的聚合類型如下:
- SUM:求和,多行的 Value 進行累加。
- REPLACE:替代,下一批數據中的 Value 會替換之前導入過的行中的 Value。
- MAX:保留最大值。
- MIN:保留最小值。
- REPLACE_IF_NOT_NULL:非空值替換。和 REPLACE 的區別在于對于null值,不做替換。
- HLL_UNION:HLL 類型的列的聚合方式,通過 HyperLogLog 算法聚合。
- BITMAP_UNION:BIMTAP 類型的列的聚合方式,進行位圖的并集聚合。
1、示例:
現有數據表結構如下
|
ColumnName |
Type |
AggregationType |
Comment |
|
ser_month |
varchar(6) |
|
月份 |
|
latn_id |
INT |
|
本地網標識 |
|
latn_name |
VARCHAR(30) |
|
本地網名稱 |
|
telecom_area_id |
INT |
|
區局標識 |
|
telecom_area_name |
VARCHAR(50) |
|
區局名稱 |
|
user_count |
INT |
SUM |
5G合約本月新增用戶數 |
|
last_update_date |
DATE |
REPLACE |
最后更新日期 |
- 創建聚合表
CREATE TABLE IF NOT EXISTS ctyun_liucy.user_count_info
(
`ser_month` varchar(6) COMMENT "月份",
`latn_id` INT COMMENT "本地網標識",
`latn_name` varchar(30) COMMENT "本地網名稱",
`telecom_area_id` INT COMMENT "區局標識",
`telecom_area_name` varchar(50) COMMENT "區局名稱",
`user_count` INT SUM DEFAULT "0" COMMENT "5G合約本月新增用戶數",
`last_update_date` DATE REPLACE COMMENT "最后更新日期"
)
AGGREGATE KEY(`ser_month`, `latn_id`, `latn_name`,`telecom_area_id`,`telecom_area_name`)
DISTRIBUTED BY HASH(`ser_month`, `latn_id`) BUCKETS 3
PROPERTIES (
"replication_allocation" = "tag.location.default:3"
);
- 插入數據
insert into ctyun_liucy.user_count_info (ser_month,latn_id,latn_name,telecom_area_id,telecom_area_name,user_count,last_update_date) values
('202401','1001','武漢','100101','洪山區','21','20240110'),
('202401','1005','宜昌','100511','西陵區','13','20240110'),
('202401','1009','十堰','100905','竹山縣','8','20240110'),
('202401','1001','武漢','100101','洪山區','33','20240111'),
('202401','1001','武漢','100101','洪山區','10','20240112'),
('202401','1009','十堰','100908','茅箭區','6','20240112');
- 查詢數據
可以看到user_count字段根據AGGREGATE KEY做了SUM操作,last_update_date以最后一條記錄進行了更新。
2、應用場景:
Aggregate 模型可以通過預聚合,極大地降低聚合查詢時所需掃描的數據量和查詢的計算量,非常
適合有固定模式的報表類查詢場景。
3、實現原理:
Doris 采用了類似于 LSM-Tree 的數據寫入模型,將數據以追加的方式順序寫入磁盤,實現了寫優化。這樣可以提高系統的寫入性能,但是也帶來了讀取時的額外開銷。為了處理多次寫入造成的數據變化,Doris 引入后臺compaction進行優化。
聚合模型可能發生合并(compaction)的階段有兩個,不同批次導入的數據會根據key發生一次聚合合并,另外一個階段是當后臺數據合并未完成,但是進行了查詢,查詢時時會將數據按key進行合并,保證查詢的結果正確。
注意:select count(*)在doris中非常耗時,可以直接某一個key列進行計數 。但是在聚合模型查出的結果不一定正確,因為此時可能并未聚合,所以聚合模型時盡量減少count操作,相關操作結果可能跟自己想要的結果存在偏差。
二、 Unique數據模型
在某些多維分析場景下,用戶更關注的是如何保證 Key 的唯一性(如數據更新需求),即如何獲得 Primary Key 唯一性約束。 因此,Doris引入了 Unique 數據模型。Unique模型能夠保證Key的唯一性,當用戶更新一條數據時,新寫入的數據會覆蓋具有相同key的舊數據。
Unique模型提供了兩種實現方式:
- 讀時合并(merge-on-read)。在讀時合并實現中,用戶在進行數據寫入時不會觸發任何數據去重相關的操作,所有數據去重的操作都在查詢或者compaction時進行。因此,讀時合并的寫入性能較好,查詢性能較差,同時內存消耗也較高。
- 寫時合并(merge-on-write)。在1.2版本中,我們引入了寫時合并實現,該實現會在數據寫入階段完成所有數據去重的工作,因此能夠提供非常好的查詢性能。
1、讀時合并模型
讀時合并的Unique數據模型是Aggregate數據模型的一個特例,它的底層實現原理與聚合模型一直,合并操作發生在compaction和查詢時合并。
2、寫時合并模型
Unique模型的寫時合并實現,查詢性能更接近于duplicate模型,在有主鍵約束需求的場景上相比聚合模型有較大的查詢性能優勢,尤其是在聚合查詢以及需要用索引過濾大量數據的查詢中。
在開啟了寫時合并選項的Unique表上,數據在導入階段就會去將被覆蓋和被更新的數據進行標記刪除,同時將新的數據寫入新的文件。在查詢的時候, 所有被標記刪除的數據都會在文件級別被過濾掉,讀取出來的數據就都是最新的數據,消除掉了讀時合并中的數據聚合過程,并且能夠在很多情況下支持多種謂詞的下推。因此在許多場景都能帶來比較大的性能提升,尤其是在有聚合查詢的情況下。
3、Unique寫時合并模型數據更新
Unique Key模型目前僅支持在Merge-on-Write實現上進行列更新
用戶通過正常的導入方式將一部分列的數據寫入Doris的Memtable,此時Memtable中并沒有整行數據,在Memtable下刷的時候,會查找歷史數據,用歷史數據補齊一整行,并寫入數據文件中,同時將歷史數據文件中相同key的數據行標記刪除
當出現并發導入時,Doris會利用MVCC機制來保證數據的正確性。如果兩批數據導入都更新了一個相同key的不同列,則其中系統版本較高的導入任務會在版本較低的導入任務成功后,使用版本較低的導入任務寫入的相同key的數據行重新進行補齊
使用建議:
- 對寫入性能要求較高,查詢性能要求較低的用戶,建議使用Aggregate Key模型
- 對查詢性能要求較高,對寫入性能要求不高(例如數據的寫入和更新基本都在凌晨低峰期完成),或者寫入頻率不高的用戶,建議使用Unique Key模型merge-on-write實現
由于Merge-on-Write實現需要在數據寫入的時候,進行整行數據的補齊,以保證最優的查詢性能,因此使用Merge-on-Write實現進行部分列更新會有較為明顯的導入性能下降。
寫入性能優化建議:
- 使用配備了NVMe的SSD,或者極速SSD云盤。因為補齊數據時會大量的讀取歷史數據,產生較高的讀IOPS,以及讀吞吐
- 開啟行存將能夠大大減少補齊數據時產生的IOPS,導入性能提升明顯,用戶可以在建表時通過如下property來開啟行存:
"store_row_column" = "true"
讀時合并的Unique模型在寫入過程中不做任何額外處理,所以寫入性能不受影響,與普通的數據導入相同。但是在查詢時進行聚合的代價較大,典型的聚合查詢性能相比Unique Key模型的Merge-on-Write實現會有5-10倍的下降。
三、 Duplicate數據模型
在某些多維分析場景下,數據既沒有主鍵,也沒有聚合需求。因此,我們引入 Duplicate 數據模型來滿足這類需求。
CREATE TABLE
`dsj_2024_kmh_fj_hz1` (
`lx` varchar(200) NOT NULL,
`cty_flg` varchar(20) NULL,
`region_id` bigint(20) NULL,
`region_name` text NULL,
`group_region_id` bigint(20) NULL,
`group_region_name` text NULL,
`val_d` bigint(20) NULL,
`val_d1` bigint(20) NULL,
`val_l` bigint(20) NULL,
`date_id` bigint(20) NULL
) ENGINE = OLAP DUPLICATE KEY(`lx`) COMMENT 'OLAP' DISTRIBUTED BY HASH(`date_id`) BUCKETS 30 PROPERTIES (
"replication_allocation" = "tag.location.default: 3",
"is_being_synced" = "false",
"storage_format" = "V2",
"light_schema_change" = "true",
"disable_auto_compaction" = "false",
"enable_single_replica_compaction" = "false"
);
這種數據模型區別于 Aggregate 和 Unique 模型。數據完全按照導入文件中的數據進行存儲,不會有任何聚合。即使兩行數據完全相同,也都會保留。 而在建表語句中指定的 DUPLICATE KEY,只是用來指明底層數據按照那些列進行排序。(更貼切的名稱應該為 “Sorted Column”, 這里取名 “DUPLICATE KEY” 只是用以明確表示所用的數據模型。)。在 DUPLICATE KEY 的選擇上,我們建議適當的選擇前 2-4 列就可以。
對于實際使用上,因為我們很多表是從Hive遷移過來,Hive中的表也沒有聚合、去重相關概念,因此Hive導入的數據大部分表都可以選用該模型進行創建。
- 無排序鍵Duplicate數據模型
當創建表的時候沒有指定Unique、Aggregate或Duplicate時,會默認創建一個Duplicate模型的表,并自動指定排序列。
當用戶并沒有排序需求的時候,可以通過在表屬性中配置:
"enable_duplicate_without_keys_by_default" = "true"
創建明細模型的時候,就會不再指定排序列,也不會給該表創建前綴索引,以此減少在導入和存儲上額外的開銷。