真实对比Nullable(UInt64)列和普通UInt64列的物理磁盘文件,纠正存储膨胀的常见误解,说明Nullable真实的性能代价所在。
代码CREATE TABLE demo.nulltest (k UInt64, v_plain UInt64, v_nullable Nullable(UInt64)) ENGINE=MergeTree ORDER BY k; INSERT INTO demo.nulltest SELECT number, number, if(number%100=0, NULL, number) FROM numbers(20000000); -- 真实1%的行是NULL -- 真实合并成1个part之后,直接看磁盘上的.bin文件
ls -la part目录/*.bin → 0f231cbc...bin 20460777 字节 ← k列 258dee36...bin 20460777 字节 ← v_plain列 796d0908...bin 20460777 字节 ← v_nullable的数据文件(跟普通UInt64一样大) d9779c7c...bin 15912 字节 ← v_nullable额外多出的null-mask文件 真实多出的代价:15912 / 20460777 ≈ 0.078%
Nullable(UInt64)真实在磁盘上被拆成了两个独立文件:一个跟普通UInt64列完全一样大的数据文件(NULL的位置也占了一个UInt64槽位,只是值不作数),加一个额外的null-mask文件——本质是一个每行1个bit、标记这一行是否为NULL的位图,本文1%的行是NULL,这个位图真实高度可压缩,只占了15,912字节,相对2000万行的数据体量几乎可以忽略。
每一步都是本文上面用真实磁盘文件对比跑出来的原始结果。