前段時間給 StarRocks 的物化檢視新增了一個特性,那也是我第一次接觸 StarRocks,因為完全不熟悉這個資料庫,所以很多東西都是從頭開始瞭解概念。
為了能順利的新增這個特性(具體內容可以見後文),我需要把整個物化檢視的流程串聯一遍,於是便有了這篇文章。
在開始之前簡單瞭解下物化檢視的基本概念:
簡單來說,檢視和 MySQL 這類傳統資料庫的概念類似,也是用於解決大量消耗效能的 SQL 的,可以提前將這些資料查詢好然後放在一張單獨的表中,這樣再查詢的時候效能消耗就比較低了。
重新整理條件
為了保證檢視資料的實時性,還需要在資料發生變化的時候能夠及時重新整理檢視裡的資料,目前有這幾個地方會觸發檢視重新整理:
- 手動重新整理檢視,使用
REFRESH MATERIALIZED VIEW order_mv;
語句 - 將檢視設定為 active 狀態:
ALTER MATERIALIZED VIEW order_mv ACTIVE;
- 基表資料發生變化時觸發重新整理。
- truncate 基表時觸發重新整理:
truncate table trunc_db.t1;
- drop partition 時觸發:
ALTER TABLE <tbl_name> DROP PARTITION(S) p0, p1 [, ...];
這裡的 truncate table 和 drop partition 目前的版本還存在 bug:當基表和物化檢視不在一個資料庫時不會觸發自動重新整理,目前已經修復了。
- https://github.com/StarRocks/starrocks/pull/52618
- https://github.com/StarRocks/starrocks/pull/52295
重新整理流程
如圖所示,當觸發一次重新整理之後主要就是需要計算出需要重新整理的分割槽。
第一次觸發重新整理的時候是不會帶上週期(比如時間範圍),然後根據過濾計算出來的週期,預設情況下只會使用第一個週期(我們可以透過 partition_refresh_number
引數來調整單次重新整理的分割槽數量)。
然後如果還有其餘的週期,會將這些週期重新觸發一次重新整理任務(會帶上剛才剩餘的週期資料),這樣進行遞迴執行。
透過日誌會看到返回的分割槽資料。
新增最佳化引數
我們在使用物化檢視的時候,碰到一個場景:
CREATE TABLE IF NOT EXISTS test.par_tbl1
(
datekey DATETIME,
k1 INT,
item_id STRING,
v2 INT
)PRIMARY KEY (`datekey`,`k1`)
PARTITION BY date_trunc('day', `datekey`);
CREATE TABLE IF NOT EXISTS test.par_tbl2
(
datekey DATETIME,
k1 INT,
item_id STRING,
v2 INT
)PRIMARY KEY (`datekey`,`k1`)
PARTITION BY date_trunc('day', `datekey`);
CREATE TABLE IF NOT EXISTS test.par_tbl3
(
datekey DATETIME,
k1 INT,
item_id STRING,
v2 INT
)
PRIMARY KEY (`datekey`,`k1`);
但我們有三張基表,其中 1 和 2 都是分割槽表,但是 3 是非分割槽表。
此時基於他們新建了一個物化檢視:
CREATE
MATERIALIZED VIEW test.mv_test
REFRESH ASYNC
PARTITION BY a_time
PROPERTIES (
"excluded_trigger_tables" = "par_tbl3"
)
AS
select date_trunc("day", a.datekey) as a_time, date_trunc("day", b.datekey) as b_time,date_trunc("day", c.datekey) as c_time
from test.par_tbl1 a
left join test.par_tbl2 b on a.datekey = b.datekey and a.k1 = b.k1
left join test.par_tbl3 c on a.k1 = c.k1;
當我同時更新了分割槽表和非分割槽表的資料時:
UPDATE `par_tbl1` SET `v2` = 2 WHERE `datekey` = '2024-08-05 01:00:00' AND `k1` = 3;
UPDATE `par_tbl3` SET `item_id` = '3' WHERE `datekey` = '2024-10-01 01:00:00' AND `k1` = 3;
預期的結果是隻有 par_tbl1
表裡修改的資料會被同步到檢視("excluded_trigger_tables" = "par_tbl3"
已經被設定為不會觸發檢視重新整理),但實際情況是 par_tbl1
和 par_tbl2
表裡所有的資料都會被重新整理到物化檢視中。
我們可以使用這個 SQL 查詢無刷檢視任務的執行狀態:
SELECT * FROM information_schema.task_runs order by create_time desc;
這樣就會造成資源損耗,如果這兩張基表的資料非常大,本次重新整理會非常耗時。
所以我們的需求是在這樣的場景下也只重新整理修改的資料。
因此我們在新建物化檢視的時候新增了一個引數:
CREATE
MATERIALIZED VIEW test.mv_test
REFRESH ASYNC
PARTITION BY a_time
PROPERTIES (
"excluded_trigger_tables" = "par_tbl3",
"excluded_refresh_tables"="par_tbl3"
)
AS
select date_trunc("day", a.datekey) as a_time, date_trunc("day", b.datekey) as b_time,date_trunc("day", c.datekey) as c_time
from test.par_tbl1 a
left join test.par_tbl2 b on a.datekey = b.datekey and a.k1 = b.k1
left join test.par_tbl3 c on a.k1 = c.k1;
這樣當在重新整理資料的時候,會判斷 excluded_refresh_tables
配置的表是否有發生資料變化,如果有的話則不能將當前計算出來的分割槽(1,2 兩張表的全量資料)全部重新整理,而是繼續求一個交集,只計算基表發生變化的資料。
這樣就可以避免 par_tbl1、par_tbl2 的資料全量重新整理,而只重新整理修改的資料。
這樣的場景通常是在關聯的基表中有一張字典表,通常資料量不大,所以也不需要分割槽的場景。
這樣在建立物化檢視的時候就可以使用這兩個引數 excluded_trigger_tables,excluded_refresh_tables
將它排除掉了。
整體的重新整理邏輯並不複雜,主要就是幾個不同的重新整理入口以及重新整理過程中計算分割槽的邏輯。
參考連結:
- https://docs.starrocks.io/zh/docs/using_starrocks/async_mv/Materialized_view/#理解-starrocks-物化檢視
- https://docs.starrocks.io/zh/docs/using_starrocks/async_mv/use_cases/data_modeling_with_materialized_views/#分割槽建模
- https://github.com/StarRocks/starrocks/pull/52295
- https://github.com/StarRocks/starrocks/pull/52618