ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

StarRocks map_keys 函数完全指南:从 SQL 语义到列式存储零拷贝实现的深度解析

StarRocks map_keys 函数完全指南:从 SQL 语义到列式存储零拷贝实现的深度解析 StarRocks map_keys 函数完全指南从 SQL 语义到列式存储零拷贝实现的深度解析【免费下载链接】starrocksThe worlds fastest open query engine for sub-second analytics both on and off the data lakehouse. With the flexibility to support nearly any scenario, StarRocks provides best-in-class performance for multi-dimensional analytics, real-time analytics, and ad-hoc queries. A Linux Foundation project.项目地址: https://gitcode.com/GitHub_Trending/st/starrocks本篇指南聚焦 StarRocks 的map_keys()函数——它从指定的 MAP 类型值中返回一个包含全部键的数组。文章将完整覆盖该函数的语法、参数、返回值语义与 NULL 处理规则结合当前仓库中 BE 向量化执行引擎、函数注册表和 FE 优化器的真实源码剖析map_keys()在列存结构上如何实现近乎零拷贝的键提取并给出原生表与 Hive 数据湖两套可直接复现的查询示例。读完后你将掌握该函数的使用边界、性能特征及与distinct_map_keys()等相邻函数的协作方式。函数概述与基本语法map_keys()是 StarRocks 提供的 MAP 类型函数族成员其作用是返回指定 MAP 值中所有键组成的数组。该函数自v2.5版本起受支持而自v3.1起StarRocks 原生表支持在建表时直接定义 MAP 列使得map_keys()不仅可用于数据湖场景也可用于 StarRocks 内部表。语法map_keys(any_map)参数any_map需要提取键的 MAP 值。返回值返回值格式为arraykeyType数组的元素类型与 MAP 的键类型严格一致。NULL 语义遵循以下两条规则输入为 NULL 时返回 NULLMAP 内部的键或值为 NULL 时NULL 被视为普通值参与处理即 NULL 键会照常出现在结果数组中而不会导致整行结果为 NULL。函数注册与 BE 向量化实现函数注册表中的定义在 StarRocks 中内置函数的函数名 → 返回类型 → 参数签名 → BE 实现入口映射由生成脚本集中声明。map_keys的注册条目位于 functions.py[170001, map_keys, True, False, ANY_ARRAY, [ANY_MAP], MapFunctions::map_keys],这一行声明了关键事实函数 ID 为 170001返回类型是ANY_ARRAY数组元素类型随输入 MAP 的键类型推导参数类型是ANY_MAPBE 侧的实现入口为MapFunctions::map_keys。同一注册表中还列有map_size170000、map_values170002、distinct_map_keys170006、map_concat170007等一族 MAP 函数map_keys与它们共享同一套 MapColumn 列存结构。BE 核心实现基于 MapColumn 的零拷贝包装map_keys()的 BE 向量化实现位于 map_functions.cpp核心逻辑只有十余行StatusOrColumnPtr MapFunctions::map_keys(FunctionContext* context, const Columns columns) { DCHECK_EQ(1, columns.size()); RETURN_IF_COLUMNS_ONLY_NULL(columns); auto arg0 ColumnHelper::unpack_and_duplicate_const_column(columns[0]-size(), columns[0]); const auto* col_map down_castconst MapColumn*(ColumnHelper::get_data_column(arg0.get())); const auto map_keys col_map-keys_column(); auto map_keys_array ArrayColumn::create(std::move(*map_keys).mutate(), UInt32Column::static_pointer_cast(col_map-offsets_column()-clone())); if (arg0-has_null()) { return NullableColumn::create( std::move(map_keys_array), NullColumn::static_pointer_cast(down_castconst NullableColumn*(arg0.get())-null_column()-clone())); } else { return map_keys_array; } }从源码结构看这一实现揭示了几个重要的底层事实MAP 在列存中的物理布局。StarRocks 的MapColumn由三部分构成扁平化的keys_column()所有行键的拼接、扁平化的values_column()所有行值的拼接、以及offsets_column()每行键值对在扁平数组中的起止偏移。这与文档中返回值元素类型与键类型一致的语义完全对应——键本身就是一块连续的列数据。map_keys()本质上是一次重新包装而非逐行抽取。实现直接取出MapColumn的keys_column()并用 MAP 自身的offsets_column()克隆出偏移构造出一个ArrayColumn。没有逐行循环、没有哈希查找、没有内存分配复制键数据mutate()走的是可写化路径无 NULL 列时可共享底层数据因此对大结果集的键提取开销极低。作为对照同文件中map_size()L139-L144则确实逐行计算offsets[i1] - offsets[i]可见map_keys()的设计刻意避开了逐行开销。NULL 处理与文档语义一一对应。RETURN_IF_COLUMNS_ONLY_NULL与末尾的arg0-has_null()分支保证了输入 NULL 返回 NULL而 MAP 内部的 NULL 键只是keys_column()中携带 null 槽位的普通元素会原样进入结果数组——这正是文档所述NULL 被当作普通值处理的源码依据。map_values()是完全对称的实现L174-L192两者仅区别在取keys_column()还是values_column()这也是map_keys/map_values成对出现、行为一致的原因。FE 侧常量折叠与子字段裁剪FE 优化器对map_keys有专门的感知与改写规则常量折叠FoldConstantsRule.java 将map_keys与distinct_map_keys等列入可对常量实参直接求值的函数集合常量 MAP 上的map_keys()会在计划期被折叠避免运行期开销。复杂类型子字段裁剪PruneComplexTypeUtil.java 的注释明确以SELECT map_values(col).b.d, map_keys(col) FROM TABLE为例说明优化器会为map_keys(col)生成独立的 MAP_KEYS 访问路径仅物化查询真正需要的子字段从而在读取复杂类型时减少无关列的物化。子字段访问路径归一化PruneSubfieldRule.java 与 SubfieldAccessPathNormalizer.java 将map_keys识别为可参与路径归一化的复杂类型函数与下标访问map[key]统一纳入访问路径框架。函数名常量在 FE 中统一登记于 FunctionSet.javaMAP_KEYS map_keys、DISTINCT_MAP_KEYS distinct_map_keys。实战示例一查询 StarRocks 原生表自 v3.1 起可在建表时定义 MAP 列。以下示例使用包含col_map列的test_map表CREATE TABLE test_map( col_int INT, col_map MAPVARCHAR(50),INT ) DUPLICATE KEY(col_int); INSERT INTO test_map VALUES (1,map{a:1,b:2}), (2,map{c:3}), (3,map{d:4,e:5}); SELECT * FROM test_map ORDER BY col_int; ------------------------ | col_int | col_map | ------------------------ | 1 | {a:1,b:2} | | 2 | {c:3} | | 3 | {d:4,e:5} | ------------------------ 3 rows in set (0.05 sec)对col_map列的每行提取全部键select map_keys(col_map) from test_map order by col_int; ------------------- | map_keys(col_map) | ------------------- | [a,b] | | [c] | | [d,e] | ------------------- 3 rows in set (0.05 sec)注意返回类型推导col_map的键类型为VARCHAR(50)故结果数组为arrayvarchar(50)元素类型严格继承自键类型。实战示例二查询数据湖中的 MAP 数据对数据湖如 Hive外表map_keys()同样适用。示例中使用 Hive 表hive_map其数据如下SELECT * FROM hive_map ORDER BY col_int; ------------------------ | col_int | col_map | ------------------------ | 1 | {a:1,b:2} | | 2 | {c:3} | | 3 | {d:4,e:5} | ------------------------在集群中创建 Hive Catalog 之后即可借助该 Catalog 与map_keys()提取col_map列每行的全部键select map_keys(col_map) from hive_map order by col_int; ------------------- | map_keys(col_map) | ------------------- | [a,b] | | [c] | | [d,e] | ------------------- 3 rows in set (0.05 sec)组合使用与相关函数map_keys()很少单独出现实际分析中常与同族函数配合使用map_values()返回所有值的数组与map_keys()一一对应。由于 BE 中两者共享 MAP 的扁平列结构键列与值列按相同 offsets 对齐map_keys(col)与map_values(col)的结果按下标天然配对可配合数组函数重建键值对应关系。distinct_map_keys()当需要去重键集合时使用注册条目见 functions.pyBE 实现见 map_functions.cpp其内部正是复用键提取逻辑后再做 distinct。map_size()/cardinality()仅需键的个数而无需要素本身时map_size(col)直接返回INT代价更低。下标访问与transform_keys()/transform_values()对提取出的键数组可继续使用 ARRAY 函数族如array_length、in判断做存在性检验例如some_key in map_keys(col)可作为 MAP 成员判断的替代写法。使用注意版本前提map_keys()自 v2.5 起支持对原生表使用 MAP 列还需 v3.1 及以上版本。NULL 语义整列输入 NULL 时该行返回 NULLMAP 内部的 NULL 键会作为普通元素出现在结果数组中下游做count、去重等聚合时需自行决定如何对待这些 NULL 元素。顺序不保证文档未承诺结果数组的键顺序实际输出顺序取决于底层 MAP 的存储布局不应依赖键在数组中的具体位置做业务判断。性能特征从 BE 实现看map_keys()不产生逐行哈希或查找开销主要成本在把 MAP 列包装为数组列适合在扫描大量行时对键集合做筛选、聚合或 JOIN 前的预处理。【免费下载链接】starrocksThe worlds fastest open query engine for sub-second analytics both on and off the data lakehouse. With the flexibility to support nearly any scenario, StarRocks provides best-in-class performance for multi-dimensional analytics, real-time analytics, and ad-hoc queries. A Linux Foundation project.项目地址: https://gitcode.com/GitHub_Trending/st/starrocks创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表