ZEAP:重塑PostgreSQL存储

2020-10-08 06:18:03

在PostgreSQL中,自从最初的MVCC模型被构思以来,表膨胀一直是一个主要问题。因此,我们决定撰写一系列博客文章,更详细地讨论这个问题。首先,什么是餐桌膨胀?表膨胀意味着即使存储在数据库中的数据量根本没有增长,表和/或索引的大小也在增长。如果想要支持事务,绝对有必要在数据被修改的情况下不覆盖数据,因为您必须记住,人们可能想要在修改或回滚事务时读取旧行。

因此,在PostgreSQL中,膨胀是与MVCC相关的内在问题。然而,PostgreSQL存储数据和处理事务的方式并不是数据库处理事务和并发的唯一方式。让我们看看还有哪些其他选择:

在MS SQL中,您会发现一个名为tempdb的东西,而Oracle和MySQL会将旧版本放入重做日志中。您可能知道,PostgreSQL在更新时复制行并将它们存储在同一个表中。Firebird还内联存储旧的行版本。

去掉行肯定是个问题。在PostgreSQL中,删除旧行通常是通过真空完成的。然而,在某些情况下,真空度跟不上,或者由于某些其他原因(通常是长事务),空间正在增长。我们在CYBERTEC已经就这种情况发表了大量的博客。

“没有不需要权衡的解决方案”也是存储的一个重要方面。没有完美的存储引擎这回事-只有能够很好地服务于特定工作负载的存储引擎。PostgreSQL也是如此:当前的表格式非常适合许多工作负载。然而,也有一个阴暗面将我们带回了起点:桌子膨胀。如果您运行的是更新密集型工作负载,通常会发生表的大小难以控制的情况。如果开发人员和系统管理员从一开始就没有完全了解PostgreSQL的内部工作原理,情况尤其如此。

Zheap是一种通过实现能够更高效地运行更新密集型工作负载的存储引擎来控制表膨胀的方法。这个项目最初是由EnterpriseDB启动的,已经投入了大量的精力。

为了使zheap为生产做好准备,我们很自豪地宣布,我们在Heroic Labs的合作伙伴已经承诺为zheap的进一步开发提供资金,并将所有代码发布给社区。CYBERTEC已经决定将资金增加一倍,并提供额外的专业知识和人力来推动ZHEAP的发展。如果有个人、公司等也有兴趣帮助我们推动zheap向前发展,我们渴望与每个愿意让这项伟大的技术取得成功的人合作。

Zheap是一个全新的存储引擎,因此深入研究基本架构是有意义的。三个基本组件必须协同工作:

让我们先看一下zheap页面的布局。正如您所知道的,PostgreSQL通常将表视为8k块的序列,因此页面的布局绝对重要:

乍一看,这个图像看起来几乎像一个标准的PostgreSQL8k页面,但实际上并非如此。您可能会注意到的第一件事是,元组以与页面开头的项目条目相同的顺序存储,以实现更快的扫描速度。我们在这里看到的下一件事是在页面末尾出现“Slot”条目。在标准PostgreSQL表中,可见性信息作为行的一部分存储,这需要大量空间。在zheap中,事务信息已移动到页面,这显着减少了数据大小(进而转化为更好的性能)。事务占用16字节的存储,并包含以下信息:事务ID、纪元和该事务的最新撤消记录指针。行指向事务槽。表中事务槽的默认数量是4个,这对于大表来说通常是可以的。但是,有时需要更多的事务插槽。在本例中,zheap有一个称为“TPD”的东西,它只不过是一个溢出区,用于根据需要存储额外的事务信息。

有时,单个页面需要多个事务处理槽。TPD提供了一种灵活的方式来处理这一问题。问题是:zheap将TPD数据存储在哪里?答案是:这些特殊页与标准数据页交错。它们只是以一种特殊的方式标记,以确保顺序扫描不会接触到它们。为了跟踪这些特殊用途的页面,zheap使用元页面来跟踪它们:

TDP只是一种使事务槽更具可伸缩性的方法。在块中设置一些槽可以减少过度触摸页面的需要。如果需要更多,TPD是一个优雅的出路。在某种程度上,这是两全其美的。

难题的下一个重要部分是单个元组的布局:在PostgreSQL中,标准堆元组有一个超过20个字节的头,因为所有事务信息都存储在一个元组中。在这种情况下就不是这样了。所有事务信息都已移动到页级结构(事务槽)。这一点非常重要:因此报头仅减少到5个字节。但是这里还进行了更多的优化:标准元组必须在元组标题和行中的实际数据之间使用CPU对齐(填充)。这可能会为表中的每一行烧毁一些字节。Zheap没有这样做,导致存储更加紧凑。通过从按值传递的数据类型中删除填充,可以节省额外的空间。所有这些优化意味着我们可以在表的每一行中节省宝贵的空间。URSE位置开始结束时间语言