リアルタイムマテリアライズドビューは、ベーステーブルのデータを事前集計して格納します。マテリアライズドビューをクエリすることで、計算ワークロードが削減され、クエリパフォーマンスが大幅に向上します。このトピックでは、Hologres でマテリアライズドビューを使用する方法について説明します。
背景情報
Hologres のリアルタイムマテリアライズドビューのデータは、手動でリフレッシュする必要はありません。ベーステーブルにデータが書き込まれると、その変更はマテリアライズドビューのクエリにリアルタイムで反映されます。データは書き込まれるとすぐに可視化され、集計されます。
リアルタイムマテリアライズドビューでは、リアルタイムで書き込みを受け取るテーブルをベーステーブルと呼びます。すべての `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` キーと値は式をサポートしていません。たとえば、
SUM(CASE WHEN COND THEN A ELSE B END)、SUM(col1 + col2)、GROUP BY date_trunc('hour', ts)はサポートされていません。各ベーステーブルに対して作成できるマテリアライズドビューは最大 10 個です。リソース消費量はマテリアライズドビューの数に比例して増加します。
パーティションテーブルに対してマテリアライズドビューが作成される場合、マテリアライズドビューの `GROUP BY` キーにはパーティションキー列を含める必要があります。マテリアライズドビューは親テーブルに対してのみ作成でき、その子テーブルには作成できません。
パーティションテーブルに対してマテリアライズドビューが作成される場合、
ATTACH PARTITION構文を使用してパーティションを親テーブルにアタッチすることはサポートされていません。ただし、CREATE TABLE PARTITION OF構文はサポートされています。マテリアライズドビューを持つベーステーブルでは、
DROP COLUMN操作はサポートされていません。マテリアライズドビューの基になるデータは、そのベーステーブルと同じ Time-to-Live (TTL) を共有します。マテリアライズドビューに手動で TTL を設定しないでください。そうしないと、マテリアライズドビューとベーステーブルの間でデータの不整合が発生する可能性があります。
サポートされる集計関数
マテリアライズドビューは現在、次の集計関数をサポートしています。
SUM
COUNT
AVG
MIN
MAX
RB_BUILD_CARDINALITY_AGG (BIGINT データ型のみをサポートします。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'); -- リアルタイムマテリアライズドビューを削除した後、次のコマンドを実行してベーステーブルから 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 データ構造を使用すると、通常はビジネス ID フィールドを表す BIGINT データをマテリアライズドビューに事前集計できます。このプロセスにより、UV 統計のリアルタイム重複排除が実現します。次のようにマテリアライズドビューを作成できます。現在、BIGINT フィールドの集計と重複排除のみがサポートされています。
-- UV 計算は RoaringBitmap データ型に依存します。事前に RoaringBitmap 拡張機能を作成する必要があります。
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 を使用して集計すると、誤った結果が得られます。これは、平均の平均が全体の平均と等しくないためです。
postgres=> select day, avg(amount_avg) from mv_sales group by day;
day | avg
-----------+--------
20210101 | 4.51 つの解決策は、日ごとに集計される別のマテリアライズドビューを作成することです。ただし、これによりマテリアライズドビューの数が増加します。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_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サポートされている集計関数とそれに対応する部分集計関数は次のとおりです。
標準集計関数 | 部分集計関数 | 最終集計関数 |
AVG | AVG_PARTIAL | AVG_FINAL |
RB_BUILD_CARDINALITY_AGG | RB_BUILD_AGG | RB_OR_CARDINALITY_AGG |
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 を設定しない。子パーティションを削除してデータを回収する。
リアルタイムマテリアライズドビューを使用するためのベストプラクティス
ベーステーブルとそのマテリアライズドビューを作成するときは、マテリアライズドビューの `GROUP BY` キーをベーステーブルの分散キーと同じに設定します。この方法は、データ圧縮率とクエリパフォーマンスを向上させることができます。
マテリアライズドビューを定義するときは、フィルター条件で頻繁に使用される列を `GROUP BY` キーの先頭に配置します。この方法は、クラスタリングキーの左端一致の原則に従います。
マテリアライズドビューのインテリジェントルーティング
クエリでマテリアライズドビュー名を明示的に指定する必要はありません。代わりに、ベーステーブルを直接クエリすることが可能です。一致するマテリアライズドビューが存在する場合、オプティマイザーはクエリを高速化するために、最も適切なマテリアライズドビューにクエリをインテリジェントにルーティングします。オプティマイザーは、次の基準に基づいてマテリアライズドビューを選択します:
マテリアライズドビューに、クエリされたすべての列、またはクエリされた値を間接的に計算できる列が含まれている。
マテリアライズドビューの `GROUP BY` 列に、元のクエリの `GROUP BY` 句のすべての列が含まれている。
複数のマテリアライズドビューが上記の基準を満たす場合、オプティマイザーは `GROUP BY` キーの列数が最も少ないものを選択する。
インテリジェントルーティングをサポートする集計関数は、SUM、COUNT、MIN、および MAX です。