メインコンテンツまでスキップ

Query rewrite

マテリアライズドビューを使用したクエリのリライト

このトピックでは、StarRocksの非同期マテリアライズドビューを活用してクエリをリライトおよび高速化する方法について説明します。

概要

StarRocksの非同期マテリアライズドビューは、SPJG(select-project-join-group-by)形式に基づく広く採用されている透過的なクエリリライトアルゴリズムを使用しています。クエリのステートメントを変更する必要なく、StarRocksは自動的にベーステーブルに対するクエリを、計算済みの結果を含む対応するマテリアライズドビューに対するクエリに自動的にリライトすることができます。その結果、マテリアライズドビューは計算コストを大幅に削減し、クエリの実行を大幅に高速化するのに役立ちます。

非同期マテリアライズドビューに基づくクエリリライト機能は、次のシナリオで特に有用です。

  • メトリクスの事前集計データの高次元性を扱っている場合、マテリアライズドビューを使用して事前集計されたメトリクスレイヤーを作成できます。
  • ワイドテーブルの結合マテリアライズドビューを使用すると、複数の大規模なワイドテーブルを含む複雑なシナリオでクエリを透過的に高速化することができます。
  • データレイクでのクエリの高速化外部カタログベースのマテリアライズドビューを構築することで、データレイク内のデータに対するクエリを簡単に高速化することができます。

    注記

    JDBCカタログ上のベーステーブルに作成された非同期マテリアライズドビューは、クエリのリライトをサポートしていません。

機能

StarRocksの非同期マテリアライズドビューに基づく自動クエリリライト機能は、次の属性を備えています。

  • 強いデータの整合性:ベーステーブルがネイティブテーブルの場合、StarRocksはマテリアライズドビューに基づくクエリリライトで得られた結果が、ベーステーブルに対する直接のクエリから返される結果と整合性があることを保証します。
  • ステイルネスのリライト:StarRocksはステイルネスのリライトをサポートし、データの変更頻度が高いシナリオに対応するために、一定レベルのデータの有効期限切れを許容することができます。
  • 複数テーブルの結合:StarRocksの非同期マテリアライズドビューは、View Delta JoinsやDerivable Joinsなどの複雑な結合シナリオを含む、さまざまなタイプの結合をサポートしており、大規模なワイドテーブルを含むシナリオでのクエリの高速化を可能にします。
  • 集計のリライト:StarRocksは、集計を伴うクエリをリライトしてレポートのパフォーマンスを向上させることができます。
  • ネストされたマテリアライズドビュー:StarRocksは、ネストされたマテリアライズドビューに基づく複雑なクエリをリライトすることをサポートしており、リライト可能なクエリの範囲を拡張することができます。
  • Unionのリライト:マテリアライズドビューのパーティションのTTL(Time-to-Live)と組み合わせて、ホットデータとコールドデータを分離するために、Unionのリライト機能を使用することができます。これにより、マテリアライズドビューからホットデータをクエリし、ベーステーブルから過去のデータをクエリすることができます。
  • ビュー上のマテリアライズドビュー:ビューに基づくデータモデリングシナリオでのクエリを高速化することができます。
  • 外部カタログ上のマテリアライズドビュー:データレイクでのクエリを高速化することができます。
  • 複雑な式のリライト:StarRocksは、関数呼び出しや算術演算を含む複雑な式を処理することができ、高度な分析および計算要件に対応することができます。

これらの機能については、以下のセクションで詳しく説明します。

結合のリライト

StarRocksは、Inner Join、Cross Join、Left Outer Join、Full Outer Join、Right Outer Join、Semi Join、Anti Joinなど、さまざまなタイプの結合を使用したクエリをリライトすることができます。

以下は、結合を使用したクエリのリライトの例です。次のように2つのベーステーブルを作成します。

CREATE TABLE customer (
c_custkey INT(11) NOT NULL,
c_name VARCHAR(26) NOT NULL,
c_address VARCHAR(41) NOT NULL,
c_city VARCHAR(11) NOT NULL,
c_nation VARCHAR(16) NOT NULL,
c_region VARCHAR(13) NOT NULL,
c_phone VARCHAR(16) NOT NULL,
c_mktsegment VARCHAR(11) NOT NULL
) ENGINE=OLAP
DUPLICATE KEY(c_custkey)
DISTRIBUTED BY HASH(c_custkey) BUCKETS 12;

