Compaction
对于基于 LSM 树的数据库,压缩是极其关键的。它将重叠的碎片化 SST 文件合并成一个有序的文件,丢弃已删除的数据,同时显著提高查询性能。
从 v0.9.1 版本开始,GreptimeDB 提供了控制 SST 文件如何压缩的策略:时间窗口压缩策略(TWCS)和严格窗口压缩策略(SWCS)。
概念
让我们从 GreptimeDB 中压缩的核心概念开始介绍。
SST 文件
当内存表刷新到持久存储(如磁盘和对象存储)时,会生成排序的 SST 文件。
在 GreptimeDB 中,SST 文件中的数据行按tag 列和时间戳组织,如下所示。每个 SST 文件覆盖特定的时间范围。当查询指定一个时间范围时,GreptimeDB 只检索可能包含该范围内数据的相关 SST 文件,而不是加载所有已持久化的文件。
通常,在实时写入工作负载中,SST 文件的时间范围不会重叠。然而,由于数据删除和乱序写入等因素,SST 文件可能会有重叠的时间范围,这会影响查询性能。
时间窗口
时间序列工作负载呈现出显著的“窗口”特征,即最近插入的行更有可能被读取。因此,GreptimeDB 将时间轴逻辑 上划分为不同的时间窗口,我们更关注压缩那些落在同一时间窗口内的 SST 文件。
特定表的时间窗口参数通常是从最新 flush 到存储的 SST 文件推断出来的,或者如果选择了 TWCS,您可以在建表时手动指定时间窗口。
GreptimeDB 预设了一组窗口大小,它们是:
- 1 小时
- 2 小时
- 12 小时
- 1 天
- 1 周
- 1 年
- 10 年
如果未指定时间窗口大小,GreptimeDB 将在第一次压缩时推断窗口,通过从上述集合中选择能够覆盖所有要压缩文件的整个时间跨度的,最小的时间窗口作为时间窗口大小。
例如,在第一次压缩期间,所有输入 SST 文件的时间跨度为 4 小时,那么 GreptimeDB 将选择 12 小时作为该表的时间窗口,并将此参数持久化以便后续的压缩中使用。
GreptimeDB 将包含最近插入时间戳的窗口视为活跃窗口(active window),而将之前的那些窗口视为非活跃窗口(inactive window)。
有序组
有序组(sorted runs)是一个包含已排序且时间范围不重叠的 SST 文件的集合。
例如,一个表包含 5 个 SST,时间范围如下(全部包括在内):[0, 10]
, [12, 23]
, [22, 25]
,[24, 30]
,[26,33]
,我们可以找到 2 个有序组:
有序组的数量往往能够反映 SST 文件的有序性。更多的有序组通常 会导致查询性能变差,因为特定时间范围的查询往往会命中多个重叠的文件。压缩的主要目标是减少有序组的数量。
层级
基于 LSM 树的数据库常常有多个层级,数据的键(key)会逐层进行合并。GreptimeDB 只有两个层级,分别是 0(未压缩)和 1(已压缩)。
压缩策略
GreptimeDB 提供了上述两种压缩策略,但在创建表时只能选择时间窗口压缩策略(TWCS)。严格窗口(SWCS)仅在执行手动压缩时可用。
时间窗口压缩策略(TWCS)
TWCS 主要旨在减少压缩过程中的读 / 写放大。
它将要压缩的文件分配到不同的时间窗口。对于每个窗口,TWCS 会识别有序组。如有序组的数量超过了允许的最大值,TWCS 会找到一个解决方案,在考虑合并惩罚的同时将其减少到阈值以下。如果有序组的数量没有超过阈值,TWCS 会检查是否存在过多的文件碎片,并在必要时合并这些碎片文件,因为 SST 文件数量也会影响查询性能。
对于窗口分配,SST 文件可能跨越多个时间窗口。为了确保不受陈旧数据影响,TWCS 根据 SST 的最大时间戳来进行分配。在时间序列工作负载 中,无序写入很少发生,即使发生了,最近数据的查询性能也比陈旧数据更为重要。
TWCS 提供了 4 个参数供调整:
max_active_window_runs
: 活跃窗口中最大允许存在的有序组数量(默认为 4)max_active_window_files
: 活跃窗口中最大允许存在的文件数量(默认为 4)max_inactive_window_runs
: 非活跃窗口中最大允许存在的有序组数量(默认为 1)max_inactive_window_files
: 非活跃窗口中最大允许存在的文件数量(默认为 1)
您可以为活跃窗口和非活跃窗口设置不同的阈值。这很重要,因为乱序写入通常发生在活跃窗口中。通过允许更多重叠文件存在于活跃窗口,TWCS 在数据摄取过程中减少了写放大,并在活跃窗口变为非活跃时合并所有这些文件。
以下图表显示了当最大活跃窗口允许的有序组数量为 1 时,活跃窗口中的文件如何被压缩:
- 在 A 中,有两个 SST 文件
[0, 3]
和[5, 6, 9]
,但由于这两个文件的时间范围不重叠,因此只有一个有序组。 - 在 B 中,一个新的 SST 文件
[1, 4]
被刷新,因此形成了两个有序组。然后将[0, 3]
和[1, 4]
合并为[0, 1, 3, 4]
. - 在 C 中,一个新的 SST 文件
[9, 10 ]
被刷新,并且它将与[5, 6, 10 ]
合并以创建[5, 6, 9, 10]
. - D 是最终状态,两个压缩后的文件中形成一个有序组。