ClickHouse v21.9 版本特性全解析:窗口函数、访问控制与分布式查询优化的关键里程碑
【免费下载链接】ClickHouseClickHouse® is a real-time analytics database management system项目地址: https://gitcode.com/GitHub_Trending/cli/ClickHouse
本文基于 ClickHouse 官方 changelog 文档 v21.9.1.8000-prestable 编写。该文档记录了 ClickHouse v21.9 系列首个 prestable(预发布)版本相对 v21.8.1.7409-prestable 的全部变更,涵盖向后不兼容变更、新特性、性能改进、功能改进、Bug 修复、构建与测试改进等六大类别。读者通过本文可以完整掌握 v21.9 引入的关键能力:
nth_value等窗口函数的正式可用、RBAC 权限体系的新增函数、Decimal 文本输出的行为变化、分布式查询下推的默认开启,以及大量可观测性与稳定性增强。
版本背景:prestable 版本在发布流程中的定位
在 ClickHouse 的版本发布体系中,形如v21.9.1.8000-prestable的版本属于预发布版本(prestable),用于在正式稳定版发布前征集反馈与修复问题。该类版本通常不会直接部署到生产环境,而是供测试环境、预发环境与社区早期使用者验证新特性。本文档的骨架——"Backward Incompatible Change"(向后不兼容变更)置于最前,也体现了 ClickHouse 社区一贯的发布哲学:先披露行为变化,再介绍新能力,让升级者在动手前对可能破坏现有应用的点有充分预期。
向后不兼容变更:升级前必须评估的四个行为变化
v21.9 共列出四项向后不兼容变更,其中两项涉及查询语义,两项涉及输出与寻址行为。
1. 列别名与表达式同名时的类型解析收紧
在部分复杂查询中,当列别名与表达式名称相同时,此前版本可能发生错误的类型转换(bad cast)。v21.9 修复了这一问题,但代价是:若同一查询中出现名称相同但表达式不同的情况,将直接抛出异常。该修复在个别开启了enable_optimize_predicate_expression的罕见场景下可能引入行为差异。对依赖宽松别名解析的存量查询,建议升级后通过测试集回归验证。
2. clickhouse-local 下本地地址带端口一律视为远程
在clickhouse-local工具中,带端口的本地地址(如127.0.0.1:9000)现在一律被当作远程地址处理,不再进行本地化匹配。这一行为统一了clickhouse-local与标准服务器在地址解析上的语义,避免了本地/远程判断不一致引发的连接路由问题。
3. 参数化聚合函数的-Merge组合子必须显式匹配参数
这是对聚合函数状态(state)类型安全的一次强化。此前,由fooState(42)(x)产生的状态可以被fooMerge(s)或fooMerge(123)(s)错误终结(finalize)。v21.9 起,必须显式指定且完全相同的参数,例如:
-- 正确:参数必须与产生 state 时的参数一致 SELECT fooMerge(42)(state) FROM ...; -- 错误:缺参或参数不一致将抛出异常 -- SELECT fooMerge(state) FROM ...; -- SELECT fooMerge(123)(state) FROM ...;不过该限制不适用于quantile、sequence*等仅在终结阶段使用参数的特殊聚合函数。从源码结构看,这一改动对应 src/AggregateFunctions 目录中聚合函数组合子(combinators)的状态类型推导逻辑,目的是在编译期/运行期拦截参数不匹配导致的隐式类型转换错误。
4. Decimal 文本输出不再保留尾部零
这是对Decimal类型文本表示的一次用户可见改变:scale 为 6 的1.230000现在会输出为1.23。若应用此前依赖尾部零对齐,需要相应调整。该行为可通过设置output_format_decimal_trailing_zeros控制,但toString()与转换为 String 的行为是强制修改、不可关闭的。
从当前仓库源码可以验证该设置的底层实现:
- 设置在 src/Core/FormatFactorySettings.h 中定义,并在 src/Core/SettingsChangesHistory.cpp 中有变更记录;
- 序列化逻辑在 SerializationDecimal.cpp 中通过
settings.decimal_trailing_zeros控制:开启时writeText(value, this->scale, ostr, settings.decimal_trailing_zeros)保留尾部零,关闭时(默认)输出去掉尾部零的紧凑形式。JSON 格式下为了避免输出非法的1.形式,同样受该设置约束。
因此,需要兼容旧输出的场景,可在查询级或会话级显式设置:
SET output_format_decimal_trailing_zeros = 1;新特性:窗口函数、访问控制与数据摄入能力的全面扩展
v21.9 的新特性数量多、覆盖广,可归纳为六大方向。
窗口函数正式开放:nth_value加入标准窗口函数家族
v21.9 实现了标准窗口函数nth_value(expr, N),返回窗口帧内第 N 行的值。这补齐了 ClickHouse 窗口函数集合中"按行号取值"的能力,配合OVER子句的PARTITION BY与ORDER BY,可用于"第几高/第几名"类分析。
该函数在当前仓库 WindowTransform.cpp 中注册,源码中同时保留了完整的签名与使用示例:
nth_value (x, offset)以及一个经典的"第三高薪"查询:
SELECT player, salary, nth_value(player, 3) OVER(ORDER BY salary DESC) AS third_highest_salary FROM salaries;需要强调的是,v21.9 同时移除了allow_experimental_window_functions设置,将窗口函数标记为正式可用(General Use),这是窗口函数从实验特性走向稳定的明确信号。同版本还修复了窗口函数中的空指针解引用风险、lagInFrame对 Nullable 类型的处理以及分区边界搜索问题,并支持在Ctrl+C时取消窗口函数计算(见 WindowTransform.cpp)。
RBAC 访问控制:新增角色/Profile 查询函数与REPLACE GRANT
v21.9 在访问控制层面集中发力:
- 新增
REPLACE GRANT语句,允许在一条语句中同时撤销与授予权限,简化权限变更的原子操作; - 新增函数
currentProfiles()、enabledProfiles()、defaultProfiles(),以及currentRoles()、enabledRoles()、defaultRoles(),分别返回当前会话的 Profile 与角色集合。结合SET ROLE、SET PROFILE可精确审计"当前会话实际生效的权限面"; system.users表新增default_database列;SET PROFILE现在会同步应用目标 Profile 上的约束(constraints);- 修改默认角色只影响新会话,不干扰已建立连接的权限上下文;
- 细化
GRANT WITH REPLACE OPTION的权限要求:执行者须同时对"授予的权限"和"撤销的权限"持有GRANT OPTION。
这些改动与 src/Access 目录的权限模型(用户、角色、行策略、配额、设置 Profile)一一对应,其中大部分实现集中在 RBAC 相关的解析器与访问检查器代码中。
查询语言能力:Heredoc 文档字符串与字典自定义查询
- Heredoc 语法:新增文档字符串(here document)写法,如
SELECT $doc$VALUE$doc$,便于在查询中内嵌包含引号、换行的大段文本,避免繁琐转义; - 字典自定义查询:MySQL、PostgreSQL、ClickHouse、JDBC、Cassandra 五种字典源均支持自定义查询,替代以往只能指定表名的方式,显著提升了字典源的灵活性(对应 src/Dictionaries 中
DictionarySource系列实现); - 常量用于聚合参数:
WITH与SELECT中的常量现在可被用作聚合函数参数; cluster/clusterAllReplicas表函数支持集群宏:集群名称中的{shard}、{replica}等宏会被正确替换(源码支持位于 src/TableFunctions)。
位图与 Map 工具函数
- 新增
bitmapSubsetOffsetLimit(bitmap, offset, cardinality_limit),从位图中按偏移量截取指定基数的子集; - 新增
tupleToNameValuePairs,将命名元组(named tuple)转为键值对数组; mapPopulatesSeries函数扩展支持Map类型。
压缩与存储格式
- 导入/导出新增bzip2压缩方法支持;
INTO OUTFILE新增自动选择压缩算法的能力(根据文件扩展名推断,如.gz、.bz2、.zst等);- 新增
output_format_avro_string_column_pattern设置,可将匹配模式的 String 列以字符串而非默认的 bytes 写入 Avro。
分布式与字典存储
- 实验性地支持通过 ZooKeeper 复制存储用户、角色、行策略、配额与设置 Profile,为多节点一致的访问控制提供基础(experimental,需显式开启);
- 新增ComplexKeyRangeHashed字典类型,支持复杂键 + 区间哈希的组合查询场景;
- 可执行字典(Executable / ExecutablePool)支持通过
clickhouse-local使用 DDL 创建。
性能改进:短查询延迟、系统调用与文件描述符的三管齐下
v21.9 的性能优化聚焦于"短查询体验"与"系统资源开销"两条主线:
- 聚合函数即时编译扩展:
groupBitOr、groupBitAnd、groupBitXor支持编译执行;Enum类型列支持编译;无 key 的聚合也纳入编译范围; - Nullable 整数 SUM 向量化:以原生表示(native representation)向量化求和,减少空值分支开销;
- 短查询延迟:针对"需要读取大量列的小查询"优化启动路径;当
max_execution_time=0(不限时)时减少clock_gettime系统调用次数;后续又进一步削减clock_gettime调用,让快速查询的整体延迟更低; - 文件描述符共享:并发读取同一文件时共享文件描述符(fd)。虽然 Linux 下感知不到明显性能差异,但典型服务器上打开的文件数会显著下降(数量级约 10~100 倍),大幅缓解
EMFILE(打开文件数超限)类问题,也让运维更轻松; - 日期时间比较特化:针对日期时间类型的比较做专门实现以提升性能;
- 索引分析去重:查询分析时不再为索引构建集合(set),并移除投影(projection)分析中的重复索引分析,避免无效的 limit 校验。
功能改进:设置项、分布式查询与可观测性的大规模增强
v21.9 的功能改进条目最多,以下按主题归类呈现核心内容。
分布式查询与集群连接
distributed_push_down_limit默认开启:SELECT * FROM dist ORDER BY key LIMIT 10这类查询会把LIMIT下推到各分片,减少回传数据量;同时该设置现在也被optimize_distributed_group_by_sharding_key优化尊重,避免执行多余的Distinct/LIMIT BY;- 分片连接复用:允许不同集群间复用分片连接,使用
cluster表函数时也避免重复建连; use_hedged_requests默认启用:通过"对冲请求"(同时向多个副本发起请求、取先返回者)缓解大集群上的尾延迟(tail latency);skip_unavailable_shards:修复了多余的(约 2 倍)连接尝试;- Distributed 目录监控:可通过
CREATE TABLE ... Engine=Distributed(cluster, db, table) SETTINGS monitor_batch_inserts=1直接在建表语句中配置目录监控参数; - shard 级常量函数:
hostName()、tcpPort()、version()、buildId()、uptime()等函数在分布式表上下文执行时产生普通列,否则产生常量,消除了跨分片常量传播的歧义。
系统表与可观测性
- 新增
system.warnings表,收集服务器配置相关的警告信息;客户端连接时默认会收到服务器已收集的警告(可通过--no-warnings关闭); system.columns为Log、TinyLog表补充列大小信息;system.replicas新增replica_is_active列,直接映射副本名到活跃状态;system.rocksdb表暴露 RocksDB 统计信息,RocksDB 选项可通过 ClickHouse 配置中的rocksdb/rocksdb_TABLE键读取;system.query_log新增log_formatted_queries设置:额外记录格式化后的查询文本,便于结合normalizeQuery等函数做规范化查询分析(因为这些函数为性能不解析/不格式化查询);- 新增指标
MaxPushedDDLEntryID,表示当前节点推送到 ZooKeeper 的最大 DDL 条目 ID;system.part_log的REMOVE_PART事件补上event_time_microseconds; - 查询失败时也尽量记录
query_kind,并将已使用的函数/表等信息写入query_log; - 异常消息现在带错误 ID(如
BAD_ARGUMENTS),便于日志检索与问题归类。
查询优化与执行
toTimeZone在时区为常量时具备单调性,支持toTimeZone上的分区裁剪;- 对 key 中的非确定性函数(含
now()、today()等常量表达式)进行校验,防止物化视图/表键中出现不确定行为; - 合并连接(merge join)正确处理右表为空集合的情况,并修复列结构问题;
- 标量子查询结果类型推导收紧:类型可为 Nullable 时一律返回 Nullable,空结果返回
NULL,无法转为 Nullable 的类型(如Array、Tuple)在空结果时抛错; - lambda 函数在名称歧义时优先解析为自身参数;
- 新增
function_range_max_elements_in_block设置,用于调节range函数单块生成数据量的安全阈值; - 新增
max_hyperscan_regexp_length与max_hyperscan_regexp_total_length,防止超大正则进入 hyperscan 相关函数(如multiMatchAny); extractAllGroupsHorizontal支持通过可选第三参数限制每行最大匹配数;arrayJoin被禁止用于分区表达式。
磁盘、加密与外部系统
- 加密磁盘支持多把密钥,并在密钥疑似错误时给出明确错误提示;加密算法若非默认的
aes_128_ctr需显式指定; - Postgres 数据库引擎支持 schema;PostgreSQL 表函数修复连接不关闭问题;PostgreSQL 引擎将
timestamp/timestamptz转为DateTime64; FROM INFILE命令新增,从本地文件导入数据;INTO OUTFILE自动选择压缩算法;- HTTP 支持将查询参数放在请求体(body)中传递;Web UI 支持通过服务器 URI 传查询设置,并修正与源站不同的服务器地址记录;
- gRPC 协议二进制数据改用 bytes 而非 string;gRPC 服务器同步问题修复;
- 客户端新增
memory客户端选项;clickhouse-benchmark支持 round-robin 多主机模式,且连接失败(如EMFILE)时不再挂起; system.detached_parts在自定义磁盘且部分磁盘缺少detached目录时不再抛异常。
其他值得注意的改进
- MySQL/PostgreSQL 协议处理器设置客户端查询类型;HTTP 端口未配置时,向 TCP 端口发送 HTTP 请求会返回带错误信息的响应;
- 认证失败时记录客户端 IP;
- Docker 中 watchdog 默认关闭,修复
Ctrl+C处理; - 表创建时即校验采样哈希函数(而不是采样时才校验),并新增 MergeTree 设置以便异常场景跳过校验、保证服务器可启动;
KILL QUERY请求处理逻辑优化。
Bug 修复:稳定性与正确性的六十余项修补
v21.9 的 Bug 修复覆盖数据一致性、类型系统、分布式与副本、外部系统接入等多个层面。
副本一致性关键修复
- 修复丢失副本(lost replica)恢复中的罕见 bug,避免副本数据分叉;另修复一组可能导致副本分叉的 bug;
- 修复
DROP PART中可能导致Unexpected merged part intersects drop range的罕见问题,以及另一处同类错误;修复DROP_RANGE竞态可能引发的 mutation 栈异常; - 修复
ReplicatedMergeTree关停时误删数据导致的元数据不一致;mutation 在非复制 MergeTree 的非法分区上卡住的问题。
查询正确性修复
- 修复多个
JOIN场景:与 totals 连接时的逻辑错误、partial_merge_join的无限非连接块流、"Unknown column name" 错误、合并连接的列结构、Nullable 常量列参与 JOIN; - 修复
LowCardinality在PREWHERE、行级安全过滤、arrayHas等场景的类型转换错误(Expected ColumnLowCardinality/ bad cast 类异常); - 修复带采样的查询报
Cannot find column;别名列重写后的名称解析错误;Distributed表中别名列问题; - 修复
DEFAULT列引用无表达式列时的Missing columns: 'xxx'; - 修复
optimize_distributed_group_by_sharding_key多列分片键与optimize_skip_unused_shards组合时的错误结果; - 修复
distributed_group_by_no_merge=2与下推LIMIT BY/LIMIT OFFSET组合问题;零分片分布式查询与聚合问题; - 修复
union distinct子查询中的列过滤;intersect/except与 limit 组合问题; - 修复多个
untuple表达式时的潜在崩溃;投影物化时部分缺列导致的崩溃; - 修复
multiSearch*空数组时的未初始化内存;聚合函数参数在组合子下丢失的问题(如topKArray状态转换异常); SET ROLE偶发错误、以被删除用户登录的崩溃、非整分钟时区偏移兼容性。
协议与外部系统修复
- 修复 zstd 解压内部缓冲区末尾存在转义序列的问题;Kafka NULL(tombstone)消息导致部分格式崩溃;MySQL 协议在并行格式(CSV/TSV)下的问题;
- RabbitMQ 关停崩溃、连接泄漏问题;库桥接(library-bridge)ID 加载修复;
- gRPC 服务器同步修复;PostgreSQL 表函数连接未关闭问题;
cache/complex_key_cache/ssd_cache等字典配置解析修复(allow_read_expired_keys、max_update_queue_size等选项此前对非 cache 类型字典未生效);system.detached_parts罕见信息错误;分区 ID 校验(含旧语法表的校验回归修复);max_memory_usage*被设为非零后无法重置回 0(unlimited)的问题;- 时间值由组件构造时的下溢修复;
thread_name多余换行符清理。
构建、测试与打包改进
- 支持使用clang-13构建,并进一步改善兼容性;
- 新增 CMake 选项,可按需启用/禁用特定 CPU 指令集构建(配合 SSE2 的 unbundled 构建策略);
- RocksDB 更新至 2021-07-16 master;protobuf 升级至 3.17.3;
clickhouse-test支持带 Jinja2 模板的 SQL 测试(模板化测试能力);- 启用 RBAC TestFlows 测试模块;修复
CLICKHOUSE_CLIENT_SECURE默认配置下的测试问题; - 修复动态库模式下辅助程序链接问题。
其他变更与杂项
MaterializeMySQL更名为MaterializedMySQL(命名规范化,对应 src/Storages 下的物化 MySQL 引擎);- 代码注释修正、依赖版本滚动(poco、arrow、zlib-ng、AMQP-CPP 等)、测试稳定性修复、
part_log默认开启、allow_remote_fs_zero_copy_replication默认置为 true 等大量内部改进记录在 changelog 的 "NO CL ENTRY" 与 "NOT FOR CHANGELOG / INSIGNIFICANT" 章节,此处不再逐一展开。
升级建议与总结
v21.9 是 ClickHouse 在 2021 年 9 月发布周期的重要版本,其核心主题可概括为三句话:
- 窗口函数正式化:以
nth_value落地和实验设置移除为标志,窗口函数从实验走向生产可用; - 访问控制补全:角色/Profile 查询函数、
REPLACE GRANT、ZooKeeper 复制存储实验特性,让细粒度权限审计与多节点权限同步成为可能; - 分布式查询体验跃升:
distributed_push_down_limit默认开启、use_hedged_requests默认启用、分片连接复用,直接降低大规模集群上的查询延迟与连接压力。
升级到 v21.9 前,请优先评估四项向后不兼容变更(别名解析收紧、clickhouse-local 地址语义、聚合参数校验、Decimal 尾部零),特别是 Decimal 输出变化可能影响依赖文本对齐的报表与导出管线——可用output_format_decimal_trailing_zeros=1平滑过渡。完整的条目式变更清单可随时查阅 v21.9.1.8000-prestable changelog,后续版本的变更可参考 docs/changelogs 目录下的其他归档。
【免费下载链接】ClickHouseClickHouse® is a real-time analytics database management system项目地址: https://gitcode.com/GitHub_Trending/cli/ClickHouse
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考