CREATE TABLE lineorder (
lo_orderkey INT(11) NOT NULL,
lo_linenumber INT(11) NOT NULL,
lo_custkey INT(11) NOT NULL,
lo_partkey INT(11) NOT NULL,
lo_suppkey INT(11) NOT NULL,
lo_orderdate INT(11) NOT NULL,
lo_orderpriority VARCHAR(16) NOT NULL,
lo_shippriority INT(11) NOT NULL,
lo_quantity INT(11) NOT NULL,
lo_extendedprice INT(11) NOT NULL,
lo_ordtotalprice INT(11) NOT NULL,
lo_discount INT(11) NOT NULL,
lo_revenue INT(11) NOT NULL,
lo_supplycost INT(11) NOT NULL,
lo_tax INT(11) NOT NULL,
lo_commitdate INT(11) NOT NULL,
lo_shipmode VARCHAR(11) NOT NULL
) ENGINE=OLAP
DUPLICATE KEY(lo_orderkey)
DISTRIBUTED BY HASH(lo_orderkey) BUCKETS 48;

上記のベーステーブルを使用して、次のようにマテリアライズドビューを作成します。

CREATE MATERIALIZED VIEW join_mv1
DISTRIBUTED BY HASH(lo_orderkey)
AS
SELECT lo_orderkey, lo_linenumber, lo_revenue, lo_partkey, c_name, c_address
FROM lineorder INNER JOIN customer
ON lo_custkey = c_custkey;

このようなマテリアライズドビューは、次のクエリをリライトできます。

SELECT lo_orderkey, lo_linenumber, lo_revenue, c_name, c_address
FROM lineorder INNER JOIN customer
ON lo_custkey = c_custkey;
Rewrite-1

StarRocksは、算術演算、文字列関数、日付関数、CASE WHEN式、またはOR述語などの複雑な式を含む結合クエリのリライトもサポートしています。たとえば、上記のマテリアライズドビューは、次のクエリをリライトすることができます。

SELECT 
lo_orderkey,
lo_linenumber,
(2 * lo_revenue + 1) * lo_linenumber,
upper(c_name),
substr(c_address, 3)
FROM lineorder INNER JOIN customer
ON lo_custkey = c_custkey;

従来のシナリオに加えて、StarRocksはさらに複雑な結合クエリのリライトもサポートしています。

View Delta Joinのリライト

View Delta Joinは、クエリで結合されるテーブルがマテリアライズドビューで結合されるテーブルのサブセットであるシナリオを指します。たとえば、次のような3つのテーブル(lineordercustomerpart)が結合されるクエリを考えてみましょう。マテリアライズドビューjoin_mv1には、lineordercustomerの結合のみが含まれている場合、StarRocksはjoin_mv1を使用してクエリをリライトすることができます。

例:

SELECT lo_orderkey, lo_linenumber, lo_revenue, c_name, c_address, p_name
FROM
lineorder INNER JOIN customer ON lo_custkey = c_custkey
INNER JOIN part ON lo_partkey = p_partkey;

これの元のクエリプランとリライト後のクエリプランは次のようになります:

Rewrite-2

View Delta Joinのリライト

View Delta Joinは、View Delta Joinにおける結合されるテーブルがマテリアライズドビューで結合されるテーブルのサブセットであるシナリオを指します。この機能は通常、大規模なワイドテーブルを含むシナリオに使用されます。たとえば、Star Schema Benchmark(SSB)のコンテキストでは、すべてのテーブルを結合するマテリアライズドビューを作成してクエリのパフォーマンスを向上させることができます。テストの結果、マルチテーブルの結合に対するクエリパフォーマンスは、マテリアライズドビューを介してクエリを透過的にリライトした後のパフォーマンスと同じレベルにまで達することがわかりました。

View Delta Joinのリライトを実行するためには、マテリアライズドビューに結合されていない結合を含める必要があります。以下に、cardinality preservation結合(性質が保持される結合)として見なされる9つのタイプの結合が挙げられます。いずれかの結合を満たすことで、View Delta Joinのリライトが有効になります。

