普通表
普通表(Table)是 Lakehouse 中存储数据的基本单元。与 MySQL 等行式数据库不同,Lakehouse 的表采用 Parquet 列式存储,数据按列组织并压缩。查询时只需读取相关的列,大幅减少 I/O。与 Hive 的静态分区不同,Lakehouse 采用类似 Iceberg 的隐藏分区机制,分区策略可以在不影响数据的情况下修改。
普通表像一个"手动管理的数据容器"——你负责写入数据,系统负责高效存储和查询。如果需要基于上游数据增量计算加工结果,用 Dynamic Table;如果只需透明加速已有查询,用 物化视图。
核心特性
列式存储:默认使用 Parquet 格式,查询时只读取需要的列,适合大规模分析。
主键支持:定义主键后,实时写入时系统自动按主键去重,适合 CDC 同步场景。默认行为为
ENABLE VALIDATE RELY
ENABLE VALIDATE RELY
,SQL 写入也会进行主键检查。
Time Travel:基于 MVCC 版本管理,保留历史版本数据,支持查询历史时间点的数据状态。
分区与分桶:支持隐藏分区(类似 Iceberg)和哈希分桶(CLUSTER BY),优化查询性能。
何时使用普通表?
适用场景
| 场景 | 原因 |
|---|
| ODS 层原始数据接入 | 需要精确控制写入时机,保留原始数据 |
| 维度表 | 数据量小,更新不频繁,需要 JOIN 加速 |
| CDC 同步目标表 | 同步任务直接写入,配合 Table Stream 消费变更 |
| 临时数据暂存 | 快速写入,后续被 DT 或任务消费 |
不适用场景
| 场景 | 推荐替代方案 | 原因 |
|---|
| 需要自动刷新的聚合结果 | Dynamic Table | 普通表需要手动维护数据 |
| 频繁查询的中间结果 | 物化视图 | 物化视图透明加速,无需改 SQL |
| 外部数据联邦查询 | 外部表 | 无需将数据迁入 Lakehouse |
快速示例
创建带分区的主键表
-- 创建表
CREATE TABLE orders (
order_id BIGINT PRIMARY KEY,
user_id BIGINT NOT NULL,
amount DECIMAL(10,2),
created_at TIMESTAMP
)
PARTITIONED BY (days(created_at));
-- 插入数据
INSERT INTO orders VALUES (1, 101, 99.00, '2024-01-15 10:30:00');
INSERT INTO orders VALUES (2, 102, 199.00, '2024-01-16 14:20:00');
-- 查询
SELECT * FROM orders WHERE created_at >= '2024-01-15';
+----------+---------+--------+---------------------+
| order_id | user_id | amount | created_at |
+----------+---------+--------+---------------------+
| 1 | 101 | 99.00 | 2024-01-15 10:30:00 |
| 2 | 102 | 199.00 | 2024-01-16 14:20:00 |
+----------+---------+--------+---------------------+
⚠️ 注意:主键默认行为为
ENABLE VALIDATE RELY
ENABLE VALIDATE RELY
,SQL 写入也会进行主键检查。如果希望仅实时写入去重而 SQL 写入不去重,使用
PRIMARY KEY DISABLE NOVALIDATE RELY
PRIMARY KEY DISABLE NOVALIDATE RELY
。
表设计最佳实践
1. 分区设计
原则:按查询中最常用的过滤字段分区,通常是时间字段。使用转换分区(days/months/years)降低基数。
-- ✅ 好:按天分区,适合时间范围查询
CREATE TABLE orders (
order_id BIGINT,
amount DECIMAL(10,2),
created_at TIMESTAMP
) PARTITIONED BY (days(created_at));
-- ❌ 差:按用户 ID 分区,基数过高
CREATE TABLE orders (
order_id BIGINT,
user_id BIGINT,
amount DECIMAL(10,2)
) PARTITIONED BY (user_id); -- 百万用户 = 百万分区!
⚠️ 注意:单个任务最多写入 2048 个分区,超出会报错
The count of dynamic partitions exceeds the maximum number 2048
The count of dynamic partitions exceeds the maximum number 2048
。建议单分区数据量在百 MB 到 GB 级别。
2. 分桶设计
原则:按 JOIN 或 GROUP BY 常用字段分桶,优化 Shuffle。
-- 适合频繁按 user_id JOIN 的场景
CREATE TABLE orders (
order_id BIGINT,
user_id BIGINT,
amount DECIMAL(10,2)
) CLUSTERED BY (user_id) INTO 16 BUCKETS;
3. 主键设计
原则:CDC 同步场景定义主键,系统自动按主键去重。
-- CDC 同步场景:定义主键确保幂等写入
CREATE TABLE users (
user_id BIGINT PRIMARY KEY,
name STRING,
email STRING,
updated_at TIMESTAMP
);
⚠️ 注意:主键约束在 Lakehouse 中主要用于 CDC 去重和 SQL 写入检查。如果只需要实时写入去重,使用
PRIMARY KEY DISABLE NOVALIDATE RELY
PRIMARY KEY DISABLE NOVALIDATE RELY
。
常见问题
常见问题 1:分区字段基数过高
问题:按高基数字段(如 user_id)分区,导致分区爆炸。
症状:写入时报错
The count of dynamic partitions exceeds the maximum number 2048
The count of dynamic partitions exceeds the maximum number 2048
。
解决:
- 分区字段的不同值数量应控制在合理范围
- 优先选择时间字段,使用转换分区(days/months/years)
- 避免按用户 ID、订单 ID 等高分散字段分区
常见问题 2:Time Travel 存储成本
问题:开启长时间 Time Travel 但表频繁更新,历史版本占用大量存储。
解决:
- 不重要的表保持默认 1 天保留期
- 临时表设置
PROPERTIES ('data_retention_days'='0')
PROPERTIES ('data_retention_days'='0')
关闭 Time Travel
- 定期检查 Time Travel 占用:
DESC TABLE <table> EXTENDED
DESC TABLE <table> EXTENDED
常见问题 3:主键行为不符合预期
问题:使用默认主键行为时,SQL 写入也会进行主键检查,导致批量插入失败。
解决:
成本影响
存储成本
- 数据按 Parquet 格式压缩存储,压缩比通常为 3-10 倍
- Time Travel 会保留历史版本,增加存储(保留天数 × 日均变更量)
计算成本
- 写入数据消耗 VCluster CRU
- 查询成本取决于扫描的数据量(分区/索引可减少扫描)
生命周期管理
创建表 → 写入数据 → 查询分析 → 优化 → 数据归档 → 删除表
↓ ↓ ↓ ↓ ↓ ↓
元数据 Time Travel 结果缓存 OPTIMIZE 生命周期管理 UNDROP 可恢复
保留历史 加速查询 合并文件 自动清理 (保留期内)
数据归档与清理
-- 设置数据生命周期(自动清理过期数据)
ALTER TABLE orders SET PROPERTIES ('data_lifecycle'='365');
-- 查看表的历史版本
DESC HISTORY orders;
-- 恢复误删的表(在 Time Travel 保留期内)
UNDROP TABLE orders;
相关文档