Apache Ignite 中的数据压缩。 斯贝尔的经历

Apache Ignite 中的数据压缩。 斯贝尔的经历当处理大量数据时,有时会出现磁盘空间不足的问题。 解决这个问题的一种方法是压缩,因此,在相同的设备上,您可以增加存储量。 在本文中,我们将了解 Apache Ignite 中数据压缩的工作原理。 本文将仅描述产品内实现的磁盘压缩方法。 其他数据压缩方法(通过网络、在内存中),无论是否实现,都将不在讨论范围之内。

因此,启用持久模式后,由于缓存中的数据发生更改,Ignite 开始写入磁盘:

  1. 缓存的内容
  2. 预写日志(以下简称 WAL)

WAL 压缩机制已经存在很长一段时间了,称为 WAL 压缩。 最近发布的 Apache Ignite 2.8 引入了另外两种机制,允许您压缩磁盘上的数据:用于压缩缓存内容的磁盘页面压缩和用于压缩某些 WAL 条目的 WAL 页面快照压缩。 以下是有关这三种机制的更多详细信息。

磁盘页面压缩

怎么开动这个

首先,让我们简单了解一下 Ignite 如何存储数据。 页存储器用于存储。 页大小在节点开始时设置,并且不能在后续阶段更改;此外,页大小必须是文件系统块大小的 XNUMX 的幂和倍数。 根据需要将页面从磁盘加载到 RAM 中;磁盘上的数据大小可能超过分配的 RAM 量。 如果 RAM 中没有足够的空间来从磁盘加载页面,则旧的、不再使用的页面将从 RAM 中逐出。

数据以如下形式存储在磁盘上:为每个缓存组的每个分区创建一个单独的文件;在该文件中,页面按索引升序依次出现。 全页标识符包含文件中的缓存组标识符、分区号和页索引。 因此,使用完整页面标识符,我们可以唯一地确定文件以及每个页面在文件中的偏移量。 您可以在 Apache Ignite Wiki 文章中阅读有关分页内存的更多信息: 点燃持久存储 - 在引擎盖下.

正如您可能从名称中猜到的那样,磁盘页面压缩机制在页面级别工作。 启用此机制后,RAM 中的数据将按原样处理,不进行任何压缩,但当页面从 RAM 保存到磁盘时,它们会被压缩。

但单独压缩每个页面并不能解决问题;您需要以某种方式减小生成的数据文件的大小。 如果页面大小不再固定,我们就无法再将页面一个接一个地写入文件,因为这可能会产生许多问题:

  • 使用页面索引,我们将无法计算它在文件中的偏移量。
  • 目前尚不清楚如何处理不在文件末尾的页面并更改其大小。 如果页面大小减小,它释放的空间就会消失。 如果页面大小增加,您需要在文件中寻找新的位置。
  • 如果一页移动的字节数不是文件系统块大小的倍数,则读取或写入该页将需要再触及一个文件系统块,这可能会导致性能下降。

为了避免在自己的级别上解决这些问题,Apache Ignite 中的磁盘页面压缩使用了一种称为稀疏文件的文件系统机制。 稀疏文件是其中一些零填充区域可以被标记为“空洞”的文件。 在这种情况下,不会分配任何文件系统块来存储这些空洞,从而节省磁盘空间。

逻辑上,为了释放文件系统块,洞的大小必须大于或等于文件系统块,这对页面大小和 Apache Ignite 施加了额外的限制:为了使压缩产生任何效果,页大小必须严格大于文件系统块的大小。 如果页大小等于块大小,那么我们将永远无法释放单个块,因为为了释放单个块,压缩页必须占用 0 字节。 如果页面大小等于 2 或 4 个块的大小,并且页面分别压缩到至少 50% 或 75%,我们就已经能够释放至少一个块。

因此,该机制如何工作的最终描述是:将页面写入磁盘时,会尝试压缩该页面。 如果压缩页面的大小允许释放一个或多个文件系统块,则该页面将以压缩形式写入,并在释放的块的位置创建一个“洞”(执行系统调用) fallocate() 与打孔标志)。 如果压缩页的大小不允许释放块,则该页将按原样保存,未压缩。 所有页面偏移量的计算方式与未压缩时相同,即页面索引乘以页面大小。 您无需自行重新定位页面。 页面偏移量,就像没有压缩一样,落在文件系统块的边界上。

Apache Ignite 中的数据压缩。 斯贝尔的经历

在当前的实现中,Ignite只能在Linux操作系统下处理稀疏文件;因此,只有在该操作系统上使用Ignite时才能启用磁盘页面压缩。

可用于磁盘页面压缩的压缩算法:ZSTD、LZ4、Snappy。 此外,还有一种操作模式(SKIP_GARBAGE),其中仅丢弃页面中未使用的空间,而不对剩余数据进行压缩,与前面列出的算法相比,这减少了CPU的负载。

性能影响

