實時物化視圖將對明細表的數據進行預先聚合,存儲為物化視圖,通過查詢物化視圖,減少計算量,顯著提升查詢性能。本文為您介紹在Hologres中如何使用物化視圖。
背景信息
Hologres實時物化視圖不需要手動刷新物化數據,明細表實時寫入,會實時反映在對物化視圖的查詢上,寫入即可見,寫入即聚合。
在實時物化視圖中,實時寫入的表叫明細表,也稱Base Table,用戶的Insert、Update、Delete都執行在明細表上,物化視圖基于明細表的聚合規則定義,當明細表發生變更時,變更會實時同步到物化視圖中。當前僅支持Insert類變更,后續會逐步增加更多類型的變更。
使用限制
當前實時物化視圖不支持對明細表進行Delete或Update操作,所以需要將明細表設置
appendonly
屬性,當前對明細表任何的Delete或Update操作會提示:Table XXX is append-only
。Flink實時寫入時mutateType
也只支持InsertOrIgnore。當前不支持異步創建物化視圖,需要創建明細表的同時創建基于該表的物化視圖。
當前僅支持單表的物化視圖,不支持CTE、多表JOIN、子查詢、不支持WHERE條件、ORDER BY、LIMIT、HAVING語句。
實時物化視圖的GROUP BY Key和Value都不支持表達式,比如不支持
SUM(CASE WHEN COND THEN A ELSE B END)
、SUM(col1 + col2)
、GROUP BY date_trunc('hour', ts)
。每張明細表最多創建10個物化視圖,物化視圖數量和資源消耗成正比。
如果基于分區表創建物化視圖,物化視圖的GROUP BY Key必須包含分區表的分區列,且不能對分區表的子表創建物化視圖,只能針對分區表父表創建。
如果基于分區表創建物化視圖,不支持
ATTACH PARTITION
至父表語法,支持CREATE TABLE PARTITION OF
語法。對于創建了物化視圖的明細表,暫不支持
DROP COLUMN
。物化視圖的底層數據與明細表的TTL一致,不可以手動設置物化視圖的TTL,否則會出現物化視圖數據和明細表數據不一致的情況。
支持的聚合函數
物化視圖當前支持如下聚合函數。
SUM
COUNT
AVG
MIN
MAX
RB_BUILD_CARDINALITY_AGG(只支持BIGINT,需創建Extension roaringbitmap)
SQL示例
創建實時物化視圖
BEGIN; CREATE TABLE base_sales( day text not null, hour int , ts timestamptz, amount float, pk text not null primary key ); CALL SET_TABLE_PROPERTY('base_sales', 'mutate_type', 'appendonly'); --當實時物化視圖被Drop后,可以取消明細表的appendonly屬性,執行以下命令 --CALL SET_TABLE_PROPERTY('base_sales', 'mutate_type', 'none'); CREATE MATERIALIZED VIEW mv_sales AS SELECT day, hour, avg(amount) AS amount_avg FROM base_sales GROUP BY day, hour; COMMIT; insert into base_sales values(to_char(now(),'YYYYMMDD'),'12',now(),100,'pk1'); insert into base_sales values(to_char(now(),'YYYYMMDD'),'12',now(),200,'pk2'); insert into base_sales values(to_char(now(),'YYYYMMDD'),'12',now(),300,'pk3');
分區表創建物化視圖
BEGIN; CREATE TABLE base_sales_p( day text not null, hour int, ts timestamptz, amount float, pk text not null, primary key (day, pk) ) partition by list(day); CALL SET_TABLE_PROPERTY('base_sales_p', 'mutate_type', 'appendonly'); --day是分區列,要出現在視圖的group by的條件中 CREATE MATERIALIZED VIEW mv_sales_p AS SELECT day, hour, avg(amount) AS amount_avg FROM base_sales_p GROUP BY day, hour; COMMIT; create table base_sales_20220101 partition of base_sales_p for values in('20220101');
查詢物化視圖
SELECT * FROM mv_sales WHERE day = to_char(now(),'YYYYMMDD') AND hour = 12;
刪除物化視圖
DROP MATERIALIZED VIEW mv_sales;
查詢物化視圖占用存儲空間
select pg_relation_size('mv_sales');
查詢所有物化視圖底層占用空間
SELECT schemaname || '.' || matviewname AS mv_full_name, pg_size_pretty(pg_relation_size('"' || schemaname || '"."' || matviewname || '"')) AS mv_size, pg_relation_size('"' || schemaname || '"."' || matviewname || '"') AS order_size FROM pg_matviews ORDER BY order_size DESC;
使用物化視圖提升精確UV計算性能
精確UV計算是計算復雜度非常高的算子,通常是系統的性能瓶頸部分。Hologres支持RB_BUILD_CARDINALITY_AGG
聚合函數,通過利用RoaringBitmap數據結構,可以對BIGINT類數據(通常是表示業務ID字段)進行物化視圖預聚合,實現UV統計實時去重,可按照如下方式創建物化視圖,當前僅支持BIGINT類字段的聚合去重。
--UV計算依賴RoaringBitmap數據類型,需要提前創建RoaringBitmap extension
CREATE EXTENSION if not exists roaringbitmap;
BEGIN;
CREATE TABLE base_sales_r(
day text not null,
hour int ,
ts timestamptz,
amount float,
userid bigint,
pk text not null primary key
);
CALL SET_TABLE_PROPERTY('base_sales_r', 'mutate_type', 'appendonly');
CREATE MATERIALIZED VIEW mv_sales_r AS
SELECT
day,
hour,
avg(amount) AS amount_avg,
rb_build_cardinality_agg(userid) as user_count
FROM base_sales_r
GROUP BY day, hour;
COMMIT;
insert into base_sales_r values(to_char(now(),'YYYYMMDD'),'12',now(),100,1,'pk1');
insert into base_sales_r values(to_char(now(),'YYYYMMDD'),'12',now(),200,2,'pk2');
insert into base_sales_r values(to_char(now(),'YYYYMMDD'),'12',now(),300,3,'pk3');
select user_count as UV from mv_sales_r where day = to_char(now(),'YYYYMMDD') AND hour = 12;
通過rb_build_cardinality_agg
計算去重數,mv_sales_r
中user_count
代表userid
去重數,查詢user_count
可獲得去重數。
使用物化視圖支持多維度聚合查詢
假設定義了上述的mv_sales
物化視圖,且明細表base_sales
中當前含有以下明細數據。
Day | Hour | Amount | PK |
20210101 | 12 | 2 | pk1 |
20210101 | 12 | 4 | pk2 |
20210101 | 13 | 6 | pk3 |
直接查詢sales_mv
將會有如下結果。
postgres=> select * from mv_sales;
day | hour | amount_avg
-----------+---------+--------------
20210101 | 12 | 3
20210101 | 13 | 6
這時如果想更改查詢物化視圖的聚合維度,例如使用維度day進行avg聚合計算,則會得到的會是一個錯誤的結果,因為avg的avg不等于total的avg。
postgres=> select day, avg(amount_avg) from mv_sales group by day;
day | avg
-----------+--------
20210101 | 4.5
這時候一種辦法是再建一張以day為維度進行聚合的物化視圖,但這樣會導致物化視圖的數量膨脹,Hologres提供了一種基于聚合中間狀態的實現,使得用戶僅用一張物化視圖,實現不同維度聚合查詢。這里以Avg為例,修改聚合視圖的定義如下。
BEGIN;
CREATE TABLE base_sales(
day text not null,
hour int ,
ts timestamptz,
amount float,
pk text not null primary key
);
CALL SET_TABLE_PROPERTY('base_sales', 'mutate_type', 'appendonly');
CREATE MATERIALIZED VIEW mv_sales_partial AS
SELECT
day,
hour,
avg(amount) as avg,
avg_partial(amount) AS amt_avg_partial
FROM base_sales
GROUP BY day, hour;
COMMIT;
原先的avg聚合函數重新定義為avg_partial聚合函數,amount_avg_partial列存儲的是聚合結果的中間狀態,查詢時需要修改查詢函數,將avg聚合函數改寫為avg_final最終聚合函數,聲明是對聚合結果中間狀態的最終聚合。
postgres=> select day, avg(avg) as avg_avg, avg_final(amt_avg_partial) as real_avg from mv_sales_partial group by day;
day | avg_avg | real_avg
-----------+-----------+----------
20210101 | 4.5 | 4
目前支持以下聚合函數及對應的partial聚合函數。
普通聚合函數 | Partial聚合函數 | 最終聚合函數 |
AVG | AVG_PARTIAL | AVG_FINAL |
RB_BUILD_CARDINALITY_AGG | RB_BUILD_AGG | RB_OR_CARDINALITY_AGG |
TTL說明
如果明細表設置了TTL,并創建了物化視圖,那么在TTL臨界點附近的數據,Hologres無法保證明細表和物化視圖查詢結果的一致性,查詢TTL臨界點附近的物化視圖數據的結果,是個未定義行為。下面以明細表base_sales_table
和物化視圖sales_mv
為例。
為base_sales_table
設置了TTL,如果數據由于TTL到期被回收掉,那么此時查詢明細表的結果如下所示。
postgres=> SELECT
day,
hour,
avg(amount) AS amount_avg
FROM base_sales
GROUP BY day, hour;
--查詢結果
day | hour | amount_avg
-----------+---------+--------------
20210101 | 12 | 4
20210101 | 13 | 6
但是由于被回收的數據,已經物化到了物化視圖的數據中,所以查詢物化視圖時有可能得到的結果如下。
postgres=> select * from mv_sales;
--查詢結果
day | hour | amount_avg
-----------+---------+--------------
20210101 | 12 | 3
20210101 | 13 | 6
此時查詢結果不一致,建議改進方案如下。
明細表不要設置TTL。
明細表設置TTL,但是物化視圖的GROUP BY含有數據的時間字段,且查詢物化視圖的時候,不會去查詢在TTL臨界點附近的數據。
明細表建成分區表,不設置TTL,回收數據通過刪除(Drop)分區表來實現。
實時物化視圖使用最佳實踐
建表時建議將物化視圖的GROUP BY Key設置為明細表的Distribution Key,這樣能進一步提升數據的壓縮率,提升查詢性能。
查詢時建議將查詢物化視圖時常用的過濾條件,放在GROUP BY Key的前列(符合Clustering Key左匹配原則)。
物化視圖的智能路由
查詢時不需要顯式指定物化視圖表名稱,可以像之前一樣基于基礎表進行查詢。如果有匹配的物化視圖表,優化器會智能路由到最佳的物化視圖表來加速查詢。在查詢時,物化視圖表的選擇規則如下:
選擇包含所有查詢列或可以通過間接計算得到的物化視圖表。
選擇GROUP BY列字段包含原始查詢GROUP BY所有列的物化視圖表。
當有多個物化視圖表符合條件時,選擇GROUP BY列字段少的物化視圖表。
當前支持智能路由的聚合函數有SUM、COUNT、MIN和MAX。