背景
我司有個廣告競價系統,地區資料包表的維度非常多,導致資料量急劇膨脹,達到億級別,使用 Mysql 資料庫,查詢非常卡頓,急需最佳化,使用 Doris 資料庫代替 Mysql 儲存和查詢,秒出結果,另外可以藉助 Doirs 的分割槽功能,自動保留 6 個月的資料。
Doris是什麼?
Apache Doris 是一個基於 MPP 架構的高效能、實時的分析型資料庫,以極速易用的特點被人們所熟知,僅需亞秒級響應時間即可返回海量資料下的查詢結果,不僅可以支援高併發的點查詢場景,也能支援高吞吐的複雜分析場景。基於此,Apache Doris 能夠較好的滿足報表分析、即席查詢、統一數倉構建、資料湖聯邦查詢加速等使用場景,使用者可以在此之上構建使用者行為分析、AB 實驗平臺、日誌檢索分析、使用者畫像分析、訂單分析等應用。
發展歷程:誕生於百度廣告報表業務的 Palo 專案 → 2017 年正式對外開源 → 2018 年 7 月由百度捐贈給 Apache 基金會進行孵化 → 2022 年 6 月,Apache Doris 成功從 Apache 孵化器畢業,正式成為 Apache 頂級專案
使用場景:
- 報表分析
- 即席查詢
- 統一數倉構建
二、Doris三種資料模型
Doris 資料模型上目前分為三類: AGGREGATE KEY, UNIQUE KEY, DUPLICATE KEY。三種模型中資料都是按KEY進行排序。
1、AGGREGATE KEY
AGGREGATE KEY相同時,新舊記錄進行聚合,目前支援的聚合函式有SUM, MIN, MAX, REPLACE。
AGGREGATE KEY模型可以提前聚合資料, 適合報表和多維分析業務。
CREATE DATABASE IF NOT EXISTS example_db;
CREATE TABLE IF NOT EXISTS example_db.example_tbl_agg1
(
`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"
);
2、UNIQUE KEY
UNIQUE KEY 相同時,新記錄覆蓋舊記錄。在1.2版本之前,UNIQUE KEY 實現上和 AGGREGATE KEY 的 REPLACE 聚合方法一樣,二者本質上相同,自1.2版本我們給UNIQUE KEY引入了merge on write實現,該實現有更好的聚合查詢效能。適用於有更新需求的分析業務。
CREATE TABLE IF NOT EXISTS example_db.example_tbl_unique
(
`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"
);
3、DUPLICATE KEY
只指定排序列,相同的行不會合並。適用於資料無需提前聚合的分析業務。
CREATE TABLE IF NOT EXISTS example_db.example_tbl_duplicate
(
`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"
);
三、資料模型的選擇建議
- Aggregate 模型可以透過預聚合,極大地降低聚合查詢時所需掃描的資料量和查詢的計算量,非常適合有固定模式的報表類查詢場景。
- Unique 模型針對需要唯一主鍵約束的場景,可以保證主鍵唯一性約束。
- Duplicate 適合任意維度的 Ad-hoc 查詢。雖然同樣無法利用預聚合的特性,但是不受聚合模型的約束,可以發揮列存模型的優勢(只讀取相關列,而不需要讀取所有 Key 列)。
四、Doris分割槽
1、動態分割槽
動態分割槽是在 Doris 0.12 版本中引入的新功能。旨在對錶級別的分割槽實現生命週期管理(TTL),減少使用者的使用負擔。
動態分割槽只支援 Range 分割槽。
CREATE TABLE tbl1
(...)
PROPERTIES
(
"dynamic_partition.prop1" = "value1",
"dynamic_partition.prop2" = "value2",
...
)
dynamic_partition.enable true or false,預設為
TRUE``dynamic_partition.time_unit
(必選引數)動態分割槽排程的單位。可指定為HOUR
、DAY
、WEEK
、MONTH
、YEAR
。分別表示按小時、按天、按星期、按月、按年進行分割槽建立或刪除。- dynamic_partition.time_zone 動態分割槽的時區,如果不填寫,則預設為當前機器的系統的時區
- dynamic_partition.start 動態分割槽的起始偏移,為負數。根據
time_unit
屬性的不同,以當天(星期/月)為基準,分割槽範圍在此偏移之前的分割槽將會被刪除 dynamic_partition.end
(必選引數)動態分割槽的結束偏移,為正數。根據time_unit
屬性的不同,以當天(星期/月)為基準,提前建立對應範圍的分割槽。dynamic_partition.prefix
(必選引數)動態建立的分割槽名字首。
舉例說明:
表 tbl1 分割槽列 k1 型別為 DATE,建立一個動態分割槽規則。按月分割槽,不刪除歷史分割槽,並且預先建立未來2個月的分割槽。同時設定以每月3號為起始日。
CREATE TABLE tbl1
(
k1 DATE,
...
)
PARTITION BY RANGE(k1) ()
DISTRIBUTED BY HASH(k1)
PROPERTIES
(
"dynamic_partition.enable" = "true",
"dynamic_partition.time_unit" = "MONTH",
"dynamic_partition.end" = "2",
"dynamic_partition.prefix" = "p",
"dynamic_partition.buckets" = "8",
"dynamic_partition.start_day_of_month" = "3"
);
假設當前日期為 2020-05-29。則根於以上規則,tbl1 會產生以下分割槽:
p202005: ["2020-05-03", "2020-06-03")
p202006: ["2020-06-03", "2020-07-03")
p202007: ["2020-07-03", "2020-08-03")
2、靜態分割槽
見官方檔案,連結:doris.apache.org/zh-CN/docs/advanc...
五、Doris簡單應用
talk is cheap,show me code,以猜你地區報表為例,目前表的資料上億,查詢非常慢,急需最佳化
建立表:
CREATE TABLE `ad_area_reports_test` (
`day` date NULL COMMENT '日期',
`hour` char(2) NOT NULL COMMENT '小時',
`province` varchar(50) NOT NULL COMMENT '省',
`city` varchar(50) NOT NULL COMMENT '市',
`user_id` bigint(20) NULL COMMENT '賬戶ID',
`ad_id` bigint(20) NULL COMMENT '廣告ID',
`ad_group_id` bigint(20) NULL COMMENT '廣告組ID',
`ad_creative_id` bigint(20) NULL COMMENT '創意ID',
`ad_form` tinyint(4) NULL,
`view` bigint(20) SUM NULL DEFAULT "0" COMMENT '展現',
`click` bigint(20) SUM NULL DEFAULT "0" COMMENT '點選',
`cost` bigint(20) SUM NULL DEFAULT "0" COMMENT '消費金額'
) ENGINE=OLAP
AGGREGATE KEY(`day`, `hour`, `province`, `city`, `user_id`, `ad_id`, `ad_group_id`, `ad_creative_id`, `ad_form`)
COMMENT 'OLAP'
PARTITION BY RANGE(`day`)()
DISTRIBUTED BY HASH(`day`) BUCKETS 10
PROPERTIES (
"dynamic_partition.enable" = "true",
"dynamic_partition.time_unit" = "MONTH",
"dynamic_partition.time_zone" = "Asia/Shanghai",
"dynamic_partition.start" = "-6",
"dynamic_partition.end" = "2",
"dynamic_partition.prefix" = "p_",
"dynamic_partition.buckets" = "8",
"dynamic_partition.start_day_of_month" = "18",
);
六、踩過的坑
- 需要提前建立分割槽,不然資料無法寫入
- 欄位型別要一致,不然資料無法寫入,hive 表中 cost 欄位為 string,而 doris 資料庫地區表中 cost 欄位為int型別,導致資料無法寫入,需要使用 cast 欄位轉化
本作品採用《CC 協議》,轉載必須註明作者和本文連結