不幸的是,我没有在真实的展台上进行实际的性能测量,因为我们不打算在生产中使用这种机制,但我们可以从理论上推测我们会在哪里输,在哪里会赢。

为此,我们需要记住访问时如何读写页面:

  • 执行读操作时,首先在 RAM 中搜索;如果搜索不成功,则由执行读操作的同一线程将该页从磁盘加载到 RAM 中。
  • 当执行写操作时,RAM 中的页面被标记为脏页面,但执行写操作的线程不会立即将该页面物理保存到磁盘。 所有脏页稍后都会在单独线程的检查点过程中保存到磁盘。

所以对读操作的影响是:

  • 正(磁盘 IO),由于读取文件系统块的数量减少。
  • 负 (CPU),因为操作系统需要额外的负载来处理稀疏文件。 也有可能这里会隐式出现额外的 IO 操作,以保存更复杂的稀疏文件结构(不幸的是,我并不熟悉稀疏文件如何工作的所有细节)。
  • 负(CPU),由于需要解压缩页面。
  • 对写操作没有影响。
  • 对检查点进程的影响(这里的一切都与读操作类似):
  • 正(磁盘 IO),由于写入的文件系统块数量减少。
  • 由于使用稀疏文件,因此为负(CPU,可能是磁盘 IO)。
  • 负数(CPU),由于需要页面压缩。

天平的哪一侧会使天平倾斜? 这在很大程度上取决于环境,但我倾向于相信磁盘页面压缩很可能会导致大多数系统的性能下降。 此外,对使用类似方法处理稀疏文件的其他 DBMS 进行的测试表明,启用压缩后性能会下降。

如何启用和配置

如上所述,支持磁盘页面压缩的 Apache Ignite 最低版本为 2.8,并且仅支持 Linux 操作系统。 启用并配置如下:

  • 类路径中必须有一个 ignite-compression 模块。 默认情况下,它位于 Apache Ignite 发行版的 libs/Optional 目录中,并且不包含在类路径中。 您只需将目录向上移动一级到 libs,然后当您通过 ignite.sh 运行它时,它将自动启用。
  • 必须启用持久性(通过启用 DataRegionConfiguration.setPersistenceEnabled(true)).
  • 页大小必须大于文件系统块大小(您可以使用以下命令设置它) DataStorageConfiguration.setPageSize() ).
  • 对于每个需要压缩数据的缓存,您必须配置压缩方法和(可选)压缩级别(methods CacheConfiguration.setDiskPageCompression() , CacheConfiguration.setDiskPageCompressionLevel()).

WAL压缩

怎么开动这个

什么是 WAL 以及为什么需要它? 非常简单:这是一个包含最终更改页面存储的所有事件的日志。 它主要是为了能够在跌倒时恢复。 任何操作,在将控制权交给用户之前,必须首先在 WAL 中记录一个事件,以便万一发生故障,可以在日志中回放并恢复用户收到成功响应的所有操作,即使这些操作没有时间反映在磁盘上的页面存储中(上面已经描述了对页面存储的实际写入是在称为“检查点”的过程中完成的,并且由单独的线程有一些延迟)。

WAL 中的条目分为逻辑条目和物理条目。 布尔值是键和值本身。 物理 - 反映页面存储中页面的更改。 虽然逻辑记录对于其他一些情况很有用,但物理记录仅用于在崩溃时进行恢复,并且仅需要自上次成功检查点以来的记录。 这里我们不会详细解释为什么会这样,但是有兴趣的可以参考 Apache Ignite Wiki 上已经提到的文章: 点燃持久存储 - 在引擎盖下.

每个逻辑记录通常有多个物理记录。 也就是说,例如,对高速缓存的一次放入操作会影响页存储器中的多个页面(具有数据本身的页面、具有索引的页面、具有空闲列表的页面)。 在一些综合测试中,我发现物理记录占据了 WAL 文件的 90%。 但是,需要它们的时间很短(默认情况下,检查点之间的间隔为 3 分钟)。 在失去相关性后删除这些数据是合乎逻辑的。 这正是 WAL 压缩机制的作用:它去掉物理记录并使用 zip 压缩剩余的逻辑记录,同时文件大小显着减小(有时缩小数十倍)。

物理上,WAL 由若干个固定大小(默认为 10MB)的段(默认为 64 个)组成,这些段以循环方式覆盖。 一旦当前段被填充,下一个段就会被指定为当前段,并且填充的段将由单独的线程复制到存档中。 WAL 压缩已经适用于归档段。 此外,作为一个单独的线程,它监视检查点的执行,并开始压缩不再需要物理记录的存档段。

Apache Ignite 中的数据压缩。 斯贝尔的经历

性能影响

由于 WAL 压缩作为单独的线程运行,因此不会对正在执行的操作产生直接影响。 但它仍然会给CPU(压缩)和磁盘带来额外的后台负载(从归档中读取每个WAL段并写入压缩段),因此如果系统以其最大容量运行,也会导致性能下降。

如何启用和配置