Rewrite-3

SSBテストを例に挙げると、次のようなベーステーブルを作成します。

CREATE TABLE customer (
c_custkey INT(11) NOT NULL,
c_name VARCHAR(26) NOT NULL,
c_address VARCHAR(41) NOT NULL,
c_city VARCHAR(11) NOT NULL,
c_nation VARCHAR(16) NOT NULL,
c_region VARCHAR(13) NOT NULL,
c_phone VARCHAR(16) NOT NULL,
c_mktsegment VARCHAR(11) NOT NULL
) ENGINE=OLAP
DUPLICATE KEY(c_custkey)
DISTRIBUTED BY HASH(c_custkey) BUCKETS 12
PROPERTIES (
"unique_constraints" = "c_custkey" -- 唯一制約を指定します。
);

CREATE TABLE dates (
d_datekey DATE NOT NULL,
d_date VARCHAR(20) NOT NULL,
d_dayofweek VARCHAR(10) NOT NULL,
d_month VARCHAR(11) NOT NULL,
d_year INT(11) NOT NULL,
d_yearmonthnum INT(11) NOT NULL,
d_yearmonth VARCHAR(9) NOT NULL,
d_daynuminweek INT(11) NOT NULL,
d_daynuminmonth INT(11) NOT NULL,
d_daynuminyear INT(11) NOT NULL,
d_monthnuminyear INT(11) NOT NULL,
d_weeknuminyear INT(11) NOT NULL,
d_sellingseason VARCHAR(14) NOT NULL,
d_lastdayinweekfl INT(11) NOT NULL,
d_lastdayinmonthfl INT(11) NOT NULL,
d_holidayfl INT(11) NOT NULL,
d_weekdayfl INT(11) NOT NULL
) ENGINE=OLAP
DUPLICATE KEY(d_datekey)
DISTRIBUTED BY HASH(d_datekey) BUCKETS 1
PROPERTIES (
"unique_constraints" = "d_datekey" -- 唯一制約を指定します。
);

CREATE TABLE supplier (
s_suppkey INT(11) NOT NULL,
s_name VARCHAR(26) NOT NULL,
s_address VARCHAR(26) NOT NULL,
s_city VARCHAR(11) NOT NULL,
s_nation VARCHAR(16) NOT NULL,
s_region VARCHAR(13) NOT NULL,
s_phone VARCHAR(16) NOT NULL
) ENGINE=OLAP
DUPLICATE KEY(s_suppkey)
DISTRIBUTED BY HASH(s_suppkey) BUCKETS 12
PROPERTIES (
"unique_constraints" = "s_suppkey" -- 唯一制約を指定します。
);

CREATE TABLE part (
p_partkey INT(11) NOT NULL,
p_name VARCHAR(23) NOT NULL,
p_mfgr VARCHAR(7) NOT NULL,
p_category VARCHAR(8) NOT NULL,
p_brand VARCHAR(10) NOT NULL,
p_color VARCHAR(12) NOT NULL,
p_type VARCHAR(26) NOT NULL,
p_size TINYINT(11) NOT NULL,
p_container VARCHAR(11) NOT NULL
) ENGINE=OLAP
DUPLICATE KEY(p_partkey)
DISTRIBUTED BY HASH(p_partkey) BUCKETS 12
PROPERTIES (
"unique_constraints" = "p_partkey" -- 唯一制約を指定します。
);

