opengauss 《Industrial-Strength OLTP Using Main Memory and Many Cores》

该论文详细介绍了opengauss内存库的工程实现,原理请参考《Speedy Transactions in Multicore In-Memory Databases》 https://ata.alibaba-inc.com/articles/216581

ABSTRACT

GaussDB和开源版本的openGauss是TP型数据库,本文展示了其面向main memory和多核的优化,在TaiShan服务器上TPCC有2.5倍的提升。

1. INTRODUCTION

2012年研发OLAP,2015年发布,FusionInsight MPPDB,GaussDB200

2017年研发下一代OLTP,为了满足来自高端用户的5个需求:

  1. 基于share-nothing架构的scale-out能力;
  2. 多核的scale-up能力;
  3. 大内存;
  4. SQL兼容;
  5. 高可用;

策略是基于GaussDB研发一款内存数据库,可以服用GaussDB的SQL兼容和HA能力。

基于PostgreSQL9.2,继承了MPPDB的线程模型。

本文要讨论GaussDB的内存数据库:Memory-Optimized Tables,下文简称为mot。

1.1 System Architecture

GaussDB的整体架构如下图所示,典型的sharenothing系统:单机DB+2PC和全局事务管理。mot做为单机的存储引擎,因此可以完整复用已有的SQL,执行器,分布式事务,HA等。

mot的内存管理:感知NUMA的内管管理,2MB chunk

opengauss 《Industrial-Strength OLTP Using Main Memory and Many Cores》

1.2 Internal Architecture

mot底层使用无锁的masstree,并发控制协议使用Silo,并对进行了工程上的增强。Silo的论文参考《Speedy Transactions in Multicore In-Memory Databases》

mot在使用masstree时key的长度从29字节增加到了45字节。

mot并发控制协议的选择考虑如下因素:

  1. OCC:Silo和TicToc分3个阶段。并发事务无需等待,仅仅在validation阶段做检测,适当的abort;
  2. Encounter time locking (ETL):乐观读,写者上锁。因此,并发的写者能互相感知并做abort。ETC和相比OCC:一方面能提前检测冲突,减少无用功;另一方面能加速Read-after-writes;
  3. Pessimistic concurrency control (2PL):

结论:OCC更加适合多核(参考《Staring into the abyss: An evaluation of concurrency control with one thousand cores》)。mot选择了Silo的算法,相比于TicToc,Silo算法比较简洁而且大部分负载情况下性能表现差不多。

mot表大小受限于内容大小,当然可以进一步优化来提升表大小,比如:anti-caching和tiering。

2. ADJUSTING THE ENGINE

2.1 Indexing and Storage

引入类似HOT机制,在更新一个行时不用每次都更新索引树,而是跟新这一行的sentinels(类似BTree索引中的IndexTuple)。

opengauss 《Industrial-Strength OLTP Using Main Memory and Many Cores》

2.1.1 Optimistic inserts优化

在Silo基础上,mot完善了基于OCC的insert优化。

2.1.1.1 Motivation.

OCC的3个阶段中,insert先在本地操作仅仅在commit时检测冲突。因此需要优化:

  1. 并发事务insert相同的key,如何保证唯一性;
  2. insert的事务最终abort;
  3. 同一个事务插入相同的key;
  4. 如何insert多个索引树;

2.1.1.2 Implementation Details.

Silo处理插入key为K流程:

  1. 如果K已经对应一个已提交record,本事务abort;
  2. 否则,生成record为R,状态为ABSENT,索引树中插入K->R;
  3. 如果发现已存在ABSENT的record,则直接复用(先成功插入者为准);

mot对Silo插入流程的改进:

  1. DoubleRow:同一行插入到在不同索引树上,导致部分索引树冲突;
  2. Self Inserted:事务需要识别索引树上哪些ABSENT状态的record是自己插入的;

opengauss 《Industrial-Strength OLTP Using Main Memory and Many Cores》

2.1.2 Non-unique indexes

在key之后增加后缀:8个字节的row指针。

2.2 Concurrency Control

read after write hazard:occ实现的事务中,如何读取本事务刚刚写入的数据。

mot的优化:实现了一个access-set结构,记录本事务read,update,insert,deleted集合。

