纯内存场景下列存储(Colstore)与行存储(Rowstore)的性能差异疑问
内存场景下行列存储排布差异问题解答
首先明确结论:即使数据集完全存于内存、无任何磁盘IO操作,行、列存储的排布差异依然会带来非常显著的性能区别。你梳理的4点判断大部分准确,以下是逐一验证:
- 针对8字节以下的字段,列存储所需的内存访问次数少于行存储:基本准确。行存场景下如果仅查询少量小字段,需要跳过整行其他字段的内存地址跳转寻址,单次64字节cache line加载能拿到的目标字段数量远低于列存。列存同字段连续排布,单个cache line最多可装8个8字节字段、16个4字节字段,不需要多余跳转,确实能减少内存访问次数,只有当你需要读取一行的所有字段时,这个优势才会消失。
- 无论是否落盘,列存储的压缩实现难度更低,就算无需回写,压缩依然会对内存运算产生影响:完全准确。列存同字段数据的相似度远高于行存,字典编码、RLE、增量编码等常见压缩算法的实现难度和压缩率都远优于行存。哪怕不落盘,压缩后的列数据占用内存更小,同等内存带宽下单位时间能加载的有效数据量更高,现在通用的轻量压缩算法解压速度都远快于内存访问速度,整体运算性能反而会提升,无需回写的场景下不用考虑压缩修改的开销,收益会更明显。
- 列存储更易实现操作向量化:完全准确。现在CPU的SIMD指令(AVX、SSE等)要求操作的数在内存中连续排布,列存的同字段连续存储天然满足这个要求,一条SIMD指令可以同时处理8/16/32个同类型字段,行存要实现向量化还需要额外做数据gather操作,开销极高,大部分场景下收益很低甚至是负收益。
- 行存储显然更便于按行维度处理
struct类型数据:完全准确。行存的一行数据就是天然的struct内存排布,直接按结构体指针访问即可,不需要做字段拼接,涉及整行读取、多字段复杂关联按行处理的场景,行存的开销远低于列存。
其他未被考虑到的核心差异点
- 缓存命中率差异:如果查询只涉及全表少量字段,列存的缓存命中率会比行存高数倍到数十倍,不会把cache空间浪费在不需要的字段上;但如果是随机单条行查询,行存只需要加载一次cache line就能拿到整行所有字段,列存需要去每个字段的内存位置分别加载,缓存命中率反而更低。
- 谓词下推执行效率:列存可以直接对单列做过滤,不需要加载其他字段,过滤后再取需要的字段即可,行存需要先加载整行再做过滤,过滤效率低很多。
- 内存碎片差异:如果涉及数据更新删除,行存很容易产生碎片化内存,只读列存的内存是连续大段分配,碎片化程度极低,内存利用率更高。
- 分支预测效率差异:列存同类型数据连续存储,做运算的时候不需要反复判断字段类型,异常处理的分支预测命中率更高,CPU流水线停顿更少。
只读数据集场景的性能差异
需要分场景判断,不能一概而论:
- 会带来数倍到数十倍大幅性能提升的场景:OLAP类查询,比如涉及全表扫描、只取少数字段做聚合/统计/过滤操作的场景,列存的缓存优势、向量化优势、压缩优势会完全体现出来,性能差距非常大。
- 只有边际优化甚至性能更差的场景:需要频繁查询整行所有字段、或者大量随机单行查询的场景,列存的字段拼接、随机寻址开销会抵消所有优势,反而不如行存性能高。
内容的提问来源于stack exchange,提问作者David542
相关产品推荐
相关产品推荐

