本文將為您介紹維表關聯的使用。
維表關聯(Lookup Join)
Lookup Join是維表 Join的一種實現方式,詳情請參見維表JOIN語句。
僅Fluss的主鍵表支援作為維表進行join,且join條件需包含維表的全部主鍵。如果建表時將Bucket Key設定為join key,join條件也可以是主鍵的首碼子集,詳情請參見基於Bucket Key的維表查詢。
維表為分區主鍵表時,join條件還需包含分區鍵。
Fluss的維表關聯預設使用非同步模式,吞吐更高,可通過 SQL Hint設定
'lookup.async' = 'false',將維表關聯改成同步模式。
關聯兩個維表的SQL樣本
建立兩個維表
USE CATALOG `fluss_catalog`;
USE `my_db`;
CREATE TABLE `orders` (
`o_orderkey` INT NOT NULL,
`o_custkey` INT NOT NULL,
`o_orderstatus` CHAR(1) NOT NULL,
`o_totalprice` DECIMAL(15, 2) NOT NULL,
`o_orderdate` DATE NOT NULL,
`o_orderpriority` CHAR(15) NOT NULL,
`o_clerk` CHAR(15) NOT NULL,
`o_shippriority` INT NOT NULL,
`o_comment` STRING NOT NULL,
`o_dt` STRING NOT NULL,
PRIMARY KEY (o_orderkey) NOT ENFORCED
);
CREATE TABLE `customer` (
`c_custkey` INT NOT NULL,
`c_name` STRING NOT NULL,
`c_address` STRING NOT NULL,
`c_nationkey` INT NOT NULL,
`c_phone` CHAR(15) NOT NULL,
`c_acctbal` DECIMAL(15, 2) NOT NULL,
`c_mktsegment` CHAR(10) NOT NULL,
`c_comment` STRING NOT NULL,
PRIMARY KEY (c_custkey) NOT ENFORCED
);兩個維表關聯
CREATE TEMPORARY TABLE `lookup_join_sink` (
`order_key` INT NOT NULL,
`order_totalprice` DECIMAL(15, 2) NOT NULL,
`customer_name` STRING NOT NULL,
`customer_address` STRING NOT NULL
) WITH ('connector' = 'blackhole');
-- 非同步模式(預設)
INSERT INTO `lookup_join_sink`
SELECT `o`.`o_orderkey`, `o`.`o_totalprice`, `c`.`c_name`, `c`.`c_address`
FROM (SELECT `orders`.*, PROCTIME() AS `ptime` FROM `orders`) AS `o`
LEFT JOIN `customer` FOR SYSTEM_TIME AS OF `o`.`ptime` AS `c`
ON `o`.`o_custkey` = `c`.`c_custkey`;同步模式關聯
-- 同步模式
INSERT INTO `lookup_join_sink`
SELECT `o`.`o_orderkey`, `o`.`o_totalprice`, `c`.`c_name`, `c`.`c_address`
FROM (SELECT `orders`.*, PROCTIME() AS `ptime` FROM `orders`) AS `o`
LEFT JOIN `customer` /*+ OPTIONS('lookup.async' = 'false') */ FOR SYSTEM_TIME AS OF `o`.`ptime` AS `c`
ON `o`.`o_custkey` = `c`.`c_custkey`;分區表維表關聯
維表為分區主鍵表時,join條件需包含全部主鍵,即主鍵與分區鍵。
-- 主鍵為 (c_custkey, dt),分區鍵為 dt
CREATE TABLE `customer_partitioned` (
`c_custkey` INT NOT NULL,
`c_name` STRING NOT NULL,
`c_address` STRING NOT NULL,
`c_nationkey` INT NOT NULL,
`c_phone` CHAR(15) NOT NULL,
`c_acctbal` DECIMAL(15, 2) NOT NULL,
`c_mktsegment` CHAR(10) NOT NULL,
`c_comment` STRING NOT NULL,
`dt` STRING NOT NULL,
PRIMARY KEY (`c_custkey`, `dt`) NOT ENFORCED
)
PARTITIONED BY (`dt`)
WITH (
'table.auto-partition.enabled' = 'true',
'table.auto-partition.time-unit' = 'year'
);join條件中除主鍵外,還需帶上分區鍵。
INSERT INTO `lookup_join_sink`
SELECT `o`.`o_orderkey`, `o`.`o_totalprice`, `c`.`c_name`, `c`.`c_address`
FROM (SELECT `orders`.*, PROCTIME() AS `ptime` FROM `orders`) AS `o`
LEFT JOIN `customer_partitioned` FOR SYSTEM_TIME AS OF `o`.`ptime` AS `c`
ON `o`.`o_custkey` = `c`.`c_custkey` AND `o`.`o_dt` = `c`.`dt`;分區表的詳情請參見資料分區。
基於Bucket Key的維表查詢(Prefix Lookup)
功能概述
主鍵表作為維表時,如果建表時將Bucket Key設定為join key,join條件可以是維表主鍵的首碼子集,無需包含全部主鍵,即Prefix Lookup。
Prefix Lookup預設使用非同步模式,吞吐更高,可通過 SQL Hint設定'lookup.async' = 'false',改成同步模式。
使用限制
僅Fluss的主鍵表支援作為維表進行Prefix Lookup。
建表時必須將Bucket Key設定為join key。Bucket Key的設定方式請參見資料分桶。
join條件必須是維表主鍵的首碼子集。分區主鍵表的join條件為主鍵首碼(不含分區鍵)加分區鍵。
不支援與
lookup.insert-if-not-exists同時使用。
使用樣本
分區表
維表為分區主鍵表時,join條件為主鍵首碼(不含分區鍵)加分區鍵。以下樣本中維表主鍵為 (c_custkey, c_nationkey, dt),Bucket Key為 c_custkey,分區鍵為 dt。
-- 主鍵為 (c_custkey, c_nationkey, dt),Bucket Key 為 c_custkey,分區鍵為 dt
CREATE TABLE `customer_partitioned_with_bucket_key` (
`c_custkey` INT NOT NULL,
`c_name` STRING NOT NULL,
`c_address` STRING NOT NULL,
`c_nationkey` INT NOT NULL,
`c_phone` CHAR(15) NOT NULL,
`c_acctbal` DECIMAL(15, 2) NOT NULL,
`c_mktsegment` CHAR(10) NOT NULL,
`c_comment` STRING NOT NULL,
`dt` STRING NOT NULL,
PRIMARY KEY (`c_custkey`, `c_nationkey`, `dt`) NOT ENFORCED
)
PARTITIONED BY (`dt`)
WITH (
'bucket.key' = 'c_custkey',
'table.auto-partition.enabled' = 'true',
'table.auto-partition.time-unit' = 'year'
);-- join條件為主鍵首碼(不含分區鍵)+ 分區鍵
INSERT INTO `lookup_join_sink`
SELECT `o`.`o_orderkey`, `o`.`o_totalprice`, `c`.`c_name`, `c`.`c_address`
FROM (SELECT `orders`.*, PROCTIME() AS `ptime` FROM `orders`) AS `o`
LEFT JOIN `customer_partitioned_with_bucket_key` FOR SYSTEM_TIME AS OF `o`.`ptime` AS `c`
ON `o`.`o_custkey` = `c`.`c_custkey` AND `o`.`o_dt` = `c`.`dt`;非分區表
以下樣本中維表主鍵為 (c_custkey, c_nationkey),Bucket Key為 c_custkey,join條件僅使用主鍵首碼 c_custkey。
-- 主鍵為 (c_custkey, c_nationkey),Bucket Key 為 c_custkey
CREATE TABLE `customer_with_bucket_key` (
`c_custkey` INT NOT NULL,
`c_name` STRING NOT NULL,
`c_address` STRING NOT NULL,
`c_nationkey` INT NOT NULL,
`c_phone` CHAR(15) NOT NULL,
`c_acctbal` DECIMAL(15, 2) NOT NULL,
`c_mktsegment` CHAR(10) NOT NULL,
`c_comment` STRING NOT NULL,
PRIMARY KEY (`c_custkey`, `c_nationkey`) NOT ENFORCED
) WITH (
'bucket.key' = 'c_custkey'
);-- Prefix Lookup,非同步模式(預設)
INSERT INTO `lookup_join_sink`
SELECT `o`.`o_orderkey`, `o`.`o_totalprice`, `c`.`c_name`, `c`.`c_address`
FROM (SELECT `orders`.*, PROCTIME() AS `ptime` FROM `orders`) AS `o`
LEFT JOIN `customer_with_bucket_key` FOR SYSTEM_TIME AS OF `o`.`ptime` AS `c`
ON `o`.`o_custkey` = `c`.`c_custkey`;-- Prefix Lookup,同步模式
INSERT INTO `lookup_join_sink`
SELECT `o`.`o_orderkey`, `o`.`o_totalprice`, `c`.`c_name`, `c`.`c_address`
FROM (SELECT `orders`.*, PROCTIME() AS `ptime` FROM `orders`) AS `o`
LEFT JOIN `customer_with_bucket_key` /*+ OPTIONS('lookup.async' = 'false') */ FOR SYSTEM_TIME AS OF `o`.`ptime` AS `c`
ON `o`.`o_custkey` = `c`.`c_custkey`;歷史分區查詢(Historical Partition Lookup)
功能概述
自動分區會按保留原則刪除到期分區。分區刪除後,即使資料已分層至Paimon,引用該分區的維表關聯也查不到資料。
開啟歷史分區查詢後,主鍵Lookup在原始分區不存在時回退至Paimon查詢。在維表上開啟table.datalake.historical-partition.enabled即可。
使用限制
預設關閉,需在維表上開啟
table.datalake.historical-partition.enabled。僅支援已開啟自動分區、且只有一個分區鍵的Paimon主鍵表。
開啟後Coordinator會建立並保留
__historical__系統磁碟分割,用於將Lookup路由至Paimon。關閉該配置會刪除該系統磁碟分割。Lookup用戶端使用Lookuper建立時擷取的表配置判斷是否回退。修改配置後,需重啟需要查詢歷史分區的Lookup作業。
使用樣本
在維表上開啟歷史分區查詢。
ALTER TABLE `customer_partitioned_with_bucket_key` SET (
'table.datalake.historical-partition.enabled' = 'true'
);參數詳情請參見Connector 參數。
Insert If Not Exists
功能概述
在執行 Lookup Join 時,如果尋找鍵在維表中沒有匹配的行,預設行為是跳過關聯(對於 LEFT JOIN,維表側返回 NULL)。通過啟用 lookup.insert-if-not-exists 選項,當尋找未命中時,Fluss 會自動插入一條以尋找鍵為主鍵的新行,並將新插入的行作為關連接果返回。
該功能特別適合與主鍵表結合使用,在流處理過程中自動構建字典表。典型情境是將高基數的字串標識符(如使用者識別碼、裝置識別碼)映射為緊湊的整數 ID,用於下遊高效的 Aggregation Merge Engine。
使用限制
僅支援主鍵 Lookup,不支援 Prefix Lookup。
維表中除主鍵列和自增列外,不能包含非空(NOT NULL)列。因為自動插入時 Fluss 無法為這些列填儲值。
通過 SQL Hint 啟用:
/*+ OPTIONS('lookup.insert-if-not-exists' = 'true') */
使用樣本
以下樣本示範如何在 Lookup Join 中自動構建 UID 字典表。
建立帶自增列的字典表。
CREATE TABLE uid_mapping (
uid VARCHAR NOT NULL,
uid_int32 INT,PRIMARY KEY (uid) NOT ENFORCED
) WITH ('auto-increment.fields' = 'uid_int32','bucket.num' = '1');執行帶
insert-if-not-exists的 Lookup Join。當某個uid首次出現時,Fluss 自動將其插入uid_mapping並分配一個自增的uid_int32值。
-- 流表 ods_events 中的 uid 自動註冊到字典表 uid_mapping,-- 並擷取對應的整型 ID uid_int32SELECT
ods.country,
ods.prov,
ods.city,
ods.ymd,
ods.uid,
dim.uid_int32
FROM ods_events AS ods
JOIN uid_mapping /*+ OPTIONS('lookup.insert-if-not-exists' = 'true') */FOR SYSTEM_TIME AS OF ods.proctime AS dim
ON dim.uid = ods.uid;假設 ods_events 包含以下資料:
country | prov | city | ymd | uid |
CN | Beijing | Haidian | 2025-01-01 | user_a |
CN | Shanghai | Pudong | 2025-01-02 | user_b |
US | California | LA | 2025-01-03 | user_a |
JP | Tokyo | Shibuya | 2025-01-04 | user_c |
關連接果為:
country | prov | city | ymd | uid | uid_int32 |
CN | Beijing | Haidian | 2025-01-01 | user_a | 1 |
CN | Shanghai | Pudong | 2025-01-02 | user_b | 2 |
US | California | LA | 2025-01-03 | user_a | 1 |
JP | Tokyo | Shibuya | 2025-01-04 | user_c | 3 |
user_a首次出現時獲得uid_int32 = 1,第二次出現時複用相同值。user_b和user_c各獲得一個新的自增 ID。
作業運行後,uid_mapping 字典表內容為:
uid | uid_int32 |
user_a | 1 |
user_b | 2 |
user_c | 3 |
相關文檔
維表關聯支援的配置項,詳情請參見Connector 參數。
Bucket Key的設定方式,詳情請參見資料分桶。
自動分區與分區管理原則,詳情請參見資料分區。