全部產品
Search
文件中心

Realtime Compute for Apache Flink:維表關聯

更新時間:Sep 12, 2026

本文將為您介紹維表關聯的使用。

維表關聯(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 字典表。

  1. 建立帶自增列的字典表。

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');
  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

相關文檔