# 欄位類型 | 數據類型 | 位元組 | 範圍 | | | | | |TINYINT|1 位元組|-2^7 + 1 ~ 2^7 - 1| |SMALLINT|2 位元組|-2^15 + 1 ~ 2^15 - 1| |INT|4 位元組|-2^31 + 1 ~ 2^31 - 1| |BIGINT|8 位元組| ...
欄位類型
數據類型 | 位元組 | 範圍 |
---|---|---|
TINYINT | 1 位元組 | -2^7 + 1 ~ 2^7 - 1 |
SMALLINT | 2 位元組 | -2^15 + 1 ~ 2^15 - 1 |
INT | 4 位元組 | -2^31 + 1 ~ 2^31 - 1 |
BIGINT | 8 位元組 | -2^63 + 1 ~ 2^63 - 1 |
LARGEINT | 16 位元組 | -2^127 + 1 ~ 2^127 - 1 |
FLOAT | 4 位元組 | 支持科學計數法 |
DOUBLE | 12 位元組 | 支持科學計數法 |
DECIMAL[(precision, scale)] | 16 位元組 | 保證精度的小數類型。預設是DECIMAL(10, 0) ,precision: 1 ~ 27 ,scale: 0 ~ 9,其中整數部分為 1 ~ 18,不支持科學計數法 |
DATE | 3 位元組 | 0000-01-01 ~ 9999-12-31 |
DATETIME | 8 位元組 | 0000-01-01 00:00:00 ~ 9999-12-31 23:59:59 |
CHAR[(length)] | 定長字元串。長度範圍:1 ~ 255。預設為 1 | |
VARCHAR[(length)] | 變長字元串。長度範圍:1 ~ 65533 | |
BOOLEAN | 與 TINYINT 一樣,0 代表 false,1 代表 true | |
HLL | 1~16385 個位元組 | hll 列類型,不需要指定長度和預設值,長度根據數據的聚合程度系統內控制,並且 HLL 列只能通過 配套的hll_union_agg、Hll_cardinality、hll_hash 進行查詢或使用 |
BITMAP | bitmap 列類型,不需要指定長度和預設值。表示整型的集合,元素最大支持到 2^64 - 1 | |
STRING | 變長字元串,0.15 版本支持,最大支持 2147483643 位元組(2GB-4),長度還受 be 配置string_type_soft_limit , 實際能存儲的最大長度取兩者最小值。只能用在 value 列,不能用在 key列和分區、分桶列 |
表的基本概念
行和列
一張表包括行(Row)和列(Column);
Row 即用戶的一行數據。Column 用於描述一行數據中不同的欄位。
doris中的列分為兩類:key列和value列
key列在doris中有兩種作用:
聚合表模型中,key是聚合和排序的依據
其他表模型中,key是排序依據
數據表模型
Doris 的數據模型主要分為3類:
• Aggregate 聚合模型
• Unique 唯一模型
• Duplicate 明細模型
Aggregate 模型
是相同key的數據進行自動聚合的表模型。表中的列按照是否設置了 AggregationType
,分為 Key(維度列)和 Value(指標列),沒有設置AggregationType
的稱為 Key,設置了 AggregationType
的稱為 Value。當我們導入數據時,對於 Key 列相同的行會聚合成一行,而 Value 列會按照設置的AggregationType
進行聚合。AggregationType
目前有以下五種聚合方式:
- SUM:求和,多行的 Value 進行累加。
- REPLACE:替代,下一批數據中的 Value 會替換之前導入過的行中的 Value。
- REPLACE_IF_NOT_NULL :當遇到 null 值則不更新。
- MAX:保留最大值。
- MIN:保留最小值。
有如下場景:需要創建一個表,來記錄公司每個用戶的每一次消費行為信息,有如下欄位:
而且,公司對這份數據,特別關心一個報表
每一個用戶最後一次訪問我們頁面的時間,用戶消費的總金額,用戶停留在我們頁面上的最大最小時長
每次要看這個報表,都需要在“明細表”上運行一個統計sql
Select
user_id,data,city,age,gender,
max(visit_data) as last_visit_data,
sum(cost) as cost,
max(dwell_time) as max_dwell_time,
min(dwell_time) as min_dwell_time
From t
Group by user_id,data,city,age,gender -- 對應的是聚合模型型key
聚合模型
-- 這是一個用戶消費和行為記錄的數據表
CREATE TABLE IF NOT EXISTS test.ex_user
(
`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 "用戶最小停留時間"
)
ENGINE=olap
AGGREGATE KEY(`user_id`, `date`, `city`, `age`, `sex`)
-- 分桶 創建表時必須指定分桶 分桶的概念下麵說
DISTRIBUTED BY HASH(`user_id`) BUCKETS 1;
-- 插入數據
insert into test.ex_user values\
(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);
查看數據的時候發現,數據只剩下6條了,就是因為再key相同的時候,將後面的結果聚合了
UNIQUE 模型
是相同key的數據進行自動去重的表模型。在某些多維分析場景下,用戶更關註的是如何保證 Key 的唯一性,即如何獲得 Primary Key 唯一性約束。因此,引入了 Uniq 的數據模型。該模型本質上是聚合模型的一個特例,也是一種簡化的表結構表示方式。
-- 創建表
drop table if exists test.user;
CREATE TABLE IF NOT EXISTS test.user
(
-- key列
`user_id` LARGEINT NOT NULL COMMENT "用戶 id",
`username` VARCHAR(50) NOT NULL COMMENT "用戶昵稱",
-- value列
`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;
-- 插入數據
insert into test.user values\
(10000,'zss','北京',18,0,12345678910,'北京朝陽區 ','2017-10-01 07:00:00'),\
(10000,'zss','北京',19,0,12345678910,'北京順義區 ','2018-10-01 07:00:00'),\
(10000,'lss','北京',20,0,12345678910,'北京海澱區','2017-11-15 06:10:20');
insert into test.user1 values\
(10000,'zss','北京',18,0,12345678910,'北京朝陽區 ','2017-10-01 07:00:00'),\
(10000,'zss','北京',19,0,12345678910,'北京順義區 ','2018-10-01 07:00:00'),\
(10000,'lss','北京',20,0,12345678910,'北京海澱區','2017-11-15 06:10:20');
查詢結果後發現,相同的數據就會被替換掉
Duplicate 模型
就是存明細數據的表模型,既不做聚合也不做去重。在某些多維分析場景下,數據既沒有主鍵,也沒有聚合需求。Duplicate 數據模型可以滿足這類需求。數據完全按照導入文件中的數據進行存儲,不會有任何聚合。即使兩行數據完全相同,也都會保留。 而在建表語句中指定的 DUPLICATE KEY,只是用來指明底層數據按照那些列進行排序。
-- 創建表
CREATE TABLE IF NOT EXISTS test.log_detail
(
`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`) -- key用作排序使用
DISTRIBUTED BY HASH(`timestamp`) BUCKETS 1;
-- 插入數據
insert into test.log_detail values\
('2017-10-01 08:00:05',1,404,'not found page', 101, '2017-10-01 08:00:05'),\
('2017-10-01 08:00:05',1,404,'not found page', 101, '2017-10-01 08:00:05'),\
('2017-10-01 08:00:05',2,404,'not found page', 101, '2017-10-01 08:00:06'),\
('2017-10-01 08:00:06',2,404,'not found page', 101, '2017-10-01 08:00:07');
數據模型的選擇
數據模型在建表時就已經確定,且無法修改;所以,選擇一個合適的數據模型非常重要。
-
Aggregate 模型可以通過預聚合,極大地降低聚合查詢時所需掃描的數據量和查詢的計算量,非常適合有固定模式的報表類查詢場景。
-
Uniq 模型針對需要唯一主鍵約束的場景,可以保證主鍵唯一性約束。但是無法利用 ROLLUP 等預聚合帶來的查詢優勢(因為本質是 REPLACE,沒有 SUM 這種聚合方式)。
-
Duplicate 適合任意維度的查詢。雖然同樣無法利用預聚合的特性,但是不受聚合模型的約束,可以發揮列存模型的優勢(只讀取相關列,而不需要讀取所有 Key 列)
分區與分桶
- partition(分區):是在邏輯上將一張表按行(橫向)劃分
- tablet(又叫bucket,分桶):在物理上對一個分區再按行(橫向)劃分
分區(Partition)
• Partition 列可以指定一列或多列,在聚合模型中,分區列必須為 KEY 列。
• 不論分區列是什麼類型,在寫分區值時,都需要加雙引號。
• 分區數量理論上沒有上限。
• 當不使用 Partition 建表時,系統會自動生成一個和表名同名的,全值範圍的 Partition。該 Partition 對用戶不可見,並且不可刪改。
• 創建分區時不可添加範圍重疊的分區。
Range 分區
單列分區
drop table if exists test.expamle_range_tbl;
CREATE TABLE IF NOT EXISTS test.expamle_range_tbl
(
`user_id` LARGEINT NOT NULL COMMENT "用戶id",
`date` DATE NOT NULL COMMENT "數據灌入日期時間",
`timestamp` DATETIME NOT NULL COMMENT "數據灌入的時間戳",
`city` VARCHAR(20) COMMENT "用戶所在城市",
`age` SMALLINT COMMENT "用戶年齡",
`sex` TINYINT COMMENT "用戶性別"
)
ENGINE=OLAP
DUPLICATE KEY(`user_id`, `date`) -- 表模型
-- 分區的語法
PARTITION BY RANGE(`date`) -- 指定分區類型和分區列
(
-- 指定分區名稱,分區的上界 前閉後開
PARTITION `p201701` VALUES LESS THAN ("2017-02-01"),
PARTITION `p201702` VALUES LESS THAN ("2017-03-01"),
PARTITION `p201703` VALUES LESS THAN ("2017-04-01")
)
DISTRIBUTED BY HASH(`user_id`) BUCKETS 1;
• 分區列通常為時間列,以方便的管理新舊數據。
• Partition 支持通過 VALUES LESS THAN (...) 僅指定上界,系統會將前一個分區的上界作為該分區的下界,生成一個左閉右開的區間。同時,也支持通過 VALUES[...) 指定上下界,生成一個左閉右開的區間。
• 通過 VALUES ...) 同時指定上下界比較容易理解。這裡舉例說明,當使用 VALUES LESS THAN (...) 語句進行分區的增刪操作時,分區範圍的變化情況:
如上 expamle_range_tbl 得建表語句中可以看到,當建表完成後,會自動生成如下3個分區:
-- 查看表中分區得情況
SHOW PARTITIONS FROM test.expamle_range_tbl \G;
-- 增加一個分區
ALTER TABLE test.expamle_range_tbl ADD PARTITION p201705 VALUES LESS THAN ("2017-06-01");
--刪除分區
ALTER TABLE test.expamle_range_tbl DROP PARTITION p201703;
-- 此時的分區狀態如下:
-- p201701: [MIN_VALUE, 2017-02-01)
-- p201702: [2017-02-01, 2017-03-01)
-- p201705: [2017-04-01, 2017-06-01)
-- 出現了一個空洞:[2017-03-01, 2017-04-01)
-- 如果導入的數據範圍在這個空洞範圍內,是無法導入的
多列分區
PARTITION BY RANGE(`date`, `id`) 前閉後開
(
PARTITION `p201701_1000` VALUES LESS THAN ("2017-02-01", "1000"),
PARTITION `p201702_2000` VALUES LESS THAN ("2017-03-01", "2000"),
PARTITION `p201703_all` VALUES LESS THAN ("2017-04-01")-- 預設採用id類型的最小值
)
-- 當用戶插入數據時,分區列值會按照順序依次比較,最終得到對應的分區
List分區
• 分區列支持 BOOLEAN, TINYINT, SMALLINT, INT, BIGINT, LARGEINT, DATE, DATETIME, CHAR, VARCHAR 數據類型,分區值為枚舉值。只有當數據為目標分區枚舉值其中之一時,才可以命中分區。
• Partition 支持通過 VALUES IN (...) 來指定每個分區包含的枚舉值。
-- 單列
CREATE TABLE IF NOT EXISTS test.expamle_list_tbl
(
`user_id` LARGEINT NOT NULL COMMENT "用戶id",
`date` DATE NOT NULL COMMENT "數據灌入日期時間",
`timestamp` DATETIME NOT NULL COMMENT "數據灌入的時間戳",
`city` VARCHAR(20) NOT NULL COMMENT "用戶所在城市",
`age` SMALLINT NOT NULL COMMENT "用戶年齡",
`sex` TINYINT NOT NULL 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 "用戶最小停留時間"
)
ENGINE=olap
AGGREGATE KEY(`user_id`, `date`, `timestamp`, `city`, `age`, `sex`)
PARTITION BY LIST(`city`)
(
PARTITION `p_cn` VALUES IN ("Beijing", "Shanghai", "Hong Kong"),
PARTITION `p_usa` VALUES IN ("New York", "San Francisco"),
PARTITION `p_jp` VALUES IN ("Tokyo")
)
-- 指定分桶的語法
DISTRIBUTED BY HASH(`user_id`) BUCKETS 1
PROPERTIES
(
"replication_num" = "3"
);
-- 多列
PARTITION BY LIST(`id`, `city`)
(
PARTITION `p1_city` VALUES IN (("1", "Beijing",), ("2", "Shanghai")),
PARTITION `p2_city` VALUES IN (("2", "Beijing"), ("1", "Shanghai")),
PARTITION `p3_city` VALUES IN (("3", "Beijing"), ("4", "Shanghai"))
)
分桶(Bucket)
- 如果使用了 Partition,則 DISTRIBUTED ... 語句描述的是數據在各個分區內的劃分規則。如果不使用 Partition,則描述的是對整個表的數據的劃分規則。
- 分桶列可以是多列,但必須為 Key 列。分桶列可以和 Partition 列相同或不同。
- 分桶列的選擇,是在 查詢吞吐 和 查詢併發 之間的一種權衡:
- 如果選擇多個分桶列,則數據分佈更均勻。如果一個查詢條件不包含所有分桶列的等值條件,那麼該查詢會觸發所有分桶同時掃描,這樣查詢的吞吐會增加,單個查詢的延遲隨之降低。這個方式適合大吞吐低併發的查詢場景。
- 如果僅選擇一個或少數分桶列,則對應的點查詢可以僅觸發一個分桶掃描。此時,當多個點查詢併發時,這些查詢有較大的概率分別觸發不同的分桶掃描,各個查詢之間的IO影響較小(尤其當不同桶分佈在不同磁碟上時),所以這種方式適合高併發的點查詢場景。
- 分桶的數量理論上沒有上限
關於 Partition 和 Bucket的數量和數據量的建議。
- 一個表的 Tablet 總數量等於 (Partition num * Bucket num)。
- 一個表的 Tablet 數量,在不考慮擴容的情況下,推薦略多於整個集群的磁碟數量。
- 單個 Tablet 的數據量理論上沒有上下界,但建議在 1G - 10G 的範圍內。如果單個 Tablet 數據量過小,則數據的聚合效果不佳,且元數據管理壓力大。如果數據量過大,則不利於副本的遷移、補齊,且會增加 Schema Change 或者 Rollup 操作失敗重試的代價(這些操作失敗重試的粒度是 Tablet)。分桶應該控制桶內數據量 ,不易過大或者過小
- 當 Tablet 的數據量原則和數量原則衝突時,建議優先考慮數據量原則。
- 在建表時,每個分區的 Bucket 數量統一指定。但是在動態增加分區時(ADD PARTITION),可以單獨指定新分區的 Bucket 數量。可以利用這個功能方便的應對數據縮小或膨脹。
- 一個 Partition 的 Bucket 數量一旦指定,不可更改。所以在確定 Bucket 數量時,需要預先考慮集群擴容的情況。比如當前只有 3 台 host,每台 host 有 1 塊盤。如果 Bucket 的數量只設置為 3 或更小,那麼後期即使再增加機器,也不能提高併發度。
註:表的數據量可以通過 SHOW DATA命令查看,結果除以副本數,即表的數據量。
複合分區與單分區的選擇
複合分區
• 第一級稱為 Partition,即分區。用戶可以指定某一維度列作為分區列(當前只支持整型和時間類型的列),並指定每個分區的取值範圍。
• 第二級稱為 Distribution,即分桶。用戶可以指定一個或多個維度列以及桶數對數據進行 HASH 分佈。
以下場景推薦使用複合分區
• 有時間維度或類似帶有有序值的維度,可以以這類維度列作為分區列。分區粒度可以根據導入頻次、分區數據量等進行評估。地域、時間
• 歷史數據刪除需求:如有刪除歷史數據的需求(比如僅保留最近N 天的數據)。使用複合分區,可以通過刪除歷史分區來達到目的。也可以通過在指定分區內發送 DELETE 語句進行數據刪除。
• 改善數據傾斜問題:每個分區可以單獨指定分桶數量。如按天分區,當每天的數據量差異很大時,可以通過指定分區的分桶數,合理劃分不同分區的數據,分桶列建議選擇區分度大的列。
用戶也可以不使用複合分區,即使用單分區。則數據只做 HASH 分佈。
PROPERTIES
在建表語句的最後,可以用 PROPERTIES 關鍵字來設置一些表的屬性參數(參數有很多)
PROPERTIES(
"參數名" = "參數值"
)
分片副本數(replication_num)
每個 Tablet 的副本數量。預設為 3,建議保持預設即可。在建表語句中,所有 Partition中的 Tablet 副本數量統一指定。而在增加新分區時,可以單獨指定新分區中 Tablet 的副本數量。
副本數量可以在運行時修改。強烈建議保持奇數。
最大副本數量取決於集群中獨立 IP 的數量(註意不是 BE 數量)。Doris 中副本分佈的原則是,不允許同一個 Tablet 的副本分佈在同一臺物理機上,而識別物理機即通過 IP。所以,即使在同一臺物理機上部署了 3 個或更多 BE 實例,如果這些 BE 的 IP 相同,則依然只能設置副本數為 1。對於一些小,並且更新不頻繁的維度表,可以考慮設置更多的副本數。這樣在 Join 查詢時,可以有更大的概率進行本地數據 Join。
存儲介質 和 熱數據冷卻時間
• storage_medium
• storage_cooldown_time
建表時,可以統一指定所有 Partition 初始存儲的介質及熱數據的冷卻時間,如:
"storage_medium" = "SSD"
-- 要在當前時間之後,並且是一個datetime類型
"storage_cooldown_time" = "2023-04-20 00:00:00"
-- 預設初始存儲介質可通過 fe 的配置文件 fe.conf 中指定 default_storage_medium=xxx,如果沒有指定,則預設為 HDD。如果指定為 SSD,則數據初始存放在 SSD 上。沒設storage_cooldown_time,則預設 30 天後,數據會從 SSD 自動遷移到 HDD上。如果指定了 storage_cooldown_time,則在到達 storage_cooldown_time 時間後,數據才會遷移。
註意,當指定 storage_medium 時,如果 FE 參數 enable_strict_storage_medium_check 為False 該參數只是一個“儘力而為”的設置。即使集群內沒有設置 SSD 存儲介質,也不會報錯,而是自動存儲在可用的數據目錄中。 同樣,如果 SSD 介質不可訪問、空間不足,都可能導致數據初始直接存儲在其他可用介質上。而數據到期遷移到 HDD 時,如果 HDD 介質不 可 訪 問 、 空 間 不 足 , 也 可 能 遷 移 失 敗 ( 但 是 會 不 斷 嘗 試 ) 。 如 果 FE 參 數enable_strict_storage_medium_check 為 True 則當集群內沒有設置 SSD 存儲介質時,會報錯Failed to find enough host in all backends with storage medium is SSD。