Перезапись запросов с помощью материализованных представлений
Этот раздел описывает, как использовать асинхронные материализованные представления Selena для перезаписи и ускорения запросов.
Обзор
Асинхронное материализованное представление Selena использует широко принятый алгоритм прозрачной перезаписи запросов на основе формы SPJG (select-project-join-group-by). Без необходимости изменять оператор запроса, Selena может автоматически переписывать запросы к базовым таблицам в запросы к соответствующему материализованному представлению, которое содержит предварительно вычисленные результаты. В результате материализованные представления могут помочь вам значительно снизить вычислительные затраты и существенно ускорить выполнение запросов.
Функция перезаписи запросов на основе асинхронных материализованных представлений особенно полезна в следующих сценариях:
-
Предварительная агрегация метрик
Вы можете использовать материализованные представления для создания слоя предварительно агрегированных метрик, если вы имеете дело с данными высокой размерности.
-
Joins широких таблиц
Материализованные представления позволяют прозрачно ускорять запросы с joins нескольких больших широких таблиц в сложных сценариях.
-
Ускорение запросов в озере данных
Создание материализованного представления на основе внешнего каталога может легко ускорить запросы к данным в вашем озере данных.
NOTE
Асинхронные материализованные представления, созданные на базовых таблицах в JDBC catalog, не поддерживают перезапись запросов.
Функции
Автоматическая перезапись запросов на основе асинхронного материализованного представления Selena имеет следующие атрибуты:
- Строгая согласованность данных: Если базовые таблицы являются нативными таблицами, Selena гарантирует, что результаты, полученные через перезапись запросов на основе материализованного представления, согласованы с результатами, возвращаемыми из прямого запроса к ба зовым таблицам.
- Staleness rewrite: Selena поддерживает staleness rewrite, позволяя вам допустить определенный уровень устаревания данных для решения сценариев с частыми изменениями данных.
- Joins нескольких таблиц: Асинхронное материализованное представление Selena поддерживает различные типы joins, включая некоторые сложные сценарии join, такие как View Delta Joins и Derivable Joins, позволяя ускорять запросы в сценариях, включающих большие широкие таблицы.
- Перезапись агрегации: Selena может переписывать запросы с агрегациями для улучшения производительности отчетов.
- Вложенное материализованное представление: Selena поддерживает перезапись сложных запросов на основе вложенных материализованных представлений, расширяя область запросов, которые могут быть переписаны.
- Union rewrite: Вы можете объединить функцию Union rewrite с TTL (Time-to-Live) partitions материализованного представления для достижения разделения горячих и холодных данных, что позволяет запрашивать горячие данные из материализованных представлений, а исторические данные из базовой таблицы.
- Материализованные предста вления на представлениях: Вы можете ускорять запросы в сценариях с моделированием данных на основе представлений.
- Материализованные представления на внешних каталогах: Вы можете ускорять запросы в озерах данных.
- Перезапись сложных выражений: Может обрабатывать сложные выражения, включая вызовы функций и арифметические операции, удовлетворяя продвинутые аналитические и вычислительные требования.
Эти функции будут подробно рассмотрены в следующих разделах.
Перезапись Join
Selena поддерживает перезапись запросов с различными типами joins, включая Inner Join, Cross Join, Left Outer Join, Full Outer Join, Right Outer Join, Semi Join и Anti Join.
Следующий пример перезаписи запросов с joins. Создайте две базовые таблицы следующим образом:
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;

Selena поддерживает перезапись запросов join со сложными выражениями, такими как арифметические операции, строковые функции, функции дат, выражения 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;
В дополнение к обычному сценарию, Selena доп олнительно поддерживает перезапись запросов join в более сложных сценариях.
Перезапись Query Delta Join
Query Delta Join относится к сценарию, в котором таблицы, объединенные в запросе, являются надмножеством таблиц, объединенных в материализованном представлении. Например, рассмотрим следующий запрос, который включает joins трех таблиц: lineorder, customer и part. Если материализованное представление join_mv1 содержит только join lineorder и customer, Selena может переписать запрос, используя 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;
Его исходный план запроса и план после перезаписи следующие:

