存储的未来
对于某些用例,当前存储设计是次优的。我们相信可以通过在”heap”操作和存储之间添加一个抽象层来进行改进。当前,存储设计基于按行组织页的假设:heapam.h假设:每个tuple只有一个元组头和一个数据区域,即包括HeapTuple及tuple逻辑操作的代码,比如delete、update、加锁。类似,执行器代码表示TupleTableSlot抽象层的元组,该抽象层下面是HeapTuple。2015年2ndQuadrant致力于在PG中实施列式存储项目,以下是根据实施过程中吸取的经验得出的计划。
项目大纲
1) 垂直分区
2) 执行器批处理
3) 执行器向量化
4) 列索引
5) 表的可拔插存储
6) 列式存储插件
当将向量化执行引擎集成到列式存储中时,才能获得最高性能。列式存储不用向量化当然也可以,但是获得的收益却不是最大。因为CPU仍然是一次仅操作一个元素。也可以不在列式存储上做向量化,但收益也很小,因为要使向量化,必须将基于行的数据转换成基于列的数据,这是一个缓慢的操作。
垂直分区
将表的存储区域拆分为多个部分的能力,将列的子集放入每个存储区域。这有几点:
1) 跳过读取查询中不使用的列存储区域
2) 不同列使用不同存储策略(基于行或基于列;基于列的不同实现:实验、压缩或非压缩等)
3) 在具有多个存储区域的元组上读取元组,用于他们之间的join
挑战:
1) 表和存储区域之间进行join需要单独处理
2) Join消除是关键
3) 逻辑/物理元组表示需要改变(尤其是单个atrrelid值的pg_attribute不再表示一个表的元组描述符)
批量执行
指执行器在单个节点一次处理多个元组的能力,而不是当前一次仅处理一个。需要大改TupleTableSlot结构以及节点执行流程。这适用于9.7.
向量化执行
执行器在CPU级别使用SIMD指令用于函数操作的能力。这基于执行器批量执行。聚合操作需要提供专用代码。
列式索引
这个项目关于列存储的新索引访问方法。一个明显的输出是深入了解哪种列存储方法最有效。好处:索引比标准索引更加紧凑,因此扫描速度更快。
表的可拔插存储
这个项目关于为表存储创建一个类似访问方法的接口。目前,所有存储都通过heapam.c。这使编写不同实现成为可能。PG12开始已支持表访问方法的可拔插。Heapam.c接口假定用于有一个表和一个TID。目前TID只是关系中元组的物理位置。该项目可能需要更高元组标识符以适应不同的存储实现。同时,当前heapam.c实现返回一个包含元组的HeapTuple结构,但不同的实现可能有完全不同的方式来表示存储中的元组。因为我们希望利用元组的不同表示而不是heapify他们。所以可能需要进行更多修改,以便可以将元组传递给执行程序代码。这如何工作,还不清楚,需要更多研究。执行器批处理可以依靠他一次对多个元组进行操作。
Tom Lane的警示
我们需要避免DDL代码的重写。目前所有utility代码都假设HeapTuples可传递到任何地方。对于不同存储格式,这种假设就会失效。我们需要一些方法来避免这个项目陷入无休止的utility代码重构中。
解决方案似乎很简单:不需要在system catalog中立即解决这个问题,如果我们禁止对system catalog使用不同存储格式,我们就不需要边界大量utility代码。
将来有人可以重构涉及单个catalog的代码,以允许将可拔插(非堆)存储用于该catalog。这可以零碎地完成,取消对一个特定catalog的限定。
列存的插件
面向列存储的可拔插存储引擎。
现有用例分析
上面介绍的是PostgreSQL的,分析其他数据库也很有用。
MySQL/MariaDB
MySQL和MariaDB提供可拔插存储引擎,请参考其手册。
存储引擎 |
描述 |
我们在PG中需要这样类似的东西吗? |
InnoDB |
提供索引组织表,行的老版本在undo表空间。二级索引是间接的,具有单独版本 |
是的。索引组织表、undo表空间、二级索引是有用的特性。看来,至少其中一些可以使用可拔插存储API来实现 |
MyISAM |
没有事务和恢复的表引擎 |
不用。如果需要,我们有unlogged表 |
CSV Federated Cassandra CONNECT SphinxSE |
这些表引擎要么访问本地数据,要么访问外部数据源 |
不需要。我们有FDWs |
MGR_MyIsam Merge |
这些引擎允许定义作为主表集联合的表。这种联合是可更新的:更新被推送到主表。 |
不需要。我们有可更新视图、分区、表继承等 |
Archive |
归档数据的存储:追加、可压缩存储 |
是的。归档存储非常有用 |
Blackhole |
静默“吃掉”所有插入数据的表引擎。它在代理中有有用的用例,通过在主端过滤复制流量来减少部分复制中的网络流量。 |
不需要。我们的逻辑解码已经具备这样能力。此外,还具有相同功能的Blackhole FDW |
Mroonga |
Mroonga 使用 FTS、地理空间和其他索引实现非事务性表。 |
不。PG中新的索引类型可以通过索引访问方法来实现 |
OQGraph |
允许查询和对图进行索引的表引擎 。允许用户查询某种视图,而注意数据存储在另一个表中 |
不。这个表引擎不打算存储主要数据。如果我们需要类似的解决方案,我们应该使用索引访问方法、视图、物化视图来实现它 |
Sequence |
提供generate_series()函数的类似功能 |
不用。generate_series()完全可以满足这个目的 |
Aria XtraDB |
MariaDB的MyISAM和InnoDB增强版本 |
不用。我们应该尽可能避免碎片化 |
TokuDB RocksDB |
这些引擎提供写优化表。TokuDB 实现了fractal树,而RocksDB实现了LSM树。 |
是的。写优化表很不错。 |
ScaleDB Spider |
提供内置在表引擎中的集群 |
不用。可拔插存储似乎不适合集群的机制 |
Memory |
内存表引擎实现了完全驻留在内存中的非持久表。除了MySQL 实现的缺点之外,内存引擎可以给我们带来以下好处:更快的内存操作绕过缓冲区管理器;优化磁盘IO相关操作 |
是的。如果内存表执行的更快,那就太好了。有人可能会反对PG不应该使用内存存储,但若这种存储引擎能够带来显著性能优势,用户就会对它感兴趣 |
MongoDB
mongoDB也提供可拔插存储,参考其手册。