维护每个key的状态的状态转换。提交时从access-set上提取出write-set和insert-set。

和Silo不同的是,mot支持了RC隔离级别:不对read-set做校验。

read-set:如果高于RC级别,对read-set做冲突检测;

write-set:对row上锁;

insert-set:对sentinel上锁;

opengauss 《Industrial-Strength OLTP Using Main Memory and Many Cores》

3. INTEGRATION WITH GAUSSDB

由于GaussDB是基于早起PostgreSQL做的开发,并没有storage engine的概念,因此mot是通过FDW机制来实现。同时,也需要对原有FDW做相应的扩展,比如:在create table/index时在本地触发对mot的DDL操作。

opengauss 《Industrial-Strength OLTP Using Main Memory and Many Cores》

3.1 Tables and Indexes Created and Used

创建FDW函数:ValidateTableDef

fdwroutine->AddForeignUpdateTargets = MOTAddForeignUpdateTargets;
    fdwroutine->GetForeignRelSize = MOTGetForeignRelSize;
    fdwroutine->GetForeignPaths = MOTGetForeignPaths;
    fdwroutine->GetForeignPlan = MOTGetForeignPlan;
    fdwroutine->PlanForeignModify = MOTPlanForeignModify;
    fdwroutine->ExplainForeignScan = MOTExplainForeignScan;
    fdwroutine->BeginForeignScan = MOTBeginForeignScan;
    fdwroutine->IterateForeignScan = MOTIterateForeignScan;
    fdwroutine->ReScanForeignScan = MOTReScanForeignScan;
    fdwroutine->EndForeignScan = MOTEndForeignScan;
    fdwroutine->AnalyzeForeignTable = MOTAnalyzeForeignTable;
    fdwroutine->AcquireSampleRows = MOTAcquireSampleRowsFunc;
    fdwroutine->ValidateTableDef = MOTValidateTableDef;
    fdwroutine->PartitionTblProcess = NULL;
    fdwroutine->BuildRuntimePredicate = NULL;
    fdwroutine->BeginForeignModify = MOTBeginForeignModify;
    fdwroutine->ExecForeignInsert = MOTExecForeignInsert;
    fdwroutine->ExecForeignUpdate = MOTExecForeignUpdate;
    fdwroutine->ExecForeignDelete = MOTExecForeignDelete;
    fdwroutine->EndForeignModify = MOTEndForeignModify;
    fdwroutine->IsForeignRelUpdatable = MOTIsForeignRelationUpdatable;
    fdwroutine->GetFdwType = MOTGetFdwType;
    fdwroutine->TruncateForeignTable = MOTTruncateForeignTable;
    fdwroutine->VacuumForeignTable = MOTVacuumForeignTable;
    fdwroutine->GetForeignRelationMemSize = MOTGetForeignRelationMemSize;
    fdwroutine->GetForeignMemSize = MOTGetForeignMemSize;
    fdwroutine->GetForeignSessionMemSize = MOTGetForeignSessionMemSize;
    fdwroutine->NotifyForeignConfigChange = MOTNotifyForeignConfigChange;

3.1.1 Index Usage for Planning and Execution

优化器中新增GetForeignPaths。

3.2 Integrating MOT with HA

  1. Logging:复用GaussDB的XLOG来记录WAL日志,GaussDB并行写日志,多个并发backend进程插入WAL slot,在commit时接管WAL并flush;
  2. Checkpointing:提供checkpoint的callback函数;
  3. Recovery:提供recover的callback函数,并行recover,每个线程负责一个datasegment;

3.3 VACUUM and DROP

3.3.1 Deleting Pools

基于epoch的gc,新分配内存区域,将live的数据拷贝到新内存区域,老的内存区域直接回收掉。

3.3.2 GC and Pools Deletion

3.4 JIT for Query Acceleration

GaussDB已经支持了JIT:

  1. 表达式,比如WHERE子句;
  2. Inline函数;

上述两种JIT优化是通用的,但近支持了SQL的部分JIT优化,对加速CPU-bound查询效果显著,比如OLAP。

而mot选择对特定OLTP查询场景进行优化,一旦进入这种场景就是全JIT优化,面向OLTP常用负载:点查、简单的范围查询。