CREATE TABLE lineorder (
lo_orderdate DATE NOT NULL, -- NOT NULLとして指定します。
lo_orderkey INT(11) NOT NULL,
lo_linenumber TINYINT NOT NULL,
lo_custkey INT(11) NOT NULL, -- NOT NULLとして指定します。
lo_partkey INT(11) NOT NULL, -- NOT NULLとして指定します。
lo_suppkey INT(11) NOT NULL, -- NOT NULLとして指定します。
lo_orderpriority VARCHAR(100) NOT NULL,
lo_shippriority TINYINT NOT NULL,
lo_quantity TINYINT NOT NULL,
lo_extendedprice INT(11) NOT NULL,
lo_ordtotalprice INT(11) NOT NULL,
lo_discount TINYINT NOT NULL,
lo_revenue INT(11) NOT NULL,
lo_supplycost INT(11) NOT NULL,
lo_tax TINYINT NOT NULL,
lo_commitdate DATE NOT NULL,
lo_shipmode VARCHAR(100) NOT NULL
) ENGINE=OLAP
DUPLICATE KEY(lo_orderdate,lo_orderkey)
PARTITION BY RANGE(lo_orderdate)
(PARTITION p1 VALUES [("0000-01-01"), ("1993-01-01")),
PARTITION p2 VALUES [("1993-01-01"), ("1994-01-01")),
PARTITION p3 VALUES [("1994-01-01"), ("1995-01-01")),
PARTITION p4 VALUES [("1995-01-01"), ("1996-01-01")),
PARTITION p5 VALUES [("1996-01-01"), ("1997-01-01")),
PARTITION p6 VALUES [("1997-01-01"), ("1998-01-01")),
PARTITION p7 VALUES [("1998-01-01"), ("1999-01-01")))
DISTRIBUTED BY HASH(lo_orderkey) BUCKETS 48
PROPERTIES (
"foreign_key_constraints" = "
(lo_custkey) REFERENCES customer(c_custkey);
(lo_partkey) REFERENCES part(p_partkey);
(lo_suppkey) REFERENCES supplier(s_suppkey)" -- 外部キーを指定します。
);

次に、次のようなベーステーブルに基づいてマテリアライズドビューlineorder_flat_mvを作成します。

CREATE MATERIALIZED VIEW lineorder_flat_mv
DISTRIBUTED BY HASH(LO_ORDERDATE, LO_ORDERKEY) BUCKETS 48
PARTITION BY LO_ORDERDATE
REFRESH MANUAL
PROPERTIES (
"session.partition_refresh_nubmer"="1"
)
AS SELECT /*+ SET_VAR(query_timeout = 7200) */ -- 更新操作のタイムアウトを設定します。
l.LO_ORDERDATE AS LO_ORDERDATE,
l.LO_ORDERKEY AS LO_ORDERKEY,
l.LO_LINENUMBER AS LO_LINENUMBER,
l.LO_CUSTKEY AS LO_CUSTKEY,
l.LO_PARTKEY AS LO_PARTKEY,
l.LO_SUPPKEY AS LO_SUPPKEY,
l.LO_ORDERPRIORITY AS LO_ORDERPRIORITY,
l.LO_SHIPPRIORITY AS LO_SHIPPRIORITY,
l.LO_QUANTITY AS LO_QUANTITY,
l.LO_EXTENDEDPRICE AS LO_EXTENDEDPRICE,
l.LO_ORDTOTALPRICE AS LO_ORDTOTALPRICE,
l.LO_DISCOUNT AS LO_DISCOUNT,
l.LO_REVENUE AS LO_REVENUE,
l.LO_SUPPLYCOST AS LO_SUPPLYCOST,
l.LO_TAX AS LO_TAX,
l.LO_COMMITDATE AS LO_COMMITDATE,
l.LO_SHIPMODE AS LO_SHIPMODE,
c.C_NAME AS C_NAME,
c.C_ADDRESS AS C_ADDRESS,
c.C_CITY AS C_CITY,
c.C_NATION AS C_NATION,
c.C_REGION AS C_REGION,
c.C_PHONE AS C_PHONE,
c.C_MKTSEGMENT AS C_MKTSEGMENT,
s.S_NAME AS S_NAME,
s.S_ADDRESS AS S_ADDRESS,
s.S_CITY AS S_CITY,
s.S_NATION AS S_NATION,
s.S_REGION AS S_REGION,
s.S_PHONE AS S_PHONE,
p.P_NAME AS P_NAME,
p.P_MFGR AS P_MFGR,
p.P_CATEGORY AS P_CATEGORY,
p.P_BRAND AS P_BRAND,
p.P_COLOR AS P_COLOR,
p.P_TYPE AS P_TYPE,
p.P_SIZE AS P_SIZE,
p.P_CONTAINER AS P_CONTAINER,
d.D_DATE AS D_DATE,
d.D_DAYOFWEEK AS D_DAYOFWEEK,
d.D_MONTH AS D_MONTH,
d.D_YEAR AS D_YEAR,
d.D_YEARMONTHNUM AS D_YEARMONTHNUM,
d.D_YEARMONTH AS D_YEARMONTH,
d.D_DAYNUMINWEEK AS D_DAYNUMINWEEK,
d.D_DAYNUMINMONTH AS D_DAYNUMINMONTH,
d.D_DAYNUMINYEAR AS D_DAYNUMINYEAR,
d.D_MONTHNUMINYEAR AS D_MONTHNUMINYEAR,
d.D_WEEKNUMINYEAR AS D_WEEKNUMINYEAR,
d.D_SELLINGSEASON AS D_SELLINGSEASON,
d.D_LASTDAYINWEEKFL AS D_LASTDAYINWEEKFL,
d.D_LASTDAYINMONTHFL AS D_LASTDAYINMONTHFL,
d.D_HOLIDAYFL AS D_HOLIDAYFL,
d.D_WEEKDAYFL AS D_WEEKDAYFL
FROM lineorder AS l
INNER JOIN customer AS c ON c.C_CUSTKEY = l.LO_CUSTKEY
INNER JOIN supplier AS s ON s.S_SUPPKEY = l.LO_SUPPKEY
INNER JOIN part AS p ON p.P_PARTKEY = l.LO_PARTKEY
INNER JOIN dates AS d ON l.LO_ORDERDATE = d.D_DATEKEY;

