普通表

普通表(Table)是 Lakehouse 中存储数据的基本单元。与 MySQL 等行式数据库不同,Lakehouse 的表采用 Parquet 列式存储,数据按列组织并压缩。查询时只需读取相关的列,大幅减少 I/O。与 Hive 的静态分区不同,Lakehouse 采用类似 Iceberg 的隐藏分区机制,分区策略可以在不影响数据的情况下修改。

核心特性

列式存储:默认使用 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 | +----------+---------+--------+---------------------+

表设计最佳实践

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); -- 百万用户 = 百万分区!

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

常见问题

常见问题 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 写入也会进行主键检查,导致批量插入失败。

解决

  • 如果只需要实时写入去重:
    PRIMARY KEY DISABLE NOVALIDATE RELY
    PRIMARY KEY DISABLE NOVALIDATE RELY
  • 如果需要严格唯一性:保持默认
    ENABLE VALIDATE RELY
    ENABLE VALIDATE RELY

成本影响

存储成本

  • 数据按 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;

相关文档

联系我们
预约咨询
微信咨询
电话咨询