本文檔主要從邏輯層面描述 Doris 的數據模型以幫助用戶更好的使用 Doris 應對不一樣的業務場景。sql
基本概念
在 Doris 中,數據以表(Table)的形式進行邏輯上的描述。 一張表包括行(Row)和列(Column)。Row 即用戶的一行數據。Column 用於描述一行數據中不一樣的字段。數據庫
Column 能夠分爲兩大類:Key 和 Value。從業務角度看,Key 和 Value 能夠分別對應維度列和指標列。日誌
Doris 的數據模型主要分爲3類:code
- Duplicate 明細模型
- Aggregate 聚合模型
- Unique 惟一主鍵模型
下面咱們分別介紹。排序
Duplicate 明細模型
明細模型是 Doris 默認使用的數據模型。該數據模型不會對導入的數據進行任何處理。表中的數據即用戶導入的原始數據。索引
ColumnName | Type | SortKey | Comment |
---|---|---|---|
timestamp | DATETIME | Yes | 日誌時間 |
type | INT | Yes | 日誌類型 |
error_code | INT | Yes | 錯誤碼 |
error_msg | VARCHAR(1024) | No | 錯誤詳細信息 |
op_id | BIGINT | No | 負責人id |
op_time | DATETIME | No | 處理時間 |
建表語句以下:ci
CREATE TABLE IF NOT EXISTS example_db.expamle_tbl ( `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`) ... /* 省略 Partition 和 Distribution 信息 */ ;
建表語句中指定的 DUPLICATE KEY,只是用來指明底層數據按照那些列進行排序。(更貼切的名稱應該爲 「Sorted Column」,這裏取名 「DUPLICATE KEY」 只是用以明確表示所用的數據模型。關於 「Sorted Column」的更多解釋,索引文檔)。在 DUPLICATE KEY 的選擇上,咱們建議適當的選擇前 2-4 列就能夠。文檔
這種數據模型適用於既沒有聚合需求,又沒有主鍵惟一性約束的原始數據的存儲。同時,用戶也能夠經過物化視圖功能功能在這種模型基礎上創建聚合視圖,所以是一種比較推薦的數據模型。it
Aggregate 聚合模型
聚合模型須要用戶在建表時顯式的將列分爲 Key 列和 Value 列。該模型會自動的對 Key 相同的行,在 Value 列上進行聚合操做。io
咱們以實際的例子來講明什麼是聚合模型,以及如何正確的使用聚合模型。
示例1:導入數據聚合
假設業務有以下數據表模式:
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 | 用戶最小停留時間 |
若是轉換成建表語句則以下(省略建表語句中的 Partition 和 Distribution 信息)
CREATE TABLE IF NOT EXISTS example_db.expamle_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`, `timestamp`, `city`, `age`, `sex`) ... /* 省略 Partition 和 Distribution 信息 */ ;
能夠看到,這是一個典型的用戶信息和訪問行爲的事實表。 在通常星型模型中,用戶信息和訪問行爲通常分別存放在維度表和事實表中。這裏咱們爲了更加方便的解釋 Doris 的數據模型,將兩部分信息統一存放在一張表中。
表中的列按照是否設置了 AggregationType
,分爲 Key (維度列) 和 Value(指標列)。沒有設置 AggregationType
的,如 user_id
、date
、age
... 等稱爲 Key,而設置了 AggregationType
的稱爲 Value。
當咱們導入數據時,對於 Key 列相同的行會聚合成一行,而 Value 列會按照設置的 AggregationType
進行聚合。 AggregationType
目前有如下四種聚合方式:
- SUM:求和,多行的 Value 進行累加。
- REPLACE:替代,下一批數據中的 Value 會替換以前導入過的行中的 Value。
- MAX:保留最大值。
- MIN:保留最小值。
假設咱們有如下導入數據(原始數據):
user_id | date | city | age | sex | last_visit_date | cost | max_dwell_time | min_dwell_time |
---|---|---|---|---|---|---|---|---|
10000 | 2017-10-01 | 北京 | 20 | 0 | 2017-10-01 06:00:00 | 20 | 10 | 10 |
10000 | 2017-10-01 | 北京 | 20 | 0 | 2017-10-01 07:00:00 | 15 | 2 | 2 |
10001 | 2017-10-01 | 北京 | 30 | 1 | 2017-10-01 17:05:45 | 2 | 22 | 22 |
10002 | 2017-10-02 | 上海 | 20 | 1 | 2017-10-02 12:59:12 | 200 | 5 | 5 |
10003 | 2017-10-02 | 廣州 | 32 | 0 | 2017-10-02 11:20:00 | 30 | 11 | 11 |
10004 | 2017-10-01 | 深圳 | 35 | 0 | 2017-10-01 10:00:15 | 100 | 3 | 3 |
10004 | 2017-10-03 | 深圳 | 35 | 0 | 2017-10-03 10:20:22 | 11 | 6 | 6 |
咱們假設這是一張記錄用戶訪問某商品頁面行爲的表。咱們以第一行數據爲例,解釋以下:
數據 | 說明 |
---|---|
10000 | 用戶id,每一個用戶惟一識別id |
2017-10-01 | 數據入庫時間,精確到日期 |
北京 | 用戶所在城市 |
20 | 用戶年齡 |
0 | 性別男(1 表明女性) |
2017-10-01 06:00:00 | 用戶本次訪問該頁面的時間,精確到秒 |
20 | 用戶本次訪問產生的消費 |
10 | 用戶本次訪問,駐留該頁面的時間 |
10 | 用戶本次訪問,駐留該頁面的時間(冗餘) |
那麼當這批數據正確導入到 Doris 中後,Doris 中最終存儲以下:
user_id | date | city | age | sex | last_visit_date | cost | max_dwell_time | min_dwell_time |
---|---|---|---|---|---|---|---|---|
10000 | 2017-10-01 | 北京 | 20 | 0 | 2017-10-01 07:00:00 | 35 | 10 | 2 |
10001 | 2017-10-01 | 北京 | 30 | 1 | 2017-10-01 17:05:45 | 2 | 22 | 22 |
10002 | 2017-10-02 | 上海 | 20 | 1 | 2017-10-02 12:59:12 | 200 | 5 | 5 |
10003 | 2017-10-02 | 廣州 | 32 | 0 | 2017-10-02 11:20:00 | 30 | 11 | 11 |
10004 | 2017-10-01 | 深圳 | 35 | 0 | 2017-10-01 10:00:15 | 100 | 3 | 3 |
10004 | 2017-10-03 | 深圳 | 35 | 0 | 2017-10-03 10:20:22 | 11 | 6 | 6 |
能夠看到,用戶 10000 只剩下了一行聚合後的數據。而其他用戶的數據和原始數據保持一致。這裏先解釋下用戶 10000 聚合後的數據:
前5列沒有變化,從第6列 last_visit_date
開始:
-
2017-10-01 07:00:00
:由於last_visit_date
列的聚合方式爲 REPLACE,因此2017-10-01 07:00:00
替換了2017-10-01 06:00:00
保存了下來。注:在同一個導入批次中的數據,對於 REPLACE 這種聚合方式,替換順序不作保證。如在這個例子中,最終保存下來的,也有多是
2017-10-01 06:00:00
。而對於不一樣導入批次中的數據,能夠保證,後一批次的數據會替換前一批次。 -
35
:由於cost
列的聚合類型爲 SUM,因此由 20 + 15 累加得到 35。 -
10
:由於max_dwell_time
列的聚合類型爲 MAX,因此 10 和 2 取最大值,得到 10。 -
2
:由於min_dwell_time
列的聚合類型爲 MIN,因此 10 和 2 取最小值,得到 2。
通過聚合,Doris 中最終只會存儲聚合後的數據。換句話說,即明細數據會丟失,用戶不可以再查詢到聚合前的明細數據了。
示例2:保留明細數據
接示例1,咱們將表結構修改以下:
ColumnName | Type | AggregationType | Comment |
---|---|---|---|
user_id | LARGEINT | 用戶id | |
date | DATE | 數據灌入日期 | |
timestamp | DATETIME | 數據灌入時間,精確到秒 | |
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 | 用戶最小停留時間 |
即增長了一列 timestamp
,記錄精確到秒的數據灌入時間。
導入數據以下:
user_id | date | timestamp | city | age | sex | last_visit_date | cost | max_dwell_time | min_dwell_time |
---|---|---|---|---|---|---|---|---|---|
10000 | 2017-10-01 | 2017-10-01 08:00:05 | 北京 | 20 | 0 | 2017-10-01 06:00:00 | 20 | 10 | 10 |
10000 | 2017-10-01 | 2017-10-01 09:00:05 | 北京 | 20 | 0 | 2017-10-01 07:00:00 | 15 | 2 | 2 |
10001 | 2017-10-01 | 2017-10-01 18:12:10 | 北京 | 30 | 1 | 2017-10-01 17:05:45 | 2 | 22 | 22 |
10002 | 2017-10-02 | 2017-10-02 13:10:00 | 上海 | 20 | 1 | 2017-10-02 12:59:12 | 200 | 5 | 5 |
10003 | 2017-10-02 | 2017-10-02 13:15:00 | 廣州 | 32 | 0 | 2017-10-02 11:20:00 | 30 | 11 | 11 |
10004 | 2017-10-01 | 2017-10-01 12:12:48 | 深圳 | 35 | 0 | 2017-10-01 10:00:15 | 100 | 3 | 3 |
10004 | 2017-10-03 | 2017-10-03 12:38:20 | 深圳 | 35 | 0 | 2017-10-03 10:20:22 | 11 | 6 | 6 |
那麼當這批數據正確導入到 Doris 中後,Doris 中最終存儲以下:
user_id | date | timestamp | city | age | sex | last_visit_date | cost | max_dwell_time | min_dwell_time |
---|---|---|---|---|---|---|---|---|---|
10000 | 2017-10-01 | 2017-10-01 08:00:05 | 北京 | 20 | 0 | 2017-10-01 06:00:00 | 20 | 10 | 10 |
10000 | 2017-10-01 | 2017-10-01 09:00:05 | 北京 | 20 | 0 | 2017-10-01 07:00:00 | 15 | 2 | 2 |
10001 | 2017-10-01 | 2017-10-01 18:12:10 | 北京 | 30 | 1 | 2017-10-01 17:05:45 | 2 | 22 | 22 |
10002 | 2017-10-02 | 2017-10-02 13:10:00 | 上海 | 20 | 1 | 2017-10-02 12:59:12 | 200 | 5 | 5 |
10003 | 2017-10-02 | 2017-10-02 13:15:00 | 廣州 | 32 | 0 | 2017-10-02 11:20:00 | 30 | 11 | 11 |
10004 | 2017-10-01 | 2017-10-01 12:12:48 | 深圳 | 35 | 0 | 2017-10-01 10:00:15 | 100 | 3 | 3 |
10004 | 2017-10-03 | 2017-10-03 12:38:20 | 深圳 | 35 | 0 | 2017-10-03 10:20:22 | 11 | 6 | 6 |
咱們能夠看到,存儲的數據,和導入數據徹底同樣,沒有發生任何聚合。這是由於,這批數據中,由於加入了 timestamp
列,全部行的 Key 都不徹底相同。也就是說,只要保證導入的數據中,每一行的 Key 都不徹底相同,那麼即便在聚合模型下,Doris 也能夠保存完整的明細數據。
示例3:導入數據與已有數據聚合
接示例1。假設如今表中已有數據以下:
user_id | date | city | age | sex | last_visit_date | cost | max_dwell_time | min_dwell_time |
---|---|---|---|---|---|---|---|---|
10000 | 2017-10-01 | 北京 | 20 | 0 | 2017-10-01 07:00:00 | 35 | 10 | 2 |
10001 | 2017-10-01 | 北京 | 30 | 1 | 2017-10-01 17:05:45 | 2 | 22 | 22 |
10002 | 2017-10-02 | 上海 | 20 | 1 | 2017-10-02 12:59:12 | 200 | 5 | 5 |
10003 | 2017-10-02 | 廣州 | 32 | 0 | 2017-10-02 11:20:00 | 30 | 11 | 11 |
10004 | 2017-10-01 | 深圳 | 35 | 0 | 2017-10-01 10:00:15 | 100 | 3 | 3 |
10004 | 2017-10-03 | 深圳 | 35 | 0 | 2017-10-03 10:20:22 | 11 | 6 | 6 |
咱們再導入一批新的數據:
user_id | date | city | age | sex | last_visit_date | cost | max_dwell_time | min_dwell_time |
---|---|---|---|---|---|---|---|---|
10004 | 2017-10-03 | 深圳 | 35 | 0 | 2017-10-03 11:22:00 | 44 | 19 | 19 |
10005 | 2017-10-03 | 長沙 | 29 | 1 | 2017-10-03 18:11:02 | 3 | 1 | 1 |
那麼當這批數據正確導入到 Doris 中後,Doris 中最終存儲以下:
user_id | date | city | age | sex | last_visit_date | cost | max_dwell_time | min_dwell_time |
---|---|---|---|---|---|---|---|---|
10000 | 2017-10-01 | 北京 | 20 | 0 | 2017-10-01 07:00:00 | 35 | 10 | 2 |
10001 | 2017-10-01 | 北京 | 30 | 1 | 2017-10-01 17:05:45 | 2 | 22 | 22 |
10002 | 2017-10-02 | 上海 | 20 | 1 | 2017-10-02 12:59:12 | 200 | 5 | 5 |
10003 | 2017-10-02 | 廣州 | 32 | 0 | 2017-10-02 11:20:00 | 30 | 11 | 11 |
10004 | 2017-10-01 | 深圳 | 35 | 0 | 2017-10-01 10:00:15 | 100 | 3 | 3 |
10004 | 2017-10-03 | 深圳 | 35 | 0 | 2017-10-03 11:22:00 | 55 | 19 | 6 |
10005 | 2017-10-03 | 長沙 | 29 | 1 | 2017-10-03 18:11:02 | 3 | 1 | 1 |
能夠看到,用戶 10004 的已有數據和新導入的數據發生了聚合。同時新增了 10005 用戶的數據。
數據的聚合,在 Doris 中有以下三個階段發生:
- 每一批次數據導入的 ETL 階段。該階段會在每一批次導入的數據內部進行聚合。
- 底層 BE 進行數據 Compaction 的階段。該階段,BE 會對已導入的不一樣批次的數據進行進一步的聚合。
- 數據查詢階段。在數據查詢時,對於查詢涉及到的數據,會進行對應的聚合。
數據在不一樣時間,可能聚合的程度不一致。好比一批數據剛導入時,可能還未與以前已存在的數據進行聚合。可是對於用戶而言,用戶只能查詢到聚合後的數據。即不一樣的聚合程度對於用戶查詢而言是透明的。用戶需始終認爲數據以最終的完成的聚合程度存在,而不該假設某些聚合還未發生。(可參閱聚合模型的侷限性一節得到更多詳情。)
Unique 惟一主鍵模型
在某些多維分析場景下,用戶更關注的是如何保證 Key 的惟一性,即如何得到 Primary Key 惟一性約束。所以,咱們引入了 Unique 的數據模型。該模型本質上是聚合模型的一個特例,也是一種簡化的表結構表示方式。咱們舉例說明。
ColumnName | Type | IsKey | Comment |
---|---|---|---|
user_id | BIGINT | Yes | 用戶id |
username | VARCHAR(50) | Yes | 用戶暱稱 |
city | VARCHAR(20) | No | 用戶所在城市 |
age | SMALLINT | No | 用戶年齡 |
sex | TINYINT | No | 用戶性別 |
phone | LARGEINT | No | 用戶電話 |
address | VARCHAR(500) | No | 用戶住址 |
register_time | DATETIME | No | 用戶註冊時間 |
這是一個典型的用戶基礎信息表。這類數據沒有聚合需求,只需保證主鍵惟一性。(這裏的主鍵爲 user_id + username)。那麼咱們的建表語句以下:
CREATE TABLE IF NOT EXISTS example_db.expamle_tbl ( `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`, `user_name`) ... /* 省略 Partition 和 Distribution 信息 */ ;
而這個表結構,徹底同等於如下使用聚合模型描述的表結構:
ColumnName | Type | AggregationType | Comment |
---|---|---|---|
user_id | BIGINT | 用戶id | |
username | VARCHAR(50) | 用戶暱稱 | |
city | VARCHAR(20) | REPLACE | 用戶所在城市 |
age | SMALLINT | REPLACE | 用戶年齡 |
sex | TINYINT | REPLACE | 用戶性別 |
phone | LARGEINT | REPLACE | 用戶電話 |
address | VARCHAR(500) | REPLACE | 用戶住址 |
register_time | DATETIME | REPLACE | 用戶註冊時間 |
及建表語句:
CREATE TABLE IF NOT EXISTS example_db.expamle_tbl ( `user_id` LARGEINT NOT NULL COMMENT "用戶id", `username` VARCHAR(50) NOT NULL COMMENT "用戶暱稱", `city` VARCHAR(20) REPLACE COMMENT "用戶所在城市", `age` SMALLINT REPLACE COMMENT "用戶年齡", `sex` TINYINT REPLACE COMMENT "用戶性別", `phone` LARGEINT REPLACE COMMENT "用戶電話", `address` VARCHAR(500) REPLACE COMMENT "用戶地址", `register_time` DATETIME REPLACE COMMENT "用戶註冊時間" ) AGGREGATE KEY(`user_id`, `user_name`) ... /* 省略 Partition 和 Distribution 信息 */ ;
即 Unique 模型徹底能夠用聚合模型中的 REPLACE 方式替代。其內部的實現方式和數據存儲方式也徹底同樣。這裏再也不繼續舉例說明。
聚合模型的侷限性
聚合模型(包括 Unique 模型),經過一種預計算的方式來減小查詢時須要實時計算的數據量,加速查詢。可是這種模型會有使用上的侷限性。
在聚合模型中,模型對外展示的,是最終聚合後的數據。也就是說,任何還未聚合的數據(好比說兩個不一樣導入批次的數據),必須經過某種方式,以保證對外展現的一致性。咱們舉例說明。
假設表結構以下:
ColumnName | Type | AggregationType | Comment |
---|---|---|---|
user_id | LARGEINT | 用戶id | |
date | DATE | 數據灌入日期 | |
cost | BIGINT | SUM | 用戶總消費 |
假設存儲引擎中有以下兩個已經導入完成的批次的數據:
batch 1
user_id | date | cost |
---|---|---|
10001 | 2017-11-20 | 50 |
10002 | 2017-11-21 | 39 |
batch 2
user_id | date | cost |
---|---|---|
10001 | 2017-11-20 | 1 |
10001 | 2017-11-21 | 5 |
10003 | 2017-11-22 | 22 |
能夠看到,用戶 10001 分屬在兩個導入批次中的數據尚未聚合。可是爲了保證用戶只能查詢到以下最終聚合後的數據:
user_id | date | cost |
---|---|---|
10001 | 2017-11-20 | 51 |
10001 | 2017-11-21 | 5 |
10002 | 2017-11-21 | 39 |
10003 | 2017-11-22 | 22 |
咱們在查詢引擎中加入了聚合算子,來保證數據對外的一致性。
另外,在聚合列(Value)上,執行與聚合類型不一致的聚合類查詢時,要注意語意。好比咱們在如上示例中執行以下查詢:
SELECT MIN(cost) FROM table;
獲得的結果是 5,而不是 1。
同時,這種一致性保證,在某些查詢中,會極大的下降查詢效率。
咱們以最基本的 count(*) 查詢爲例:
SELECT COUNT(*) FROM table;
在其餘數據庫中,這類查詢都會很快的返回結果。由於在實現上,咱們能夠經過如 「導入時對行進行計數,保存 count 的統計信息」,或者在查詢時 「僅掃描某一列數據,得到 count 值」 的方式,只需很小的開銷,便可得到查詢結果。可是在 Doris 的聚合模型中,這種查詢的開銷很是大。
咱們以剛纔的數據爲例:
batch 1
user_id | date | cost |
---|---|---|
10001 | 2017-11-20 | 50 |
10002 | 2017-11-21 | 39 |
batch 2
user_id | date | cost |
---|---|---|
10001 | 2017-11-20 | 1 |
10001 | 2017-11-21 | 5 |
10003 | 2017-11-22 | 22 |
由於最終的聚合結果爲:
user_id | date | cost |
---|---|---|
10001 | 2017-11-20 | 51 |
10001 | 2017-11-21 | 5 |
10002 | 2017-11-21 | 39 |
10003 | 2017-11-22 | 22 |
因此,select count(*) from table;
的正確結果應該爲 4。但若是咱們只掃描 user_id
這一列,若是加上查詢時聚合,最終獲得的結果是 3(10001, 10002, 10003)。而若是不加查詢時聚合,則獲得的結果是 5(兩批次一共5行數據)。可見這兩個結果都是不對的。
爲了獲得正確的結果,咱們必須同時讀取 user_id
和 date
這兩列的數據,再加上查詢時聚合,才能返回 4 這個正確的結果。也就是說,在 count() 查詢中,Doris 必須掃描全部的 AGGREGATE KEY 列(這裏就是 user_id
和 date
),而且聚合後,才能獲得語意正確的結果。當聚合列很是多時,count() 查詢須要掃描大量的數據。
所以,當業務上有頻繁的 count(*) 查詢時,咱們建議用戶經過增長一個值恆爲 1 的,聚合類型爲 SUM 的列來模擬 count(*)。如剛纔的例子中的表結構,咱們修改以下:
ColumnName | Type | AggregateType | Comment |
---|---|---|---|
user_id | BIGINT | 用戶id | |
date | DATE | 數據灌入日期 | |
cost | BIGINT | SUM | 用戶總消費 |
count | BIGINT | SUM | 用於計算count |
增長一個 count 列,而且導入數據中,該列值恆爲 1。則 select count(*) from table;
的結果等價於 select sum(count) from table;
。然後者的查詢效率將遠高於前者。不過這種方式也有使用限制,就是用戶須要自行保證,不會重複導入 AGGREGATE KEY 列都相同的行。不然,select sum(count) from table;
只能表述原始導入的行數,而不是 select count(*) from table;
的語義。
另外一種方式,就是 將如上的 count
列的聚合類型改成 REPLACE,且依然值恆爲 1。那麼 select sum(count) from table;
和 select count(*) from table;
的結果將是一致的。而且這種方式,沒有導入重複行的限制。
Duplicate 模型
Duplicate 模型沒有聚合模型的這個侷限性。由於該模型不涉及聚合語意,在作 count(*) 查詢時,任意選擇一列查詢,便可獲得語意正確的結果。
數據模型的選擇建議
由於數據模型在建表時就已經肯定,且沒法修改。因此,選擇一個合適的數據模型很是重要。
- Aggregate 模型能夠經過預聚合,極大地下降聚合查詢時所需掃描的數據量和查詢的計算量,很是適合有固定模式的報表類查詢場景。可是該模型對 count(*) 查詢很不友好。同時由於固定了 Value 列上的聚合方式,在進行其餘類型的聚合查詢時,須要考慮語意正確性。
- Unique 模型針對須要惟一主鍵約束的場景,能夠保證主鍵惟一性約束。可是沒法利用 ROLLUP 等預聚合帶來的查詢優點(由於本質是 REPLACE,沒有 SUM 這種聚合方式)。
- Duplicate 適合任意維度的 Ad-hoc 查詢。雖然一樣沒法利用預聚合的特性,可是不受聚合模型的約束,能夠發揮列存模型的優點(只讀取相關列,而不須要讀取全部 Key 列)。