StarRocks dictionary_get 函数详解:从字典缓存中高效查询键值映射
2026/9/18 8:18:07 网站建设 项目流程

StarRocks dictionary_get 函数详解:从字典缓存中高效查询键值映射

【免费下载链接】starrocksThe world's 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

dictionary_get是 StarRocks 中与全局字典(Dictionary)配套的查询函数,用于在 SQL 中直接读取字典对象(dictionary object)内键(key)所映射的值(value)。它主要服务于以dict_mapping为核心的全局字典加速方案,帮助你在查询阶段按字典主键快速取回映射结果,再配合[N].<column_name>语法灵活取用某一列的值。读完本文,你将掌握dictionary_get的完整语法、参数语义、STRUCT 返回值的使用技巧,并能从 BE 端源码理解字典缓存探测(cache probe)的底层执行原理。

背景:字典对象与字典缓存

在讲解dictionary_get之前,有必要先明确"字典对象"这一概念。StarRocks 的全局字典方案(详见 dict_mapping 文档 与 使用 AUTO_INCREMENT 和全局字典加速 COUNT(DISTINCT) 与 JOIN)通常包含两个环节:

  1. 加载阶段:通过dict_mapping函数,在数据导入目标表时,自动从字典表中取得指定键对应的映射值(例如把 STRING 类型的订单号映射为 BIGINT 类型的自增 ID);
  2. 查询阶段dictionary_get负责从字典缓存(dictionary cache)中按 key 查询 value,将映射关系再次取回用于查询计算。

从源码结构看,dictionary_get属于exprs_ext/dict下的扩展表达式,与dictmapping_expr一同被注册在 expr_factory_dict_exprs_extension.cpp 中,二者的执行都依赖 BE 节点的ComputeEnv所提供的DictionaryCacheManager(参见 compute_env.cpp)。也就是说,dictionary_get查询的不是任意一张普通表,而是 BE 内存中的字典缓存对象。

语法

dictionary_get('dictionary_object_name', key_expression_list, [NULL_IF_NOT_EXIST]) key_expression_list ::= key_expression [, ...] key_expression ::= column_name | const_value

参数说明

参数是否必选说明
dictionary_object_name必选字典对象的名称(字符串常量)。
key_expression_list必选所有 key 列的表达式列表,可以是列名(column name)列表,也可以是常量值(const value)列表;多个表达式之间用逗号分隔。
NULL_IF_NOT_EXIST可选当 key 在字典缓存中不存在时的行为,取值为布尔型:
-true:key 不存在时返回NULL
-false(默认值):key 不存在时抛出异常。

需要特别留意的是,当以列名作为 key 表达式时,该列的数据会被逐行作为 key 去探测字典缓存;当以常量作为 key 表达式时,则是单次常量查询。key_expression_list中的表达式数量与顺序必须与字典对象的 key 列一一对应(复合主键场景下尤为关键)。

返回值

dictionary_get返回STRUCT 类型,结构体中包含该 key 映射到的所有 value 列。因此可以通过两种方式取用其中某一列的值:

  • dictionary_get(...)[N]:按下标取列,N表示 value 列的位置,从 1 开始计数
  • dictionary_get(...).<column_name>:按列名取列。

使用示例

以下示例均使用 dict_mapping 文档示例中的数据集:字典表dict(key 列order_uuid,value 列order_id_int,映射关系为a1→1a2→2a3→3)以及多 value 列的维度字典dimension_obj(key 为1,value 列为ProductNameCategorySubCategoryBrandColorSize)。

示例 1:以列为 key,批量查询整行映射

查询字典对象dict_obj中,order_uuid列每一行所映射的 value 列值:

MySQL > SELECT dictionary_get('dict_obj', order_uuid) FROM dict; +--------------------+ | DICTIONARY_GET | +--------------------+ | {"order_id_int":1} | | {"order_id_int":3} | | {"order_id_int":2} | +--------------------+ 3 rows in set (0.02 sec)

可见函数按行执行,每一行返回一个 STRUCT,其中包含 value 列order_id_int及其映射值。

示例 2:以常量 key 查询

查询字典对象dict_obj中 key 为a1映射的 value 列值:

MySQL > SELECT dictionary_get("dict_obj", "a1"); +--------------------+ | DICTIONARY_GET | +--------------------+ | {"order_id_int":1} | +--------------------+ 1 row in set (0.01 sec)

