Doris數據表設計

數據模型

本文主要從邏輯層面,描述Doris的數據模型,以幫助用戶更好的使用 Doris 應對不同的業務場景。

基本概念

在 Doris 中,數據以表(Table)的形式進行邏輯上的描述。 一張表包括行(Row)和列(Column)。Row 即用戶的一行數據。Column 用於描述一行數據中不同的字段。

Column 可以分爲兩大類:Key 和 Value。從業務角度看,Key 和 Value 可以分別對應維度列和指標列

Doris的數據模型主要分爲3類:

  • Aggregate
  • Unique
  • Duplicate

下面我們分別介紹

Aggregate

ColumnName Type AggregationType Comment
user_id LARGEINT 用戶id
date DATE 數據灌入日期
city VARCHAR(20) 用戶所在城市
age SMALLINT 用戶年齡
sex TINYINT 用戶性別
last_visit_date DATETIME REPLACE 用戶最後一次訪問時間
cost BIGINT SUM 用戶總消費
max_dwell_time INT MAX 用戶最大停留時間
min_dwell_time INT MIN 用戶最小停留時間

轉換爲SQL語句如下:

CREATE TABLE IF NOT EXISTS example_db.example_tbl
(
    `user_id` LARGEINT NOT NULL COMMENT "用戶id",
    `date` DATE NOT NULL COMMENT "數據灌入日期時間",
    `city` VARCHAR(20) COMMENT "用戶所在城市",
    `age` SMALLINT COMMENT "用戶年齡",
    `sex` TINYINT COMMENT "用戶性別",
    `last_visit_date` DATETIME REPLACE DEFAULT "1970-01-01 00:00:00" COMMENT "用戶最後一次訪問時間",
    `cost` BIGINT SUM DEFAULT "0" COMMENT "用戶總消費",
    `max_dwell_time` INT MAX DEFAULT "0" COMMENT "用戶最大停留時間",
    `min_dwell_time` INT MIN DEFAULT "99999" COMMENT "用戶最小停留時間"
)
AGGREGATE KEY(`user_id`, `date`, `city`, `age`, `sex`)
DISTRIBUTED BY HASH(`user_id`) BUCKETS 1
PROPERTIES (
"replication_allocation" = "tag.location.default: 1"
);

當我們插入兩次這條數據:

INSERT INTO example_tbl(user_id,DATE,city,age,sex,last_visit_date,cost,max_dwell_time,min_dwell_time)
VALUES(10004,'2017/10/03','深圳',35,0,'2019/10/03 10:00:00',20,9,9)

得到的結果是這樣的:

可以看出cost字段對兩條數據進行了求和。而求和的依據是根據相同的key來的。

Unique模型

在某些多維分析場景下,用戶更關注的是如何保證 Key 的唯一性,即如何獲得 Primary Key 唯一性約束。因此,我們引入了Unique的數據模型。該模型本質上是聚合模型的一個特例,也是一種簡化的表結構表示方式。

也舉例說明,我們新建一個用戶基礎信息表。這類數據沒有聚合需求,只需保證主鍵唯一性。

建表語句如下:

CREATE TABLE IF NOT EXISTS example_tb2
(
    `user_id` LARGEINT NOT NULL COMMENT "用戶id",
    `username` VARCHAR(50) NOT NULL COMMENT "用戶暱稱",
    `city` VARCHAR(20) COMMENT "用戶所在城市",
    `age` SMALLINT COMMENT "用戶年齡",
    `sex` TINYINT COMMENT "用戶性別",
    `phone` LARGEINT COMMENT "用戶電話",
    `address` VARCHAR(500) COMMENT "用戶地址",
    `register_time` DATETIME COMMENT "用戶註冊時間"
)
UNIQUE KEY(`user_id`, `username`)
DISTRIBUTED BY HASH(`user_id`) BUCKETS 1
PROPERTIES (
"replication_allocation" = "tag.location.default: 1"
);

這個模型名字叫Unique模型,但跟Mysql中的唯一約束不同。Mysql中同一個key多次插入會報錯,而這個模型同一個key插入,後面插入的總是會覆蓋之前的。相當於聚合模型(Aggregate)的字段都是REPLACE類型。

