列式存储是一种常用的存储技术。通常,它意味着高性能,并且基本上已经成为当今分析数据库的标准配置。

列式存储的基本原理是减少从硬盘检索的数据量。数据表可以有很多列,但计算可能只使用其中的极少数列。使用列式存储,不需要检索无用的列,而使用行式存储,则需要扫描所有列。当检索到的列只占总数的很小一部分时,列式存储在 IO 时间方面具有很大优势,并且计算似乎变得更快。

但是列式存储也有另一面 – 它对于任何场景来说都不是最快的。

实现列式存储比实现行式存储复杂得多,因为对于数据表来说,列数可以提前确定,但行数不会停止增长。使用行式存储,我们根据记录的顺序将数据写入并追加到表中。将数据表存储为单个文件很容易。但这不适用于以柱状格式存储的数据。由于会有数据追加,我们无法事先知道行数,因此不可能先写完一列再写下一列。通常,我们将存储空间划分为多个块,向一个块中写入固定行数(用N表示),写入完成后移动到下一个块。稍后,将逐块检索数据。在每个块中,数据以列方式存储,而块之间,数据可以视为按行方式存储。需要一个特殊的管理模块,用一个目录来记录不断增长的数据块及其存储的每一列的信息,这造成了很多不便。因此,在单个数据文件中实现列式存储是很困难的。这种存储模式通常是专门的数据仓库产品所采用的。

但是,当数据量不大时,块存储机制对于并行处理的实现并不友好。并行处理需要将数据分为多个段。要做到这一点,有两个条件:每个段中的数据量几乎相等(每个线程的处理负载相等)和灵活分段的能力(无法预先确定并行任务的数量)。行式数据可以根据行数进行分段,即使对于非常少量的数据也可以进行并行处理。按列的数据只能被划分为块,而数据不能进一步划分。记录数(上述N)不能太少;否则,会因最小磁盘检索单元的存在而浪费太多资源。在N=1的极端情况下,存储模式等于行存储。当N太小时,涉及的数据总量很大,目录就会变得很大,给内容管理带来负担。因此,N通常指定为一百万或以上。为了灵活地对数据进行分段,至少需要数百个数据块。也就是说,只有当总量至少达到数亿数据行时,列式数据的并行计算才会变得流畅。

集算器SPL提供双增量分段策略,使N随着数据量的增加而增长,同时保持数据块数量不变。这样,也可以固定目录的大小,可以方便地在单个文件中实现列式存储,并且可以实现灵活的分段,以便对少量数据进行并行计算。

根据列式存储的原理,只有当计算涉及的列数相对较少时,存储模式才会带来明显的优势。很多性能测试用例(例如作为国际标准的TPCH)都选择这样的计算场景,以便于发挥列式数据库的优势。这些只是现实业务场景的一部分。在金融行业中,计算涉及一百多个列的表中的大部分列的情况并不罕见。在这种情况下,列式存储的优势只能发挥一半。即使列式存储比行式存储具有更高的压缩比和更少的检索数据量,但当许多列参与计算时,其优势并不那么明显。毕竟,检索按列存储的数据的过程比检索按行存储的数据复杂得多。

因此,当现实世界的计算性能不如测试用例时,这是正常的,这并不意味着测试结果是假的。

列式存储还会导致随机磁盘访问。每列中的数据是连续存储的,但不同列中的数据不是连续存储的。检索的列越多,检索产生的随机性程度就越严重,即使对于单线程任务也是如此。对于SSD来说,这不是一个很严重的问题,因为当每列数据连续,并且上述N足够大时,检索成本所占比例很小,并且SSD没有寻道时间。

但是对于有寻道时间的硬盘来说,这个问题就变得灾难性的。当访问大量列时,性能很可能甚至不如按行存储。并发和并行处理都会使问题恶化。另一方面,增加缓存的大小来缓解问题会占用过多的内存空间。

尝试使用 HDD 上的列式存储时要小心。

列式存储的另一个大问题是它的索引性能比行式存储低得多。正如我们所说,索引表存储有序的键值及其对应记录在原表中的位置。对于行式存储,一条记录的位置可以用一个数字来表示,但是对于列式存储,一条记录的每一列都有不同的位置,原则上这些位置都应该被记录。这会创建一个几乎与原始表一样大的索引表,从而导致大量的存储利用率和较高的检索成本。这和复制原表排序的方法没有太大区别。

当然,在现实世界中没有人会这样做。一般的做法还是前面提到的块存储机制。索引仅存储记录的序号。搜索从索引表中读取序数,找到相应的块,从第一个记录“计数”到块中具有相应序数的记录,并检索列值。对每一列执行“计数”操作。在最好的情况下,将读取与列数相等的磁盘单元数;如果你不幸运,整个区块都会被扫描。相比之下,行式存储的索引通常只需要读取一到两个磁盘单元(由记录占用的空间决定)。列式存储检索的数据量是行式存储的几十倍甚至一百倍。对于 HDD,还存在难以忍受的寻道时间。因此,列式存储基本无法处理高并发的查询需求。

使用列式存储进行遍历,使用行式存储进行搜索。对于遍历和搜索都要求高性能的数据,甚至需要冗余存储两份数据。数据平台应该允许程序员针对每种计算场景采用最合适的存储模式,而不是针对所有场景做出相同的决定。

那么,集算器SPL允许用户在行式存储和列式存储之间选择更合适的一种。它还提供附加值索引策略来存储面向遍历的列式数据的行式数据副本以供搜索。

Comments are closed.