示例 3:查询多 value 列字典的完整映射

查询字典对象dimension_obj中 key 为1映射的全部 value 列:

MySQL > SELECT dictionary_get("dimension_obj", 1); +-----------------------------------------------------------------------------------------------------------------+ | DICTIONARY_GET | +-----------------------------------------------------------------------------------------------------------------+ | {"ProductName":"T-Shirt","Category":"Apparel","SubCategory":"Shirts","Brand":"BrandA","Color":"Red","Size":"M"} | +-----------------------------------------------------------------------------------------------------------------+ 1 row in set (0.01 sec)

返回值是一个包含 6 个字段的 STRUCT。

示例 4:按下标取第一个 value 列

查询字典对象dimension_obj中 key 为1映射的第 1 个 value 列:

MySQL > SELECT dictionary_get("dimension_obj", 1)[1]; +-------------------+ | DICTIONARY_GET[1] | +-------------------+ | T-Shirt | +-------------------+ 1 row in set (0.01 sec)

示例 5:按下标取第二个 value 列

查询字典对象dimension_obj中 key 为1映射的第 2 个 value 列:

MySQL > SELECT dictionary_get("dimension_obj", 1)[2]; +-------------------+ | DICTIONARY_GET[2] | +-------------------+ | Apparel | +-------------------+ 1 row in set (0.01 sec)

示例 6:按列名取 value 列

查询字典对象dimension_obj中 key 为1映射的ProductName列:

MySQL > SELECT dictionary_get("dimension_obj", 1).ProductName; +----------------------------+ | DICTIONARY_GET.ProductName | +----------------------------+ | T-Shirt | +----------------------------+ 1 row in set (0.01 sec)

从源码看 dictionary_get 的执行原理

FE 端:生成 DICTIONARY_GET_EXPR 表达式节点

在前端(FE)侧,dictionary_get调用会被翻译为执行计划中的字典查询表达式。在 ExecDictionaryGet.java 中可以看到,该表达式携带四类关键信息:

  • dict_id:字典对象在 FE 侧分配的全局唯一 ID;
  • txn_id:字典缓存的事务版本号(DictionaryCacheTxnId),用于定位指定版本的缓存;
  • key_size:key 列的数量,供 BE 拆分 key 与 value 列使用;
  • null_if_not_exist:即参数NULL_IF_NOT_EXIST

序列化时它被写成TExprNodeType.DICTIONARY_GET_EXPRtoThrift方法),随查询计划下发给 BE。

BE 端:prepare 阶段加载字典缓存

在 dictionary_get_expr.cpp 的DictionaryGetExpr::prepare中,表达式初始化时会:

  1. RuntimeStateexec_env()->compute_env()获取DictionaryCacheManager,若缺失则报错open dictionary expression failed, missing compute dictionary cache manager
  2. 通过get_dictionary_schema_by_id(dict_id)取得字典的完整列 schema(key 列 + value 列);
  3. 通过get_dictionary_by_version(dict_id, txn_id)取出指定事务版本的字典缓存DictionaryCachePtr
  4. 依据key_size将 schema 拆分为_key_chunk_value_chunk两个模板 chunk,并预先构建一个可空的 STRUCT 列_nullable_struct_column作为输出模板。

这里体现了字典缓存的版本一致性设计:DictionaryCacheTxnId是由 FE 单调递增分配的事务号,用于标识每一次字典缓存刷新任务,同一字典在同一事务内所有 BE 上的读取保持一致(详见 dictionary_cache_manager.h 中对DictionaryCacheManager的注释)。

BE 端:evaluate 阶段探测缓存并组装 STRUCT

DictionaryGetExpr::evaluate_checked是核心执行路径,其流程如下:

  1. 逐一计算所有子表达式(children),得到 key 列数据;
  2. 参数校验:若 key 列存在NULL值,直接返回错误invalid parameter for dictionary_get function: get NULL paramenter,即不允许用 NULL 作为 key;
  3. 将常量列展开(unpack_and_duplicate_const_column)为与输入行数一致的大小,并剥离可空包装;
  4. 调用DictionaryCacheManager::probe_given_dictionary_cache对字典缓存进行批量探测,得到 value chunk;
  5. 将 value chunk 的每一列追加到预构建的 STRUCT 列的各字段列中,同时根据null_column标记整体 STRUCT 是否为空,最终返回NullableColumn<StructColumn>