Duplicate模型

在某些多維分析場景下,數據既沒有主鍵,也沒有聚合需求。因此,我們引入 Duplicate 數據模型來滿足這類需求。舉例說明:

CREATE TABLE IF NOT EXISTS example_tb3
(
    `timestamp` DATETIME NOT NULL COMMENT "日誌時間",
    `type` INT NOT NULL COMMENT "日誌類型",
    `error_code` INT COMMENT "錯誤碼",
    `error_msg` VARCHAR(1024) COMMENT "錯誤詳細信息",
    `op_id` BIGINT COMMENT "負責人id",
    `op_time` DATETIME COMMENT "處理時間"
)
DUPLICATE KEY(`timestamp`, `type`, `error_code`)
DISTRIBUTED BY HASH(`type`) BUCKETS 1
PROPERTIES (
"replication_allocation" = "tag.location.default: 1"
);

此種模式下,插入兩條完全一樣的數據會保留2條數據。而在建表語句中指定的 DUPLICATE KEY,只是用來指明底層數據按照那些列進行排序

INSERT INTO example_tb3 (TIMESTAMP,TYPE,error_code,error_msg,op_id,op_time) VALUES('2022-12-04 00:00:00',1,1,'重複數據',3,'2022-12-05 00:00:00');

聚合模型的侷限性

Aggregate模型的侷限性

在聚合模型中,模型對外展現的就是最終聚合後的數據。也就是說任何還未聚合的數據必須通過某種方式來保證對外展示的一致性。

ColumnName Type AggregationType Comment
user_id LARGEINT 用戶id
date DATE 數據灌入日期
cost BIGINT SUM 用戶總消費

假設導入兩批數據:

我們再去查詢,查詢到的結果一定是聚合以後的,如user10001 date:2017-11-20這個數據的cost應該是51。這是doris在查詢引擎中加入了聚合算子,來保證數據對外的一致性。

這種一致性的保證,就讓某些查詢效率很低。如:

SELECT COUNT(*) FROM table;

如果是mysql這種,它只需要對掃描某一列數據就能獲得count值,但是在Doris的聚合模型中,開銷就很大。他如果只掃描user_id列的話,不加查詢聚合結果爲5,加上聚合結果爲3,都是不對的,正確結果應該爲4.因爲它要想正確的結果必須同時取user_id、date這兩列數據,再加上查詢時聚合,才能返回正確的結果。

因此,當業務上有頻繁的 count(* )查詢時,我們建議用戶通過增加一個值恆爲 1 的,聚合類型爲 REPLACE的列來模擬 count(*)

ColumnName Type AggregationType Comment
user_id LARGEINT 用戶id
date DATE 數據灌入日期
cost BIGINT SUM 用戶總消費
count BIGINT REPLACE 用於計算count

這樣的話,通過這個語句就能得到之前count(*)的結果

select sum(count) from table; 

數據模型選擇的建議

數據模型在建表時就已經確定,且無法修改。所以,選擇一個合適的數據模型非常重要。

  1. Aggregate 模型可以通過預聚合,極大地降低聚合查詢時所需掃描的數據量和查詢的計算量,非常適合有固定模式的報表類查詢場景。但是該模型對 count(*) 查詢很不友好。同時因爲固定了 Value 列上的聚合方式,在進行其他類型的聚合查詢時,需要考慮語意正確性。

  2. Unique 模型針對需要唯一主鍵約束的場景,可以保證主鍵唯一性約束。但是無法利用 ROLLUP 等預聚合帶來的查詢優勢(因爲本質是 REPLACE,沒有 SUM 這種聚合方式)。

  • 【注意】Unique 模型僅支持整行更新,如果用戶既需要唯一主鍵約束,又需要更新部分列(例如將多張源表導入到一張 doris 表的情形),則可以考慮使用 Aggregate 模型,同時將非主鍵列的聚合類型設置爲 REPLACE_IF_NOT_NULL。具體的用法可以參考語法手冊
  1. Duplicate 適合任意維度的 Ad-hoc 查詢。雖然同樣無法利用預聚合的特性,但是不受聚合模型的約束,可以發揮列存模型的優勢(只讀取相關列,而不需要讀取所有 Key 列)
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章