bigywater.tech
ClickHouse 内部原理 · 48

Array、Tuple、Map 的底层存储

真实建一张含Array/Tuple/Map三种复合类型的表,用columns_substreams.txt精确核对每种类型在磁盘上真实拆分成了几个物理文件。

10个物理文件
真实4个逻辑列在磁盘上拆分出的.bin文件数
Map=Array(Tuple)
真实Map内部被当成Array(Tuple(key,value))处理

做的事:真实建4个不同类型的列,看它们在磁盘上拆成了几个文件

代码CREATE TABLE demo.atmtest ( k UInt64, arr Array(UInt32), tup Tuple(a UInt32, b String), mp Map(String, UInt32) ) ENGINE=MergeTree ORDER BY k; INSERT INTO demo.atmtest SELECT number, [number,number+1,number+2], (number, toString(number)), map('x', number, 'y', number+1) FROM numbers(1000000);

验证:4个逻辑列,真实拆成了10个物理文件

实测 · 本机执行输出(columns_substreams.txt,真实的文件拆分清单)
1 substreams for column `k`:
	k                        ← 简单类型,1个文件

2 substreams for column `arr` (Array(UInt32)):
	arr.size0                ← 真实存每行数组的长度/偏移信息
	arr                      ← 真实存所有数组元素拍平后的值

3 substreams for column `tup` (Tuple(a UInt32, b String)):
	tup.a                    ← Tuple的第1个字段,真实独立存储
	tup.b.size               ← Tuple的第2个字段(String),真实需要长度前缀
	tup.b                    ← 第2个字段的真实数据

4 substreams for column `mp` (Map(String, UInt32)):
	mp.size0                 ← 真实Map当成Array(Tuple)处理,先有长度信息
	mp.keys.size             ← key是String,真实需要长度前缀
	mp.keys                  ← 所有key真实拍平存储
	mp.values                ← 所有value真实拍平存储

真实总计:1+2+3+4 = 10个物理.bin文件,对应磁盘上真实的10个文件

这份真实的columns_substreams.txt精确对上了磁盘上真实存在的10个.bin文件——不多不少。

三种复合类型,真实各自不同的分解方式

Array(UInt32)真实是最简单的复合类型:所有行的所有数组元素被"拍平"存成一个长长的UInt32数据流,另外配一个size0记录每一行数组的长度,读取第N行时先查size0算出这行数据在拍平数据流里的起止偏移,再去数据流里真实切出对应的一段——这也是为什么数组本身的读取代价,真实是跟数组总元素个数(而不是行数)更相关。Tuple(a UInt32, b String)真实是"名义上的复合类型,物理上完全独立":Tuple的每个命名字段真实各自享有一套独立的存储(字段a是纯UInt32的1个substream,字段b作为String需要size+data两个substream),本质上跟"直接声明两个独立的普通列"在物理层面几乎没有差别,只是在SQL语义上被捆绑成了一个逻辑单元。Map(String, UInt32)真实的实现方式最有意思——它不是一种独立的存储结构,而是内部被当成Array(Tuple(key, value))来处理:先有一个size0记录每行有多少个键值对,然后keys和values各自拍平存成两条独立的数据流,这也解释了为什么Map类型的key和value可以直接復用String/数值类型已有的编码和压缩能力(本系列前面几篇验证过的codec),而不需要给Map单独发明一套存储格式。

分步演示:从逻辑列到真实的物理substream拆分

每一步都是本文上面用真实columns_substreams.txt核对出来的原始结果。

4个逻辑列,真实拆成10个物理文件
这一步新拆解的类型

    本文用到的工具

    • 本机 ClickHouse 26.9.4(单节点),真实的part目录 columns_substreams.txt
    • 真实核对.bin文件数量与substream清单完全对应
    京ICP备2026067502号-1京公网安备11010502063487号