全部產品
Search
文件中心

Hologres:SQL管理物化視圖

更新時間:Nov 09, 2024

即時物化視圖將對明細表的資料進行預先彙總,儲存為物化視圖,通過查詢物化視圖,減少計算量,顯著提升查詢效能。本文為您介紹在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_ruser_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。