probe_given_dictionary_cache的实现同样位于 dictionary_cache_manager.h:

  • 先通过DictionaryCacheUtil::encode_columns对 key schema 与 value schema 做主键编码PK_ENCODE类型,使用PrimaryKeyEncoder的 V1 编码),把多列 key 编码为单列紧凑表示;
  • 再调用dictionary->lookup(...)在哈希表中查找,若null_column == nullptr(即NULL_IF_NOT_EXIST=false)且 key 不存在,则返回Status::NotFound("key not found in dictionary cache")异常;若传入了null_column(即NULL_IF_NOT_EXIST=true),则对该行标记为 NULL 并继续;
  • 最后通过DictionaryCacheUtil::decode_columns解码回原始 value 列。

底层存储与查找优化

字典缓存的底层容器是phmap::parallel_flat_hash_map(参见 dictionary_cache_manager.h 中DictionaryCacheImpl的定义),并针对查询路径做了两类关键优化:

  • 哈希策略:数值型 key 使用带种子(PhmapSeed1)的标准哈希,字符串(VARCHAR/Slice)key 使用XXH3_64bits计算 64 位哈希;
  • 预取(prefetch)加速lookup中以PREFETCHN = 8为粒度做交错式预取——先预取 8 个 key、再预取 8 个哈希桶、最后执行实际查找,从而隐藏访存延迟;对于 Slice 类型的 value,查找时先只保存指针,待整批查找完成后再统一拷贝追加,避免大字符串写入污染 CPU 缓存(代码注释中明确说明了这一设计意图)。

此外,value 为字符串类型时,每条记录的内存布局会额外携带一个 1 字节的fast decode flag,用于标记该值是否可以采用快速解码路径(PRIMARY_KEY_DECODE_FAST/PRIMARY_KEY_DECODE_NORMAL/PRIMARY_KEY_DECODE_SKIP)。DictionaryCacheUtil::precheck_value_encode会预先扫描字符串值中是否包含\0字节,若包含则回退为普通解码方式,保证解码正确性。

与 dict_mapping 的搭配:完整闭环

dictionary_getdict_mapping是一对配套函数,前者负责写入侧(加载时取映射),后者负责读取侧(查询时取映射):

  • dict_mapping面向字典表(Primary Key 表),在数据加载阶段把 key 映射为 ID 写入目标表,适合"导入即完成映射"的场景;
  • dictionary_get面向字典缓存对象,在查询阶段直接按 key 取回完整 value(STRUCT),适合需要实时引用映射关系的场景,例如在查询中把维度字典的多个属性列拼装到结果中。

两者的执行都依赖同一套DictionaryCacheManager缓存体系(BE 侧实现在 dictionary_cache/dictionary_cache_manager.cpp),共享相同的键值编码、版本管理与预取优化。

在 FE 的单元测试 DictQueryFunctionTest.java 中,可以看到对字典函数参数合法性的系统校验:字典表名必须为db.tbltbl格式、字典表必须是 Primary Key 表、null_if_not_exist参数必须是布尔常量等,这些约束同样适用于dictionary_get的参数解析与错误提示。

使用注意事项

  • key 不允许为 NULL:从源码看,任何 key 列包含 NULL 时dictionary_get都会直接报错,请先通过过滤或 COALESCE 处理数据;
  • NULL_IF_NOT_EXIST 默认关闭:默认情况下(false),key 不在字典缓存中会抛出异常而非返回 NULL,这可以避免静默产生错误映射,但也意味着查询前应确保 key 已存在于字典中;需要容错时可显式传入true
  • STRUCT 取列两种方式等价[N]按下标(从 1 起)、.<column_name>按列名,选择哪种取决于你更关心列的顺序还是列名可读性;
  • 复合 key 场景:当字典对象包含多个 key 列时,key_expression_list必须与 key 列一一对应(参考dict_mapping对复合主键的处理:所有主键都必须指定,否则报参数数量错误);
  • 缓存版本一致性dictionary_get读取的是 BE 内存中指定事务版本的字典缓存(txn_id由 FE 分配),因此其可见性与缓存刷新事务强相关,属于内存态查询,不直接读取底层字典表。

【免费下载链接】starrocksThe world's 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),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询