您可以使用属性启用 WAL 压缩 WalCompactionEnabled в DataStorageConfiguration (DataStorageConfiguration.setWalCompactionEnabled(true))。 此外,如果您对默认值 (BEST_SPEED) 不满意,可以使用 DataStorageConfiguration.setWalCompactionLevel() 方法设置压缩级别。

WAL页快照压缩

怎么开动这个

我们已经发现,在WAL中记录分为逻辑记录和物理记录。 对于每个页面的每次更改,都会在页面内存中生成一条物理 WAL 记录。 物理记录又分为 2 个子类型:页面快照记录和增量记录。 每次我们更改页面上的某些内容并将其从干净状态转移到脏状态时,该页面的完整副本都会存储在 WAL(页面快照记录)中。 即使我们只改变 WAL 中的一个字节,记录也会比页面大小稍大。 如果我们在已经脏的页面上更改某些内容,则会在 WAL 中形成一条增量记录,该记录仅反映与该页面之前状态相比的变化,而不是整个页面。 由于将页面状态从脏重置为干净是在检查点过程中执行的,因此在检查点开始后,几乎所有物理记录将仅由页面快照组成(因为在检查点开始后立即所有页面都是干净的) ,然后当我们接近下一个检查点时,增量记录分数开始增长并在下一个检查点开始时再次重置。 一些综合测试的测量表明,页面快照占物理记录总量的比例达到90%。

WAL页面快照压缩的思想是使用现成的页面压缩工具来压缩页面快照(参见磁盘页面压缩)。 同时,在WAL中,记录以append-only模式顺序保存,不需要将记录绑定到文件系统块的边界,所以这里与磁盘页压缩机制不同,我们不需要稀疏文件所有;因此,该机制不仅适用于Linux操作系统。 此外,我们能够将页面压缩多少对我们来说不再重要。 即使我们释放了 1 个字节,这也已经是一个肯定的结果,我们可以将压缩数据保存在 WAL 中,这与磁盘页面压缩不同,在磁盘页面压缩中,仅当我们释放了超过 1 个文件系统块时才保存压缩页面。

页面是高度可压缩的数据,它们在总 WAL 体积中所占的份额非常高,因此在不更改 WAL 文件格式的情况下,我们可以显着减小其大小。 压缩(包括逻辑记录)将需要更改格式并丧失兼容性,例如,对于可能对逻辑记录感兴趣的外部消费者,但不会导致文件大小显着减小。

与磁盘页面压缩一样,WAL页面快照压缩可以使用ZSTD、LZ4、Snappy压缩算法以及SKIP_GARBAGE模式。

性能影响

不难注意到,直接启用WAL页快照压缩只会影响将数据写入页内存的线程,即那些更改缓存中数据的线程。 从 WAL 读取物理记录仅发生一次,即节点在跌落后升起时(并且仅当节点在检查点期间跌落时)。

这会通过以下方式影响更改数据的线程:由于每次写入磁盘之前需要压缩页面,我们会产生负面影响(CPU),而由于减少了数据量,我们会产生积极影响(磁盘IO)写入的数据。 因此,这里一切都很简单:如果系统性能受到 CPU 的限制,我们的性能会略有下降,如果系统性能受到磁盘 I/O 的限制,我们的性能会有所提高。

间接地,减少 WAL 大小也会(积极地)影响将 WAL 段转储到存档和 WAL 压缩流中的流。

在我们的环境中使用合成数据进行的实际性能测试显示略有增加(吞吐量增加了 10%-15%,延迟减少了 10%-15%)。

如何启用和配置

最低 Apache Ignite 版本:2.8。 启用并配置如下:

  • 类路径中必须有一个 ignite-compression 模块。 默认情况下,它位于 Apache Ignite 发行版的 libs/Optional 目录中,并且不包含在类路径中。 您只需将目录向上移动一级到 libs,然后当您通过 ignite.sh 运行它时,它将自动启用。
  • 必须启用持久性(通过启用 DataRegionConfiguration.setPersistenceEnabled(true)).
  • 压缩模式必须使用以下方法设置 DataStorageConfiguration.setWalPageCompression(),默认情况下禁用压缩(DISABLED 模式)。
  • 或者,您可以使用以下方法设置压缩级别 DataStorageConfiguration.setWalPageCompression(),请参阅 javadoc 了解每种模式的有效值的方法。

结论

Apache Ignite 中考虑的数据压缩机制可以彼此独立使用,但它们的任意组合也是可以接受的。 了解它们的工作原理将使您能够确定它们对您环境中的任务的适合程度以及使用它们时您必须牺牲什么。 磁盘页压缩旨在压缩主存储,可以提供中等压缩比。 WAL 页面快照压缩将为 WAL 文件提供平均程度的压缩,并且很可能甚至会提高性能。 WAL 压缩不会对性能产生积极影响,但会通过删除物理记录来尽可能减小 WAL 文件的大小。

来源: habr.com

添加评论