Перезапись View Delta Join
View Delta Join относится к сценарию, в котором таблицы, объединенные в запросе, являются подмножеством таблиц, объединенных в материализованном представлении. Эта функция обычно используется в сценариях, включающих большие широкие таблицы. Например, в контексте Star Schema Benchmark (SSB), вы можете создать материализованное представление, которое объединяет все таблицы для улучшения производительности запросов. Посредством тестирования было обнаружено, что производительность запросов для joins нескольких таблиц может достичь того же уровня производительности, что и запрос к соответствующей большой широкой таблице после прозрачной перезаписи запросов через материализованное представление.
Чтобы выполнить перезапись View Delta Join, материализованное представление должно содержать join сохранения кардинальности 1:1, который не существует в запросе. Вот девять типов joins, которые считаются joins сохранения кардинальности, и удовлетворение любому из них позволяет перезапись View Delta Join:

Возьмем 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)" -- Указать Foreign Keys.
);
Создайте материализованное представление lineorder_flat_mv, которое объединяет lineorder, customer, supplier, part и dates:
CREATE MATERIALIZED VIEW lineorder_flat_mv
DISTRIBUTED BY HASH(LO_ORDERDATE, LO_ORDERKEY) BUCKETS 48
PARTITION BY LO_ORDERDATE
REFRESH MANUAL
PROPERTIES (
"partition_refresh_number"="1"
)
AS SELECT /*+ SET_VAR(query_timeout = 7200) */ -- Установить timeout для операции обновления.
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 включает join четырех таблиц, но в нем отсутствует таблица customer по сравнению с материализованным представлением lineorder_flat_mv. В lineorder_flat_mv, lineorder INNER JOIN customer по сути является join сохранения кардинальности. Следовательно, логически этот join может быть исключен без влияния на результаты запроса. В результате 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;
Его исходный план запроса и план после перезаписи следующие:

Аналогично, другие запросы в SSB также могут быть прозрачно переписаны, используя lineorder_flat_mv, таким образом оптимизируя производительность запросов.
Перезапись Join Derivability
Join Derivability относится к сценарию, в котором типы join в материализованном представлении и запросе не согласованы, но результаты join материализованного представления содержат результаты join запроса. В настоящее время поддерживаются два сценария — join трех или более таблиц и join двух таблиц.
-
Сценарий первый: Join трех или более таблиц
Предположим, что материализованное представление содержит Left Outer Join между таблицами
t1иt2и Inner Join между таб лицамиt2иt3. В обоих joins условие join включает столбцы изt2.Запрос, с другой стороны, содержит Inner Join между t1 и t2, и Inner Join между t2 и t3. В обоих joins условие 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;Его исходный план запроса и план после перезаписи следующие:

Аналогично, если материализованное представление определено как
t1 INNER JOIN t2 INNER JOIN t3, а запрос —LEFT OUTER JOIN t2 INNER JOIN t3, запрос также может быть переписан. Кроме того, эта возможность перезаписи распространяется на сценарии, включающие более трех таблиц. -
Сценарий второй: Join двух таблиц
Функция Join Derivability Rewrite, включающая две таблицы, поддерживает следующие конкретные случаи:

В случаях с 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;Его исходный план запроса и план после перезаписи следующие:

В случае 10, Left Outer Join запрос должен включать фильтрующий предикат
IS NOT NULLв правой таблице, например,=,<>,>,<,<=,>=,LIKE,IN,NOT LIKEилиNOT IN. Например, создайте материализованное представление следующим образом: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";Его исходный план запроса и план после перезаписи следующие:

Перезапись агрегации
Асинхронное материализованное представление Selena поддерживает перезапись многотабличных агрегатных запросов со всеми доступными агрегатными функциями, включая 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, c_name;
Его исходный план запроса и план после перезаписи следующие:

Следующие разделы излагают сценарии, г де функция Aggregation Rewrite может быть полезной.
Перезапись Aggregation Rollup
Selena поддерживает перезапись запросов с Aggregation Rollup, то есть Selena может переписывать агрегатные запросы с оператором GROUP BY a, используя асинхронное материализованное представление, созданное с оператором GROUP BY a,b. Например, следующий запрос может быть переписан, используя agg_mv1:
SELECT
lo_orderkey,
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, c_name;
Его исходный план запроса и план после перезаписи следующие:

