摘要
GaussDB及其开源版本openGauss是华为的关系型数据库管理系统(RDBMS),具有一个主要的基于磁盘的存储引擎。 本文介绍了一种新的GaussDB 存储引擎,该引擎针对主内存和多核进行了优化。 我们从一个搜索原型开始,该原型利用了硬件的强大功能,但对客户没有用处。 本文描述了将该原型转化为工业存储引擎的细节,包括与 GaussDB 的集成。 标准基准测试表明,新引擎在英特尔x86多核服务器以及基于ARM64的昆鹏CPU的华为泰山服务器上的全TPC-C性能比GaussDB提高了2.5倍以上。
1.介绍
GaussDB[13]于2019年作为分布式关系数据库管理系统发布,随后于 2020 年 7 月开源发布 openGauss [16] 作为闭源 GaussDB 的社区版本。它是由华为数据库集团开发的OLAP和OLTP混合的企业数据库系统。从2012年开始,前5年的研究和开发集中于大规模并行OLAP数据库系统。自2015年推出FusionInsight MPPDB、LibrA[3]和GaussDB 200品牌以来,PB级OLAP平台已被全球许多客户采用,包括一些世界上最大的中国金融机构,到2016年已达到Gartner 魔力象限。
2017年GaussDB OLTP在顶级客户对下一代OLTP数据库系统的需求的推动下开始发展起来。 五个关键要求定义如下:(1)采用无共享体系结构的事务性向外扩展,(2)在多插槽多核服务器上的高效扩展,(3)有利于大内存的高性能;(4)高SQL覆盖率和互操作性;(5)高可用性。
在上述驱动下,受新的内存优化数据库(如Microsoft Hekaton[6]、MemSQL[31]、SAP HANA[20]和相关领域的作品(如[36,37,38])的影响,我们正式确定了一个关键研究方向:开发一个高性能内存优化存储引擎,可插入SDB信封,得益于其SQL和高可用性(HA)功能。在下文中,我们对GaussDB的引用仅涉及GaussDB OLTP功能。
GaussDB 最初改编自 Postgres 9.2[26]。它的一个主要特性是基于线程的并行性,该特性已经从OLAP MPPDB继承而来。此功能提供了显著的系统性能。 但是,传统的同步和基于页面的数据和索引保持不变。
我们的目标是消除基于磁盘(例如页面感知)和基于进程(例如信号量管理)数据库的开销和复杂性,并利用最先进的并发数据结构和内存事务管理的当代研究来改进 GaussDB 性能,同时保持简单的 ACID 语义和完整的 SQL功能。
本文介绍了一种存储引擎,它是为在线事务处理(OLTP)利用更多的主内存和处理内核而创建的。这个新的存储引擎无缝集成在 GaussDB 中。我们面临的挑战是:(1)调整最先进的内存并发控制研究(通常以最小PoC原型的形式体现)以适应工业工作负载。(2) 将其集成到功能齐全的SQL引擎中。
在后面的内容中,我们使用术语内存优化表 (MOT) 来表示新的存储引擎组件,以及该引擎提供的新特性和功能。 MOT 是开源 openGauss 服务器代码 [15] 和文档 [11] 的一部分。
1.1 系统结构
GaussDB设计用于线性扩展到数百台物理机器。 图 1 说明了高级逻辑系统架构。 数据库数据被分区并存储在许多满足本地 ACID 属性的数据节点中。 跨分区一致性通过使用两个阶段提交和全局事务管理。
如果表是作为 MOT 创建的,SQL 查询处理器会将对该表的所有访问定向到 MOT 存储引擎,然后该表会将其所有日志记录重定向回数据节点信封。 在恢复过程中,日志记录由MOT表取回,MOT表将重放日志记录。第3节解释了MOT是如何并入没有模块化存储引擎的GaussDB。通过这种方式,事务可以同时使用MOT和基于磁盘的表,这与其他内存内实现不同,例如[6,35],它取代了SQL引擎,使重用分布式事务框架和执行混合在内存和基于磁盘的表中的事务变得复杂。
MySQL 有一个定义良好的 API 用于插入新的存储引擎。 然而,它的日志机制在存储引擎之外,因此它的可插拔存储引擎只执行 ACI 事务,没有持久性。
1.2 内部结构
我们的目标之一就是建立一个能够出色的使用当前和未来的多核CPU体系结构的系统。特别是,在内核数量线性增大的情况下。根据我们的经验实验,成熟的无锁Masstree实现和我们对silo的稳健改进相结合,给我们提供了这方面的支持。
对于索引,我们比较了最先进的解决方案,例如[21,25],然后选择了Masstree,因为它在点查询、迭代和修改方面显示了良好的性能。Masstree是Trie和B+树的组合,用于谨慎利用缓存、预取、乐观导航和细粒度锁定。它针对高争用性进行了优化,并在其前身(如OLFIT[4])的基础上添加了各种优化。然而Masstree索引的缺点是内存消耗更高。虽然行数据消耗相同的内存大小,但每个索引(主索引或次索引)的每行内存平均高出16个字节:在基于磁盘的表中使用的基于锁的B树中为29个字节,而在MOT的Masstree中为45个字节。
下一步是选择并发控制算法。为了从内存架构中获得优势,我们希望最大限度地提高OLTP事务的速度。尽管最近有关于高效内存MVCC[23]的建议,但我们选择避免快速垃圾收集,只维护实际数据。MOT的另一个设计选择不是像HStore那样对数据进行分区,因为在现实工作负载中,事务会跨越分区边界,并且性能会迅速下降[36]。一些新的设计使用静态和动态分析来调节并行性[29],但这种方法会引入高延迟和不切实际的限制。
我们拆分了单一版本,共享了该类别下我们为MOT选择的并发控制算法的所有内容,划分成以下几个子类别:
- 乐观并发控制(OCC):OCC算法,例如silo[36]和TicToc[38]具有三个阶段:事务从共享内存读取记录,并对记录的本地私有副本执行所有写入操作(读取阶段)。然后事务会执行一系列检查以确保一致性(验证阶段)。验证成功后,OCC系统通过更改可供其他事务使用这一性质来提交事务(写入阶段)。如果验证失败,则事务将中止且不写入任何内容。如果两个OCC事务同时执行,它们不会互相等待。
- 遭遇时间锁定(ETL):在ETL中,读者是乐观的,但写者可以锁定他们访问的数据。因此,来自不同ETL传输操作的编写器可以看到彼此,并可以决定中止。[9]中的经验证明,ETL通过两种方式改善OCC的性能。首先,它们会尽早检测冲突,通常情况下还会提高事务吞吐量,因为事务不会执行无用的工作,因为在提交时发现的冲突通常不能在至多中止一个事务的情况下得到解决。其次,遭遇时间锁定允许我们高效地处理写后读取(RAW),而不需要额外或复杂的机制。
- 悲观并发控制(2PL):在访问时为读或写锁定某一行,并在提交时释放锁。这些算法需要一些死锁避免方案。死锁可以通过计算等待图中的周期来检测,或者通过在TSO[2]中保持时间顺序或通过某种退避方案来避免。在2PL算法中,如果一个事务正在写入一行,则其他事务无法访问该行;如果正在读取一行,则不允许任何事务写入该行。
如[37,1]所示,OCC是大多数工作负载的最快选项。一个原因是,当每个内核执行多个线程时,一个锁很可能被一个交换线程持有,尤其是在交互模式下。此外,悲观算法涉及死锁检测,这会引入开销,并且通常使用读写锁效率低于标准自旋锁。我们选择了silo[36],因为它比其他现有选项更简单,例如TicToc[38],同时为大多数工作负载保持相同的性能。ETL有时比OCC快,但它会引入虚假中止,这会使用户感到困惑,而OCC仅在提交时中止。
目前,与行业[27]和现有技术[39]中其他领先的主存DBMS类似,MOT表数据容量要么限制在最大可用内存内,要么通过操作系统的内存页交换来超过最大可用内存,尽管这种情况下的性能可能会降低。最近提出了几个方向,通过数据重组[34]、反缓存[5]和分层[33]来缓解这一问题。这是一个重要的课题,也是MOT未来的工作。
贡献:首先我们提出并推动针对工业工作负载的MOT调整。这些调整包括对具有多个索引表的乐观插入和删除、存在DDL SQL命令(如DROP和TRUNCATE表)时的垃圾收集等。这些增强并不会增强性能,但是,我们通过微基准测试验证它们不会降低原始OCC算法的性能。其次,我们描述了MOT是如何集成到GaussDB中并展示其对GaussDB性能的影响。在MOT与GaussDB并排集成基于磁盘的存储引擎之后,SQL处理引擎成为下一个性能瓶颈。为了克服这个问题,我们添加了一个专门的JIT编译机制。
本文的其余部分安排如下。 第 2 节介绍了我们为 MOT 研究原型添加的增强功能,以使其适合工业工作负载。 第 3 节解释了我们如何将 MOT 与 GaussDB 集成,以及 MOT 如何重用 GaussDB 服务来实现持久性、恢复、检查点和 SQL 查询处理。 我们在第 4 节中展示了 GaussDB 与 MOT 的性能。最后,我们在第 5 节中调查了相关工作并在第 6 节中进行总结。