跳到主要内容
跳到主要内容

ClickHouse中的压缩

ClickHouse查询性能的秘密之一就是压缩。

磁盘上的数据越少,I/O就越少,查询和插入的速度也越快。在大多数情况下,任何压缩算法在CPU方面的开销都将被I/O减少所抵消。因此,确保ClickHouse查询快速时,首先应关注数据压缩的改进。

关于ClickHouse为何能如此有效地压缩数据,我们推荐这篇文章。总之,作为一款面向列的数据库,值将按列顺序写入。如果这些值是有序的,相同的值将相邻存放。压缩算法利用数据的连续模式。在此基础上,ClickHouse具有编解码器和粒度数据类型,允许用户进一步调整压缩技术。

ClickHouse中的压缩将受到三个主要因素的影响:

  • 排序键
  • 数据类型
  • 使用的编解码器

所有这些都通过模式进行配置。

选择正确的数据类型以优化压缩

让我们以Stack Overflow数据集为例。比较posts表的以下模式的压缩统计数据:

  • posts - 一种未优化类型的模式,没有排序键。
  • posts_v3 - 一种类型优化的模式,为每一列选择了适当的类型和位数,而排序键为(PostTypeId, toDate(CreationDate), CommentCount)

使用以下查询,我们可以测量每一列当前的压缩和未压缩大小。让我们检查最初优化模式posts的大小,并且没有排序键。

我们在这里展示了压缩和未压缩大小。两者都很重要。压缩大小相当于我们需要从磁盘读取的内容——这方面我们希望尽量减少,以提升查询性能(和存储成本)。在读取之前,这些数据需要被解压缩。未压缩大小将依赖于所使用的数据类型。最小化这个大小将减少查询的内存开销,以及查询中必须处理的数据量,提高缓存的利用率,最终改善查询时间。

以上查询依赖于系统数据库中的columns表。这个数据库由ClickHouse进行管理,包含了有用的信息,从查询性能指标到后台集群日志。我们推荐"系统表和ClickHouse内部的窗口"及相关的文章[1][2]供感兴趣的读者参考。

为了总结表的总大小,我们可以简化以上查询:

posts_v3重复此查询,即拥有优化类型和排序键的表,我们可以看到未压缩和压缩大小的显著减少。

完整的列细分显示通过在压缩前对数据进行排序,并使用适当的类型,BodyTitleTagsCreationDate列取得了可观的节省。

选择正确的列压缩编解码器

通过列压缩编解码器,我们可以更改用于编码和压缩每一列的算法(及其设置)。

编码和压缩在目标上稍有不同:减小我们的数据大小。编码通过利用数据类型的性质,对我们的数据进行映射变换。相反,压缩使用通用算法在字节级别上压缩数据。

通常,编码会先应用,然后再使用压缩。由于不同的编码和压缩算法在不同值分布上有效,我们必须了解我们的数据。

ClickHouse支持大量编解码器和压缩算法。以下是按重要性排序的一些建议:

推荐原因
ZSTD一直是最佳选择ZSTD压缩提供最佳的压缩率。ZSTD(1)应是大多数常见类型的默认设置。通过修改数字值可以尝试更高的压缩率。我们很少看到在压缩成本(插入速度较慢)上高于3的值能带来足够的好处。
Delta适用于日期和整数序列每当你拥有单调序列或连续值的小增量时,基于Delta的编解码器表现良好。更具体地说,Delta编解码器在导数很小的情况下表现良好。如果不是,DoubleDelta值得一试(如果Delta的第一层导数已经非常小,通常增加不多)。单调增量均匀的序列将压缩得更好,例如DateTime字段。
Delta可以改善ZSTDZSTD在数据增量上是有效的——反之增量编码可以改善ZSTD的压缩。在ZSTD的情况下,其他编解码器很少进一步改善效果。
如果可能,优先选择LZ4而非ZSTD如果你在LZ4ZSTD之间得到可比的压缩,倾向于选择前者,因为它提供更快的解压和更少的CPU需求。然而,在大多数情况下,ZSTD的性能将比LZ4卓越。这些编解码器中的某些在与LZ4结合使用时可能工作得更快,同时与没有编解码器的ZSTD相比提供类似的压缩。不过,这将取决于具体数据,因此需要进行测试。
T64适用于稀疏或小范围数据T64在稀疏数据或块中范围较小时可能有效。避免对随机数使用T64
未知模式的数据使用GorillaT64如果数据具有未知模式,尝试GorillaT64可能是值得的。
Gorilla适用于测量数据Gorilla在浮点数据上可能有效,特别是表示测量读数的数据,即随机波动。

此处以获取更多选项。

下面我们为IdViewCountAnswerCount指定Delta编解码器,假设这些与排序键线性相关,因此应该受益于Delta编码。

这些列的压缩改进如下所示:

ClickHouse云中的压缩

在ClickHouse云中,我们默认使用ZSTD压缩算法(默认值为1)。尽管此算法的压缩速度可能因压缩等级(等级越高=越慢)而异,但它具有在解压缩时始终保持较快速度的优点(大约20%的变动),并且还受益于可并行化的能力。我们的历史测试还表明,这种算法通常是足够有效的,甚至可以超越与编解码器结合的LZ4。它对大多数数据类型和信息分布都有效,因此是一种明智的通用默认选择,这也是为什么我们最初的压缩在没有优化的情况下已经表现出色。