50万奖金+官方证书,深圳国际金融科技大赛正式启动,点击报名 了解详情
写点什么

性能飞跃!ClickHouse 处理复杂 JSON,速度提升 58 倍,内存减少 3300 倍

  • 2025-10-23
    北京
  • 本文字数:3008 字

    阅读完需:约 10 分钟

大小:1.46M时长:08:28
性能飞跃!ClickHouse 处理复杂 JSON,速度提升58倍,内存减少3300倍

2024 年 8 月,ClickHouse v24.8 引入了功能强大的 JSON 数据类型。从那时起,我们持续通过引入新特性和优化手段不断改进它。


本文将介绍我们如何在 ClickHouse v25.8 中再次实现重大性能提升。


ClickHouse 一直是分析性能方面的行业标杆,而 v25.8 的最新改进也使 ClickHouse 成为处理 JSON 数据分析的领先方案。

v24.8 中 JSON 类型的工作原理 


我们先快速回顾一下,在 ClickHouse v24.8 中,JSON 数据在 MergeTree 分区中的存储方式(https://clickhouse.com/blog/a-new-powerful-json-data-type-for-clickhouse)。


如下图所示,我们有一组包含 K 个唯一路径的 JSON 数据,每个路径对应一个字符串值。前 N 个路径作为“动态路径”被存储为子列。其余 K – N 个路径则被存储在一个共享的数据结构中,即一个 Map(String, String) 类型的列,包含路径和值的映射。



例如,当我们查询路径 key1 的数据时,ClickHouse 首先会通过元数据判断 key1 是属于动态路径还是共享数据。在本例中,key1 属于动态路径,因此其值被存储在独立的数据文件中,能够被高效、直接地读取。



但如果我们查询的是 key_n+1,元数据会显示该路径不在动态路径中,而是存储在共享数据中。此时,ClickHouse 需要读取整个 Map(String, String) 列并在内存中进行过滤处理,效率显著下降。



默认情况下,动态路径的数量限制为 1024。当路径数量超过此限制时,尤其是在使用 S3 等远程存储时,贸然提高该限制并不可取,因为每个分区可能生成成千上万的文件,这会在数据合并过程中显著增加内存使用量,并加剧读取复杂度。


因此,对于包含成千上万乃至数万个唯一 JSON 路径的工作负载,系统性能会明显下降。

这正是我们希望解决的难题。


v25.8 引入的新共享数据序列化格式


ClickHouse v25.8 推出了两种新的共享数据序列化格式,显著提升了读取特定路径时的效率。

分桶式共享数据


第一种新格式通过将共享数据划分为 N 个桶进行组织。每个桶包含一个各自独立的 Map(String, String) 列,并通过固定的规则将路径分配到不同的桶中。


当查询如 key_m1 这类路径时,ClickHouse 可直接判断该路径位于哪个桶中,仅读取对应的桶,其余桶则完全跳过。


相较于读取整个共享数据,这种方式大幅减少了扫描的数据量,从而提高了查询性能。但需要注意的是,若桶的数量设置过多,文件数量也会随之增加,容易带来文件系统的额外负担。

高级共享数据


第二种方式采用了更为强大的高级序列化格式。


在该格式中,每个桶包含以下三个文件:


  • .structure:每个 granule(数据块)的元信息,包括行数、包含的路径列表,以及在 .paths_marks 文件中的偏移位置;

  • .data:实际路径数据,按列式格式按 granule 存储;

  • .paths_marks:记录每个路径在 .data 文件中起始位置的偏移指针。



当查询路径 key_m1 时,ClickHouse 首先读取 .structure 文件,判断当前 granule 是否包含该路径;如果不包含,则跳过该 granule;如果包含,则通过 .paths_marks 文件找到其偏移位置,并直接跳转到 .data 文件中对应的路径数据进行读取。


这种方式避免了无关路径数据被加载进内存,从而极大提升了查询效率。

支持嵌套路径


许多 JSON 文档包含嵌套结构,例如对象数组。通过上述方法提取这类嵌套路径时,仍需读取整个数组,对于处理大型数据负载而言效率较低。


为了解决这一问题,我们对高级序列化格式进行了扩展,引入了用于子列处理的额外文件。


在该扩展格式中,每个桶包含以下 6 个文件:


  • .structure:每个 granule 的元信息,包括行数、路径列表,以及在 .paths_marks 和 .substreams_metadata 文件中的偏移位置;

  • .data:实际路径数据,以列式格式按 granule 存储,并被划分为多个子流(substreams)。同一路径可能对应多个子流。该结构允许 ClickHouse 仅读取重建目标子列所需的子流,而无需扫描整个路径值;

  • .paths_marks:指向 .data 文件中每个路径数据起始位置的偏移;

  • .substreams_marks:指向 .data 文件中每个子流数据起始位置的偏移;

  • .substreams:记录每个 granule 中各路径对应的子流列表。不同 granule 中可能存在差异,例如某些数组对象可能包含不同的嵌套字段;

  • .substreams_metadata:为每个路径记录其在 .substreams 和 .substreams_marks 文件中的偏移,建立路径与其子列及数据位置之间的映射关系。



当查询路径 key_m1 的子列时,ClickHouse 首先读取 .structure 文件,判断当前 granule 是否包含该路径。如果不包含,则直接跳过该 granule;如果包含,则根据 .structure 中记录的偏移,读取 .substreams_metadata 文件中对应项,获取 key_m1 的偏移信息。


接着,ClickHouse 根据第一个偏移,从 .substreams 文件中读取该路径在当前 granule 中的子流列表。如果列表中不包含所需的子流,该 granule 会被跳过;若包含所需子流,则 ClickHouse 会根据第二个偏移,从 .substreams_marks 中读取子流位置,并仅从 .data 文件中读取这些子流的数据。


在成功重建目标子列后,ClickHouse 会继续处理下一个 granule。



这种方式避免了加载不相关路径及其无关子流的数据,从而显著提升了嵌套子列的读取性能。

平衡效率与兼容性


高级序列化格式在支持选择性读取方面具备显著优势,但也带来了一定的性能开销:当需要读取整个 JSON 列或执行合并操作时,由于共享数据在内存中的表示方式(Map(String, String))与其存储布局差异较大,会引发较重的数据结构转换,导致性能下降。


为此,ClickHouse v25.8 采取了一种折中的方案:在保存高级格式的同时,附加存储一份原始格式的数据副本。虽然这会使存储空间需求翻倍,但可以在保留高级格式选择性读取优势的同时,避免整体读取和合并操作中的性能损失。


如下图所示,原始格式副本被存储在三个额外的文件中:


  • .copy.offsets:原始 Map(String, String) 列中各项的偏移信息;

  • .copy.indexes:跨桶路径的索引(避免重复存储所有路径);

  • .copy.values:原始 Map(String, String) 列中的实际值。



这种副本机制确保了读取完整 JSON 列和执行合并操作的性能不会下降,同时依然保留了高级格式在选择性读取中的优化效果。

性能评估


我们对包含 10、100、1000 和 10,000 条唯一路径的 JSON 数据,使用新的序列化格式进行了性能基准测试。


测试结果显示,高级共享数据序列化在查询速度和内存使用方面的表现,与将所有路径作为动态子列存储的方式相当,同时具备良好的扩展性,可应对数万路径的复杂 JSON 数据。


以下为 10,000 路径测试的一些关键结果,展示了 v25.8 中新序列化机制带来的显著优化。

性能测试 1(选择性读取)


在本项测试中,我们从包含 20 万行数据的表中读取单个 JSON 键。每行均包含一个含 1 万条路径的 JSON 文档,采用宽格式存储。


测试结果表明,使用高级序列化格式后,相较原始 JSON 序列化方式,读取时间提升约 58 倍,内存使用下降约 3,300 倍。


Data type

Time (seconds)

Memory usage (MiB)

JSON no shared data

0.027

18.64 MiB

JSON shared data "advanced"

0.063

3.89 MiB

JSON shared data "map_with_buckets"

0.087

403.55 MiB

String

3.216

582.70 MiB

Map

3.594

538.37 MiB

JSON shared data "map"

3.63

12.53 GiB

性能测试 2(读取完整 JSON 对象)


本测试从同一数据表中读取每一行的完整 JSON 文档。


测试结果显示,使用高级序列化格式时,读取整个文档的性能几乎与原始 JSON 序列化方式一致。这表明,在保留对选择性读取显著加速的同时,整体读取性能并未受到影响。

结论


全新的共享数据序列化机制将 ClickHouse 对 JSON 数据的支持提升至新高度。借助这一改进,用户可以高效地查询包含数万条唯一路径的复杂 JSON 文档,并在执行选择性读取时获得极佳性能。


这一增强能力进一步巩固了 ClickHouse 在处理大规模半结构化 JSON 数据分析场景中的领先地位。

2025-10-23 14:587772

评论

发布
暂无评论

淘宝图搜接口功能解析,精准搜索相似商品

tbapi

淘宝图片搜索接口 淘宝拍立淘接口 淘宝图片api

伊克罗德信息助力傲雷部署飞连平台,全球化管理效能全面升级

伊克罗德信息科技

国内外顶尖RPA工具有哪些?

Techinsight

Studio 3T 2025.12 发布,新增功能简介

sysin

Studio 3T

新《公司法》实施周年实务挑战加剧,Alpha系统「公司法专题库」破解律师专业升级困局

科技汇

FinClip驱动App轻量化重构:组件化生态赋能前端效能跃迁

xuyinyin

某医药集团月结之战:从“数据泥潭”到“秒级决胜”的破局之路

YMatrix 超融合数据库

财务 HTAP 超融合数据库 HTAP 场景实践 YMatrix

开放创新,昇腾 CANN 再向深处

极客天地

Nessus Professional 10.9 Auto Installer for macOS Sequoia

sysin

Nessus

DNS解析中的TTL值的设置方法和注意事项

防火墙后吃泡面

2025企业数字化转型之道

Techinsight

网站访问分析30分钟实战指南:ClkLog开源社区版

ClkLog

开源 埋点 sdk 用户行为分析 画像

Tenable Nessus 10.9.0 (macOS, Linux, Windows) - 漏洞评估解决方案

sysin

Nessus

Nessus Professional 10.9 Auto Installer for Ubuntu 24.04

sysin

Nessus

商品中心—缓存与DB一致性的技术文档

不在线第一只蜗牛

Java 数据库

Nessus Professional 10.9 Auto Installer for Windows

sysin

Nessus

Wing Pro 11.0.1 (macOS, Linux, Windows) - Python IDE

sysin

Python

WPS Office for Mac 2025 夏季更新 12.1.21861 - 写作、表格处理、PPT 制作和 PDF 编辑

sysin

WPS Office

深入理解Spring AOP:核心原理与代理机制详解

量贩潮汐·WholesaleTide

Java spring

Nessus Professional 10.9 Auto Installer for RHEL 9, AlmaLinux 9, Rocky Linux 9

sysin

Nessus

AI驱动,治理升级!数造科技亮相中博会,打造一站式数据开发治理新范式

数造万象

人工智能 数据治理 数据开发 热门推荐 AI 智能体

企业应当如何进行数字化转型呢?

Techinsight

博睿数据出席GOPS全球运维大会,深度解析如何让大模型真正“懂”运维!

博睿数据

通义灵码2.5 | 一个更懂开发者的 AI 编程助手

阿里巴巴云原生

阿里云 通义灵码

通义灵码2.5 | 一个更懂开发者的 AI 编程助手

阿里云云效

阿里云 通义灵码

Pixelmator Pro 3.7 发布,新增功能简介

sysin

Pixelmator Pro

接入豆包大模型,巨人网络解锁《太空杀》AI游戏玩法新高度

新消费日报

时序数据库 TDengine × Looker Studio:不懂设计也能做出高颜值报表

TDengine

tdengine 数据分析 可视化 时序数据库 时序数据库tdengine

扫描全能王联合上海电影博物馆发起特色探馆活动,AI助力存档百年光影

合合技术团队

人工智能 算法 #大数据

Nextcloud Android 客户端 - 安全高效的文件同步与管理

qife122

开源 Nextcloud

聊点技术 | 大模型多Agent结合根因分析,能碰撞出什么火花?

博睿数据

性能飞跃!ClickHouse 处理复杂 JSON,速度提升58倍,内存减少3300倍_AI&大模型_ClickHouse_InfoQ精选文章