SSB Q2.1では、4つのテーブルを結合するクエリですが、マテリアライズドビューlineorder_flat_mvの場合、customerテーブルは含まれません。 lineorder_flat_mvでは、lineorder INNER JOIN customerは基本的にcardinality preservation結合です。したがって、論理的にはこの結合を省略してもクエリ結果に影響を与えません。結果として、Q2.1はlineorder_flat_mvを使用してリライトすることができます。

SSB Q2.1:

SELECT sum(lo_revenue) AS lo_revenue, d_year, p_brand
FROM lineorder
JOIN dates ON lo_orderdate = d_datekey
JOIN part ON lo_partkey = p_partkey
JOIN supplier ON lo_suppkey = s_suppkey
WHERE p_category = 'MFGR#12' AND s_region = 'AMERICA'
GROUP BY d_year, p_brand
ORDER BY d_year, p_brand;

その元のクエリプランとリライト後のクエリプランは次のようになります:

Rewrite-4

同様に、SSBの他のクエリもlineorder_flat_mvを使用して透過的にリライトすることができ、クエリのパフォーマンスを最適化することができます。

Join Derivabilityのリライト

Join Derivabilityは、マテリアライズドビューとクエリの結合タイプが一致しない場合でも、マテリアライズドビューの結合結果がクエリの結合結果を含んでいるシナリオを指します。現在、3つ以上のテーブルを結合する場合と2つのテーブルを結合する場合の2つのシナリオをサポートしています。

  • シナリオ1:3つ以上のテーブルを結合する場合マテリアライズドビューには、テーブルt1t2の間のLeft Outer Joinとテーブルt2t3の間のInner Joinが含まれていると想定します。両方の結合において、結合条件にはt2の列が含まれています。一方、クエリにはt1t2の間のInner Joinとt2t3の間のInner Joinが含まれています。両方の結合において、結合条件にはt2の列が含まれています。この場合、マテリアライズドビューを使用してクエリをリライトすることができます。これは、マテリアライズドビューでは、まずLeft Outer Joinが実行され、次にInner Joinが実行されます。Left Outer Joinによって生成される右テーブルは、マッチングに対して結果がない(すなわち、右テーブルの列はNULL)ため、Inner Joinの過程でこれらの結果はフィルタリングされます。したがって、マテリアライズドビューとクエリの論理は等価であり、クエリをリライトすることができます。例:以下のようにマテリアライズドビューjoin_mv5を作成します:
    CREATE MATERIALIZED VIEW join_mv5
    PARTITION BY lo_orderdate
    DISTRIBUTED BY hash(lo_orderkey)
    PROPERTIES (
    "partition_refresh_number" = "1"
    )
    AS
    SELECT lo_orderkey, lo_orderdate, lo_linenumber, lo_revenue, c_custkey, c_address, p_name
    FROM customer LEFT OUTER JOIN lineorder
    ON c_custkey = lo_custkey
    INNER JOIN part
    ON p_partkey = lo_partkey;
    join_mv5は、次のクエリをリライトすることができます:
    SELECT lo_orderkey, lo_orderdate, lo_linenumber, lo_revenue, c_custkey, c_address, p_name
    FROM customer INNER JOIN lineorder
    ON c_custkey = lo_custkey
    INNER JOIN part
    ON p_partkey = lo_partkey;
    その元のクエリプランとリライト後のクエリプランは次のようになります:
    Rewrite-5
    同様に、マテリアライズドビューがt1 INNER JOIN t2 INNER JOIN t3と定義され、クエリがLEFT OUTER JOIN t2 INNER JOIN t3である場合、クエリもリライトすることができます。さらに、このリライト機能は3つ以上のテーブルを含むシナリオにまで拡張されます。
  • シナリオ2:2つのテーブルを結合する場合2つのテーブルを結合する場合のJoin Derivability Rewrite機能は、次の具体的なケースをサポートしています。
    Rewrite-6
    ケース1から9では、リライトされた結果には意味的な等価性を保証するために、フィルタリング述語が追加されなければなりません。たとえば、次のようにマテリアライズドビューを作成します:
    CREATE MATERIALIZED VIEW join_mv3
    DISTRIBUTED BY hash(lo_orderkey)
    AS
    SELECT lo_orderkey, lo_linenumber, lo_revenue, c_custkey, c_address
    FROM lineorder LEFT OUTER JOIN customer
    ON lo_custkey = c_custkey;
    次のクエリはjoin_mv3を使用してリライトでき、リライトされた結果にはフィルタリング述語c_custkey IS NOT NULLが追加されます:
    SELECT lo_orderkey, lo_linenumber, lo_revenue, c_custkey, c_address
    FROM lineorder INNER JOIN customer
    ON lo_custkey = c_custkey;
    その元のクエリプランとリライト後のクエリプランは次のようになります:
    Rewrite-7
    ケース10では、Left Outer Joinクエリに右テーブルのフィルタリング述語IS NOT NULLを含める必要があります。たとえば、次のようにマテリアライズドビューを作成します:
    CREATE MATERIALIZED VIEW join_mv4
    DISTRIBUTED BY hash(lo_orderkey)
    AS
    SELECT lo_orderkey, lo_linenumber, lo_revenue, c_custkey, c_address
    FROM lineorder INNER JOIN customer
    ON lo_custkey = c_custkey;
    join_mv4は、次のクエリをリライトすることができます。フィルタリング述語customer.c_address = "Sb4gxKs7"はフィルタリング述語IS NOT NULLです。
    SELECT lo_orderkey, lo_linenumber, lo_revenue, c_custkey, c_address
    FROM lineorder LEFT OUTER JOIN customer
    ON lo_custkey = c_custkey
    WHERE customer.c_address = "Sb4gxKs7";
    その元のクエリプランとリライト後のクエリプランは次のようになります:
    Rewrite-8

集計のリライト

StarRocksの非同期マテリアライズドビューは、bitmap_union、hll_union、percentile_unionを含むすべての利用可能な集計関数を使用したマルチテーブルの集計クエリをリライトすることができます。たとえば、次のようにマテリアライズドビューを作成します。

CREATE MATERIALIZED VIEW agg_mv1
DISTRIBUTED BY hash(lo_orderkey)
AS
SELECT
lo_orderkey,
lo_linenumber,
c_name,
sum(lo_revenue) AS total_revenue,
max(lo_discount) AS max_discount
FROM lineorder INNER JOIN customer
ON lo_custkey = c_custkey
GROUP BY lo_orderkey, lo_linenumber, c_name;

次のクエリをリライトすることができます。

SELECT 
lo_orderkey,
lo_linenumber,
c_name,
sum(lo_revenue) AS total_revenue,
max(lo_discount) AS max_discount
FROM lineorder INNER JOIN customer
ON lo_custkey = c_custkey
GROUP BY lo_orderkey, lo_linenumber