湖模式列存表(Lake Mode Column Store Table)是 Lindorm 基于开源 Apache Iceberg 标准构建的分析型数据存储形态。数据以列式文件格式持久化于 Lindorm 实例底层文件系统,同一实例内的 OLAP 资源组与 ETL 资源组共享同一份湖模式数据,天然支持多引擎协同读写。
湖模式列存表兼容 Iceberg 开放协议,可与 Apache Spark、Apache Flink 等计算引擎互联互通,适合构建多引擎共享的湖仓一体架构。与仓模式列存表相比,湖模式列存表更侧重数据共享与生态兼容;仓模式列存表则具备更完整的 SQL 能力和更高的即席查询性能。
连接 OLAP 资源组后,通过以下语句切换至湖模式列存表数据源:
SET CATALOG lindorm_columnar;湖模式列存表
适用场景
原始日志、行为事件、埋点数据等不需要更新的明细流水分析
多引擎共享的离线数仓事实表,数据由 Flink/Spark 批量写入,OLAP 资源组负责分析查询
需要与 Apache Spark、Apache Flink 等外部计算引擎互联互通的湖仓一体场景
数据模型
湖模式列存表基于 Apache Iceberg 构建,通过 Iceberg 的快照机制(Snapshot)管理数据版本,读写相互隔离。数据按分区组织存储,查询时通过分区裁剪跳过无关分区,同一实例内的多个引擎(OLAP 资源组、ETL 资源组、Flink、Spark)直接共享同一份底层数据,无需数据搬迁或同步。
使用方法
-- 示例:用户访问日志表(按年月分区)
-- 分区列 year、month 置于列定义末尾
CREATE TABLE access_log (
user_id BIGINT COMMENT "用户ID",
path VARCHAR(512) COMMENT "请求路径",
status_code INT COMMENT "HTTP状态码",
latency_ms INT COMMENT "响应时延(毫秒)",
region VARCHAR(32) COMMENT "地区",
year STRING NOT NULL COMMENT "年",
month STRING NOT NULL COMMENT "月"
)
ENGINE = iceberg
PARTITION BY (year, month);
-- 插入单行
INSERT INTO access_log VALUES (1001, '/api/v1/query', 200, 32, 'east', '2024', '03');
-- 将查询结果写入表(可跨 Catalog)
INSERT INTO access_log (user_id, path, status_code, latency_ms, region, year, month)
SELECT user_id, path, status_code, latency_ms, region, year, month
FROM default_catalog.ods.raw_access_log
WHERE year = '2024' AND month = '03';
-- 查询(带分区裁剪)
SELECT * FROM access_log WHERE year = '2024' AND month = '03' LIMIT 100;
-- 聚合分析
SELECT month, COUNT(*) AS pv, COUNT(DISTINCT user_id) AS uv
FROM access_log
WHERE year = '2024'
GROUP BY month
ORDER BY month;
-- 跨 Catalog 联表查询(湖模式列存与仓模式列存表关联)
SELECT a.user_id, a.path, u.user_level
FROM lindorm_columnar.db01.access_log a
JOIN default_catalog.dw.user_profile u ON a.user_id = u.user_id
WHERE a.year = '2024' AND a.month = '03';注意事项
分区列须置于列定义末尾:分区列必须排在所有非分区列之后,否则建表报错。
写入不去重:相同内容的数据多次写入会生成多条独立记录,不会自动去重。
不支持
CREATE TABLE AS SELECT(CTAS):须先建表再INSERT INTO ... SELECT。TIMESTAMP类型不支持:时间字段请使用DATETIME或DATE。多引擎数据实时可见:外部引擎写入的数据对 OLAP 资源组实时可见,无需额外刷新。
数据分布
湖模式列存表通过 PARTITION BY 子句控制数据的物理分布。合理的分区设计可实现查询时的有效分区裁剪,大幅降低扫描范围。
Identity 分区
按列的原始值直接分区,适合低基数的离散维度列(如地区、年、月、状态等)。查询时 WHERE 条件包含分区列的等值过滤,引擎自动跳过不相关分区,显著减少 I/O。
-- 按年月分区
PARTITION BY (year, month)
-- 按地区分区
PARTITION BY (region)注意 分区列的取值不应过于离散,否则会产生海量小分区,增加元数据压力。
数据类型
类型 | 说明 |
| 布尔值 |
| 32 位整数 |
| 64 位整数 |
| 单精度浮点 |
| 双精度浮点 |
| 精确小数,推荐金融/交易场景 |
| 日期,格式 |
| 日期时间 |
| 变长字符串 |
| 二进制数据 |
| 同类型元素数组 |
| 键值对映射 |
计算特性
SQL 分析算子
湖模式列存表支持标准 SQL 分析语法,全面覆盖 OLAP 场景下的分析计算需求。
投影与过滤
SELECT 支持对指定列进行投影,列名可设置别名(AS)、参与算术表达式或函数计算。WHERE 子句支持多条件组合谓词:
谓词类型 | 示例 |
等值过滤 |
|
范围过滤 |
|
集合过滤 |
|
模糊匹配 |
|
NULL 判断 |
|
正则过滤 |
|
谓词在执行时尽可能下推至存储层,减少数据读取量。
连接查询(JOIN)
支持以下连接类型:
JOIN 类型 | 说明 |
| 返回两表的匹配行 |
| 保留左表所有行,右表无匹配则填 NULL |
| 保留右表所有行,左表无匹配则填 NULL |
| 保留两表所有行 |
| 笛卡尔积 |
| 仅返回左表中在右表有匹配的行,不返回右表列 |
| 仅返回左表中在右表无匹配的行 |
支持跨 Catalog 联表查询,将湖模式列存数据与仓模式列存表或宽表引擎数据关联分析:
-- 湖模式列存与仓模式列存表关联
SELECT a.user_id, a.path, u.user_level
FROM lindorm_columnar.db01.access_log a
INNER JOIN default_catalog.dw.user_profile u ON a.user_id = u.user_id
WHERE a.year = '2024' AND a.month = '03'
AND u.user_level IN ('gold', 'platinum');集合运算
运算符 | 说明 |
| 合并多个查询结果,保留重复行,性能更优 |
| 合并多个查询结果并去重 |
| 返回两个结果集的交集 |
| 返回在第一个结果集中但不在第二个中的行 |
分组聚合(GROUP BY)
支持标准聚合函数(COUNT、SUM、AVG、MIN、MAX、COUNT DISTINCT)以及通过 HAVING 对聚合结果二次过滤。
支持以下扩展聚合语法,一次扫描完成多维度汇总:
-- GROUPING SETS:显式指定多组维度组合
SELECT region, month, COUNT(*) AS pv
FROM access_log
GROUP BY GROUPING SETS ((region, month), (region), ());
-- ROLLUP:层级汇总(从细粒度到总计)
SELECT region, month, COUNT(*) AS pv
FROM access_log
GROUP BY ROLLUP (region, month);
-- CUBE:对所有维度的全量组合汇总
SELECT region, month, COUNT(*) AS pv
FROM access_log
GROUP BY CUBE (region, month);排序与分页
支持 ORDER BY 多列排序(ASC / DESC),配合 LIMIT 和 OFFSET 实现分页查询。
SELECT user_id, path, latency_ms
FROM access_log
WHERE year = '2024' AND month = '03'
ORDER BY latency_ms DESC, user_id ASC
LIMIT 20 OFFSET 40;
子查询
支持以下子查询形式,优化器自动将可转换的子查询改写为等价 JOIN 执行:
子查询类型 | 示例 |
标量子查询 |
|
|
|
|
|
相关子查询 | 子查询引用外层查询的列,逐行计算 |
窗口函数(Window Function)
窗口函数在保留原始行的同时对数据分组计算,是 OLAP 分析的核心能力。通过 OVER (PARTITION BY ... ORDER BY ... ROWS/RANGE ...) 语法定义计算窗口。
函数类别 | 常用函数 |
排名函数 |
|
聚合分析 |
|
偏移访问 |
|
首尾取值 |
|
-- 计算每个用户的累计访问次数与当月访问排名
SELECT
user_id,
path,
year,
month,
COUNT(*) OVER (
PARTITION BY user_id
ORDER BY month
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
) AS cumulative_pv,
RANK() OVER (
PARTITION BY month
ORDER BY COUNT(*) DESC
) AS monthly_rank
FROM access_log;
-- 计算相邻两次请求的响应时延差
SELECT
user_id,
path,
latency_ms,
latency_ms - LAG(latency_ms, 1) OVER (PARTITION BY user_id ORDER BY year, month) AS latency_diff
FROM access_log;
CASE WHEN 条件表达式
支持搜索式和简单式两种语法,可在 SELECT、WHERE、ORDER BY、聚合函数等任意位置使用。
SELECT
user_id,
path,
latency_ms,
CASE
WHEN latency_ms < 100 THEN '快速'
WHEN latency_ms < 500 THEN '正常'
WHEN latency_ms < 2000 THEN '较慢'
ELSE '超时'
END AS latency_tier,
SUM(CASE WHEN status_code = 200 THEN 1 ELSE 0 END) AS success_cnt
FROM access_log
GROUP BY user_id, path, latency_ms;元数据访问
湖模式列存表基于 Iceberg 构建,每张表维护有完整的元数据,包括快照历史、分区状态、数据文件清单等。通过在表名后追加 $<元数据表名> 的方式可直接查询这些元数据,无需任何额外配置。
元数据表 | 说明 |
| 当前各分区的行数、文件数、数据量等统计信息 |
| 表的所有快照列表,含快照ID、提交时间、操作类型 |
| 表的元数据变更历史 |
| 与各快照关联的 Manifest 文件概览 |
| 当前快照中所有数据文件的路径、格式、大小等详情 |
| 表的引用信息,包括分支(Branch)和标签(Tag) |
-- 查看表的分区统计
SELECT * FROM lindorm_columnar.db01.access_log$partitions;
-- 查看快照列表(可用于了解数据写入历史)
SELECT * FROM lindorm_columnar.db01.access_log$snapshots
ORDER BY committed_at DESC LIMIT 10;
-- 查看元数据变更历史
SELECT * FROM lindorm_columnar.db01.access_log$history;
-- 查看当前快照中的数据文件详情
SELECT * FROM lindorm_columnar.db01.access_log$files;