GaussDB的JIT仍然是火山模型的pull-model,mot对特性查询的sql进行了全JIT支持,因此一旦命中就不需要使用火山模型。

mot并不支持复杂agg函数,比如:groupby,count distinct

4. EXPERIMENTAL RESULTS

4.1 Hardware

环境1:

Huawei ARM64:
TaiShan
Kunpeng920-4826和6426
1TB内存
4TB NVMe 3.5GB/s
– 2-sockets of 48 cores, adding to 96 cores. 
– 2-sockets of 64 cores, adding to 128 cores. 
– 4-sockets of 64 cores, adding to 256 cores.

环境2:

Intel x86:
2socket,每个socket上18物理核,2个超线程,总共72核

4.2 Benchmark and Configuration

client端和server之间10Gb网络

所有的测试中,原GaussDB的存储引擎的数据全部能在内存中buffer住,因此测试结果可以忽略IO的影响。

4.3 Results

使用TPCC测试性能和正确性。

opengauss 《Industrial-Strength OLTP Using Main Memory and Many Cores》

结论:ARM单核性能是x86的0.75倍,和主频成比例,96个ARM核和72个x86核性能相当。

opengauss 《Industrial-Strength OLTP Using Main Memory and Many Cores》

上图测试扩展性,结论:

  1. mot随着并发增加吞吐增加,大概250并发达到峰值;
  2. 而GaussDB原存储在并发100时达到峰值;

单条query的加速比

构建表且仅有2个整数,其中一个整数做为primary key。

opengauss 《Industrial-Strength OLTP Using Main Memory and Many Cores》

结论:

  1. insert提升2倍多:原Disk-based的插入需要在page内定位行指针,插入B-Tree也需要定位node,而mot使用masstree;
  2. lookup提升2倍多:原因同上;
  3. delete提升1.5到2倍:mot执行了物理删除,而原Disk-based仅仅是标记,后续vacuum负担会更大;
  4. update提升3.5倍多:masstree查找更快。
  5. 所有测试中ARM平台提升比x86更加明显:ARM的relaxed memory对原Disk-based的2PL并发控制不优化,而MOT OCC在这种优化更明显;

JIT加速

opengauss 《Industrial-Strength OLTP Using Main Memory and Many Cores》

结论:

  1. stocklevel无提升:包含mot不支持的复杂agg,同时sql中包含pipeline breaker需要物化;
  2. 其他场景均提升30%;
  3. 在并发300个连接时提升更高,OCC允许reader在本地并发执行,减少cache miss;

存在abort时

opengauss 《Industrial-Strength OLTP Using Main Memory and Many Cores》

OCC中事务执行期间不会被阻塞,但是当有竞争时需要abort。

上图中,12个warehouse,120个并发来模拟高比例的竞争场景。

结论:

  1. 随着并发增加,abort比例上升;
  2. mot整体性能仍然有2倍提升;

Replication对性能的影响

opengauss 《Industrial-Strength OLTP Using Main Memory and Many Cores》

开启replication时,结论:

  1. mot有7%损耗;
  2. Disk-based有20%损耗;

5. RELATED WORK

  1. Prototypes and Frameworks: DBX提供了对各种并发控制协议算法进行比较的框架:2PL,OCC,Silo。经过测试在华为的机器上Silo性能表现最好;
  2. Microsoft’s Hekaton: SQL的Hekaton是内存引擎,也是在commit是进行alidation,类似Silo。不同点是它使用Bw-tree作为索引,read-lock会阻塞写,虽然能减少abort,但是也降低了并发度,因为需要原子的对共享内存区域进行++来实现原子锁,虽然都是乐观,Silo更加乐观;
  3. PostgreSQL Storage Engine:也是用FDW来实现内存引擎,功能不完善,缺少二级索引,DDL等;
  4. Mot:兼容SQL,工业级内存引擎;

6. CONCLUSION

MOT是为众核和大内存设计的内存引擎,内存管理,并发控制协议,GC都进行了工程上的优化,通过FDW嵌入到GaussDB中,无缝兼容SQL引擎,方便用户使用。

上一篇:PostgreSQL 创建B-Tree索引的过程


下一篇:《Speedy Transactions in Multicore In-Memory Databases》