核心结论
表面语法相似(均用到 FOR SYSTEM_TIME AS OF),但本质逻辑、适用场景和维表特性完全不同 ------ 形似神异,核心区别在于维表的 "版本化特性" 和 FOR SYSTEM_TIME AS OF 的实际作用。
一、语法 "形似":表面都用 FOR SYSTEM_TIME AS OF
两者在 SQL 中确实共享类似的语法结构,均通过 FOR SYSTEM_TIME AS OF 关联流与维表,导致视觉上相似:
1. Lookup Join 典型语法
sql
SELECT ...
FROM 流表 t1
LEFT JOIN 维表 t2 FOR SYSTEM_TIME AS OF t1.proc_time -- 通常关联处理时间
ON t1.key = t2.key;
2. Temporal Join 典型语法
sql
-- 版本化时态 Join(事件时间)
SELECT ...
FROM 流表 t1
LEFT JOIN 维表 t2 FOR SYSTEM_TIME AS OF t1.event_time -- 关联流的事件时间
ON t1.key = t2.key;
-- 处理时间时态 Join
SELECT ...
FROM 流表 t1
LEFT JOIN 维表 t2 FOR SYSTEM_TIME AS OF t1.proc_time -- 关联流的处理时间
ON t1.key = t2.key;
二、本质 "神异":5 大核心区别(含语法作用差异)
| 对比维度 | Lookup Join(查找 Join) | Temporal Join(时态 Join) |
|---|---|---|
| 核心目标 | 流主动查询静态 / 缓慢变化的维表,补充静态属性(如商品名称、IP 归属地)。 | 流关联维表的特定版本(历史版本或处理时最新版本),解决 "维表更新后回溯历史状态" 问题。 |
| 维表特性 | 维表是静态 / 缓慢变化的(如 MySQL 商品表、HBase IP 表),无版本化管理,仅需 "最新或缓存数据"。 | 维表是版本化的(Temporal Table),需记录数据的 "生效时间范围"(事件时间)或 "实时更新"(处理时间)。 |
FOR SYSTEM_TIME AS OF 的作用 |
仅用于对齐处理时间,避免 "流数据处理时维表正在刷新" 导致的并发问题(如脏读),无 "版本回溯" 意义。 | 核心作用是指定维表版本的时间基准 :- **事件时间:获取 "流事件发生时刻" 的维表历史版本;- 处理时间:**获取 "流数据处理时刻" 的维表最新版本。 |
| 维表数据来源 | 维表通常是外部存储系统(如 MySQL、HBase、文件),流通过 "查询" 获取数据(可缓存)。 | 维表通常是流或版本化表(如 Kafka CDC 流、带生效时间的 Hive 表),需持续维护版本历史。 |
| 状态管理 | 仅缓存维表数据(可选,通过 lookup.cache 配置),流数据不存状态;状态大小由缓存策略控制。 |
需保存维表的版本历史状态 (事件时间)或最新版本状态(处理时间);状态大小由版本数量控制。 |
| 典型场景 | 订单流关联 MySQL 商品表获取商品名称(商品信息一天更新一次)。 | 订单流关联商品价格表获取 "下单时刻" 的历史价格(价格随促销实时变化)。 |
三、关键区分:从 "维表是否版本化" 判断
最核心的判断标准是维表是否需要 "版本化管理":
- 若维表是静态 / 缓慢变化,无需记录历史版本 (如用户基础信息、商品分类)→ 用 Lookup Join;
- 若维表是动态变化,需关联特定时间版本 (如商品价格、用户等级的历史状态)→ 用 Temporal Join。
四、语法细节差异示例(直观对比)
1. Lookup Join:维表无版本化,仅配置缓存
sql
-- 维表:MySQL商品表(静态,无时间属性)
CREATE TEMPORARY TABLE Product_Lookup (
goods_id INT,
goods_name STRING,
category STRING,
PRIMARY KEY (goods_id) NOT ENFORCED -- 仅用于关联,无版本意义
) WITH (
'connector' = 'jdbc',
'url' = 'jdbc:mysql://xxx:3306/test',
'table-name' = 'product',
'lookup.cache.ttl' = '3600000' -- 缓存1小时,无需版本管理
);
-- Lookup Join:关联处理时间避免并发问题
SELECT o.order_id, o.goods_id, p.goods_name
FROM OrderStream o
LEFT JOIN Product_Lookup FOR SYSTEM_TIME AS OF o.proc_time p
ON o.goods_id = p.goods_id;
2. Temporal Join:维表版本化,需时间属性
sql
-- 维表:版本化商品价格表(事件时间,记录生效/失效时间)
CREATE TEMPORARY TABLE Product_Temporal (
goods_id INT,
price DECIMAL(10,2),
start_time TIMESTAMP(3), -- 价格生效时间
end_time TIMESTAMP(3), -- 价格失效时间(NULL表示当前)
WATERMARK FOR start_time AS start_time - INTERVAL '1' SECOND, -- 事件时间水印
PRIMARY KEY (goods_id) NOT ENFORCED -- 版本化主键
) WITH (
'connector' = 'kafka-cdc', -- 从CDC流获取版本变化
'topic' = 'product_price_cdc',
'properties.bootstrap.servers' = 'xxx:9092',
'format' = 'debezium-json'
);
-- Temporal Join:关联订单事件时间,获取历史价格
SELECT o.order_id, o.goods_id, o.order_time, p.price
FROM OrderStream o
LEFT JOIN Product_Temporal FOR SYSTEM_TIME AS OF o.order_time p
ON o.goods_id = p.goods_id;
总结
两者语法的相似性是 "表面巧合",核心差异在于维表特性和 FOR SYSTEM_TIME AS OF 的作用:Lookup Join 是 "流查静态维表",语法仅为解决并发;Temporal Join 是 "流关联维表版本",语法是版本选择的核心
推荐阅读