NOTE
В настоящее время перезапись grouping set, grouping set with rollup или grouping set with cube не поддерживается.
Только определенные агрегатные функции поддерживают перезапись запросов с Aggregate Rollup. В предыдущем примере, если материализованное представление order_agg_mv использует count(distinct client_id) вместо bitmap_union(to_bitmap(client_id)), Selena не может переписать запросы с Aggregate Rollup.
Следующая таблица показывает соответствие между агрегатными функциями в исходном запросе и агрегатной функцией, используемой для построения материализованного представления. Вы можете выбрать соответствующие агрегатные функции для построения материализованного представления в соответствии с вашим бизнес-сценарием.
| Агрегатная функция, поддерживаемая в исходных запросах | Функция, поддерживающая Aggregate Rollup в материализованном представлении |
|---|---|
| sum | sum |
| count | count |
| min | min |
| max | max |
| avg | sum / count |
| bitmap_union, bitmap_union_count, count(distinct) | bitmap_union |
| hll_raw_agg, hll_union_agg, ndv, approx_count_distinct | hll_union |
| percentile_approx, percentile_union | percentile_union |
DISTINCT агрегаты без соответствующего столбца GROUP BY не могут быть переписаны с Aggregate Rollup. Однако, начиная с Selena v1.5.2, если запрос с Aggregate Rollup DISTINCT агрегатной функцией не имеет столбца GROUP BY, но имеет равенственный предикат, он также может быть переписан соответствующим материализованным представлением, потому что Selena может преобразовать равенственные предикаты в выражение константы GROUP BY.
В следующем примере Selena может переписать запрос с помощью материализованного представления order_agg_mv1.
CREATE MATERIALIZED VIEW order_agg_mv1
DISTRIBUTED BY HASH(`order_id`) BUCKETS 12
REFRESH ASYNC START('2022-09-01 10:00:00') EVERY (interval 1 day)
AS
SELECT
order_date,
count(distinct client_id)
FROM order_list
GROUP BY order_date;
-- Запрос
SELECT
order_date,
count(distinct client_id)
FROM order_list WHERE order_date='2023-07-03';
В дополнение к указанным выше функциям, начиная с Selena v1.5.2, асинхронные материализованные представления также поддерживают универсальные агрегатные функции, которые также могут использоваться для перезаписи запросов. Для получения дополнительной информации об универсальных агрегатных функциях см. Generic aggregate function states.
-- Создать асинхронное материализованное представление test_mv2 для хране ния aggregate states.
CREATE MATERIALIZED VIEW test_mv2
PARTITION BY (dt)
DISTRIBUTED BY RANDOM
AS
SELECT
dt,
-- Исходные агрегатные функции.
min(id) AS min_id,
max(id) AS max_id,
sum(id) AS sum_id,
bitmap_union(to_bitmap(id)) AS bitmap_union_id,
hll_union(hll_hash(id)) AS hll_union_id,
percentile_union(percentile_hash(id)) AS percentile_union_id,
-- Generic aggregate state функции.
ds_hll_count_distinct_union(ds_hll_count_distinct_state(id)) AS hll_id,
avg_union(avg_state(id)) AS avg_id,
array_agg_union(array_agg_state(id)) AS array_agg_id,
min_by_union(min_by_state(province, id)) AS min_by_province_id
FROM t1
GROUP BY dt;
-- Обновить материализованное представление.
REFRESH MATERIALIZED VIEW test_mv2 WITH SYNC MODE;
-- Прямые запросы к агрегатной функции будут прозрачно ускорены с помощью test_mv2.
SELECT
dt,
min(id),
max(id),
sum(id),
bitmap_union_count(to_bitmap(id)), -- count(distinct id)
hll_union_agg(hll_hash(id)), -- approx_count_distinct(id)
percentile_approx(id, 0.5),
ds_hll_count_distinct(id),
avg(id),
array_agg(id),
min_by(province, id)
FROM t1
WHERE dt >= '2024-01-01'
GROUP BY dt;
SELECT
min(id),
max(id),
sum(id),
bitmap_union_count(to_bitmap(id)), -- count(distinct id)
hll_union_agg(hll_hash(id)), -- approx_count_distinct(id)
percentile_approx(id, 0.5),
ds_hll_count_distinct(id),
avg(id),
array_agg(id),
min_by(province, id)
FROM t1
WHERE dt >= '2024-01-01';
Pushdown агрегации
Начиная с v1.5.2, Selena поддерживает pushdown агрегации для перезаписи запросов материализованного представления. Когда эта функция включена, агрегатные функции будут проталкиваться вниз к Scan Operator во время выполнения запроса и переписываться материализованным представлением до выполнения Join Operator. Это снизит расширение данных, вызванное Join, и тем самым улучшит производительность запросов.
Эта функция отключена по умолчанию. Чтобы включить эту функцию, вы должны установить системную переменную enable_materialized_view_agg_pushdown_rewrite в true.
Предположим, вы хотите ускорить следующий запрос на основе SSB SQL1:
-- SQL1
SELECT
LO_ORDERDATE, sum(LO_REVENUE), max(LO_REVENUE), count(distinct LO_REVENUE)
FROM lineorder l JOIN dates d
ON l.LO_ORDERDATE = d.d_date
GROUP BY LO_ORDERDATE
ORDER BY LO_ORDERDATE;
SQL1 состоит из агрегаций для таблицы lineorder и Join lineorder и dates. Поскольку агрегации происходят внутри lineorder, а Join с dates используется только для фильтрации данных, SQL1 логически эквивалентен следующему SQL2:
-- SQL2
SELECT
LO_ORDERDATE, sum(sum1), max(max1), bitmap_union_count(bitmap1)
FROM
(SELECT
LO_ORDERDATE, sum(LO_REVENUE) AS sum1, max(LO_REVENUE) AS max1, bitmap_union(to_bitmap(LO_REVENUE)) AS bitmap1
FROM lineorder
GROUP BY LO_ORDERDATE) l JOIN dates d
ON l.LO_ORDERDATE = d.d_date
GROUP BY LO_ORDERDATE
ORDER BY LO_ORDERDATE;
SQL2 выносит агрегации вперед, тем самым уменьшая размер данных Join. Вы можете создать материализованное представление на основе подзапроса SQL2 и включить pushdown агрегации для перезаписи и ускорения агрегаций:
-- Создать материализованное представление mv0
CREATE MATERIALIZED VIEW mv0 REFRESH MANUAL AS
SELECT
LO_ORDERDATE,
sum(LO_REVENUE) AS sum1,
max(LO_REVENUE) AS max1,
bitmap_union(to_bitmap(LO_REVENUE)) AS bitmap1
FROM lineorder
GROUP BY LO_ORDERDATE;
-- Включить pushdown агрегации для перезаписи запросов материализованного представления
SET enable_materialized_view_agg_pushdown_rewrite=true;
Затем SQL1 будет переписан и ускорен материализованным представлением. Он переписывается в следующий запрос:
SELECT
LO_ORDERDATE, sum(sum1), max(max1), bitmap_union_count(bitmap1)
FROM
(SELECT LO_ORDERDATE, sum1, max1, bitmap1 FROM mv0) l JOIN dates d
ON l.LO_ORDERDATE = d.d_date
GROUP BY LO_ORDERDATE
ORDER BY LO_ORDERDATE;
Обратите внимание, что только определенные агрегатные функции, которые поддерживают перезапись Aggregate Rollup, подходят для pushdown. Это:
- MIN
- MAX
- COUNT
- COUNT DISTINCT
- SUM
- BITMAP_UNION
- HLL_UNION
- PERCENTILE_UNION
- BITMAP_AGG
- ARRAY_AGG_DISTINCT
- После pushdown агрегатные функции должны быть свернуты для соответствия исходной семантике. Для получения дополнительных инструкций по Aggregation Rollup, пожалуйста, обратитесь к Aggregation Rollup Rewrite.
- Pushdown агрегации поддерживает перезапись Rollup Count Distinct на основе функций Bitmap или HLL.
- Pushdown агрегации поддерживает только проталкивание агрегатных функций вниз к Scan Operator перед операторами Join, Filter или Where.
- Pushdown агрегации поддерживает только перезапись запросов и ускорение на основе материализованного представления, построенного на одной таблице.
Перезапись COUNT DISTINCT
Selena поддерживает перезапись вычислений COUNT DISTINCT в вычисления на основе bitmap, обеспечивая высокопроизводительную точную дедупликацию с использованием материализованных представлений. Например, создайте материализованное представление следующим образом:
CREATE MATERIALIZED VIEW distinct_mv
DISTRIBUTED BY hash(lo_orderkey)
AS
SELECT lo_orderkey, bitmap_union(to_bitmap(lo_custkey)) AS distinct_customer
FROM lineorder
GROUP BY lo_orderkey;
Оно может переписать следующий запрос:
SELECT lo_orderkey, count(distinct lo_custkey)
FROM lineorder
GROUP BY lo_orderkey;
Перезапись вложенного материализованного представления
Selena поддерживает перезапись запросов с использованием вложенного материализованного представления. Например, создайте материализованные представления join_mv2, agg_mv2 и agg_mv3 следующим образом:
CREATE MATERIALIZED VIEW join_mv2
DISTRIBUTED BY hash(lo_orderkey)
AS
SELECT lo_orderkey, lo_linenumber, lo_revenue, c_name, c_address
FROM lineorder INNER JOIN customer
ON lo_custkey = c_custkey;
CREATE MATERIALIZED VIEW agg_mv2
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 join_mv2
GROUP BY lo_orderkey, lo_linenumber, c_name;
CREATE MATERIALIZED VIEW agg_mv3
DISTRIBUTED BY hash(lo_orderkey)
AS
SELECT
lo_orderkey,
sum(total_revenue) AS total_revenue,
max(max_discount) AS max_discount
FROM agg_mv2
GROUP BY lo_orderkey;
Их взаимоотношения следующие:
agg_mv3 может переписать следующий запрос:
SELECT
lo_orderkey,
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;
Его исходный план запроса и план после перезаписи следующие:

Перезапись Union
Перезапись Predicate Union
Когда область предиката материализованного представления является подмножеством области предиката запроса, запрос может быть переписан с использованием операции UNION.
Например, создайте материализованное представление следующим образом:
CREATE MATERIALIZED VIEW agg_mv4
DISTRIBUTED BY hash(lo_orderkey)
AS
SELECT
lo_orderkey,
sum(lo_revenue) AS total_revenue,
max(lo_discount) AS max_discount
FROM lineorder
WHERE lo_orderkey < 300000000
GROUP BY lo_orderkey;
Оно может переписать следующий запрос:
select
lo_orderkey,
sum(lo_revenue) AS total_revenue,
max(lo_discount) AS max_discount
FROM lineorder
GROUP BY lo_orderkey;
Его исходный план запроса и план после перезаписи следующие:

В этом контексте agg_mv5 содержит данные, где lo_orderkey < 300000000. Данные, где lo_orderkey >= 300000000, напрямую получаются из базовой таблицы lineorder. Наконец, эти два набора данных объединяются с использованием операции UNION, а затем агрегируются для получения окончательного результата.
Перезапись Partition Union
Предположим, вы создали partitioned материализованное представление на основе partitioned таблицы. Когда диапазон partition, который сканируется переписываемым запросом, является надмножеством самого последнего диапазона partition материализованного представления, запрос будет переписан с использованием операции UNION.
Например, расс мотрим следующее материализованное представление agg_mv4. Его базовая таблица lineorder в настоящее время содержит partitions от p1 до p7, и материализованное представление также содержит partitions от p1 до p7.
CREATE MATERIALIZED VIEW agg_mv5
DISTRIBUTED BY hash(lo_orderkey)
PARTITION BY RANGE(lo_orderdate)
REFRESH MANUAL
AS
SELECT
lo_orderdate,
lo_orderkey,
sum(lo_revenue) AS total_revenue,
max(lo_discount) AS max_discount
FROM lineorder
GROUP BY lo_orderkey;
Если новый partition p8 с диапазоном partition [("19990101"), ("20000101")) добавляется к lineorder, следующий запрос может быть переписан с использованием операции UNION:
SELECT
lo_orderdate,
lo_orderkey,
sum(lo_revenue) AS total_revenue,
max(lo_discount) AS max_discount
FROM lineorder
GROUP BY lo_orderkey;
Его исходный план запроса и план после перезаписи следующие:

Как показано выше, agg_mv5 содержит данные из partitions от p1 до p7, а данные из partition p8 напрямую запрашиваются из lineorder. Наконец, эти два набора данных объединяются с использованием операции UNION.