前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >TDSQL-C PostgreSQL(CynosDB) 内核实现剖析一

TDSQL-C PostgreSQL(CynosDB) 内核实现剖析一

原创
作者头像
腾讯云数据库 TencentDB
发布2021-09-17 10:50:19
9550
发布2021-09-17 10:50:19
举报

| 导语 TDSQL-C PostgreSQL(CynosDB)是腾讯云数据库团队自研的新一代云原生数据库,融合了传统数据库、云计算与新硬件技术的优势,采用计算和存储分离的架构,100%兼容 PostgreSQL,高度兼容Oracle语法,提供具备极致弹性、高性能、海量存储、安全可靠的数据库服务。本文旨在从数据库内核的角度揭秘TDSQL-C PostgreSQL计算层的技术内幕。本文适合读者:腾讯云售后服务,TDSQL-C用户,TDSQL-C开发者,需要有基本的数据库与存储知识。

一、概述

      TDSQL-C采用计算和存储分离的架构,所有计算节点共享一份数据,存储容量高达128TB,单库最高可扩展至16节点,提供秒级的配置升降级、秒级的故障恢复和数据备份容灾服务。TDSQL-C既融合了商业数据库稳定可靠、高性能、可扩展的特征,又具有开源云数据库简单开放、自我迭代的优势。《TDSQL-C PostgreSQL(CynosDB) 内核解密》文章已总体介绍了TDSQL-C核心架构与关键技术,本文下面将介绍TDSQL-C 计算层内核相关实现细节。

二、CynosPG 内核实现

      数据库引擎CynosPG基于PostgreSQL而来,PostgreSQL是世界上功能最强大最先进的开源数据库。经过长达30年以上的积极开发和不断演进,PostgreSQL已在可靠性、稳定性、数据一致性等获得了业内极高的声誉。CynosPG主要对PostgreSQL日志系统存储系统进行改造,进行高度Oracle语法兼容,以及深度的内核性能及功能优化,对于PostgreSQL SQL引擎架构改动有限,因此CynosPG可以完全兼容PostgreSQL原生的功能。

      计算引擎CynosPG内核总体模块如上图所示,CynosPG内核涉及修改和优化的模块和机制。灰色部分是PostgreSQL内核原生模块,相对改动较少;有色部分表示在CynosPG内核中新增和修改的模块。CynosPG内核实现无状态,不必要的IO全部卸载计算层本地数据文件将不复存在,仍然包含传统数据库内核的大部分组件:查询处理器、事务管理、锁、缓存实现以及MVCC多版本,移除了PostgreSQL中的FPW特性,脏页面刷盘操作

      SQL引擎:是CynosPG的SQL引擎,包括词法/语法分析,语义分析,查询重写/优化,查询执行。由于不涉及SQL层整体架构的改动,因此CynosPG完全兼容PostgreSQL的SQL语法和语义,同时对Oracle语法进行了高度兼容。

      Access:是CynosPG数据的访问和组织方法。其中包括:

1) heap:表数据访问和元组组织方式,包括:扫描,更新,插入,删除等; 2) btree/gin/gist/spgist/hash/brin:索引访问和实现方式,包括各种索引的实现和扫描; 3) CLOG/MultiXACT:与事务提交和元组级别的并发相关。

      其中Access层是打造的重点模块,将原来访问XLog模块记录日志,修改成Journal Write模块记录日志。PostgreSQL通过原生的XLOG来保证存储的恢复时的一致性,这些XLOG会保存到本地日志文件中。针对CynosPG需要将数据页面的日志改动写入到CynosStore中,例如:

      1) 表、索引以及MultiXACT的修改按照字节修改生成日志;       2) CLOG以及CynosPG的Visibility Map的修改按照bit位的修改形成日志。

      诸如此类。经过这样的改造,CynosPG可以将系统中数据修改形成CynosStore的日志并发送到存储中,但是这些日志与PostgreSQL原生的XLog的格式是不同的。

      Storage/Buffer:buffer pool以及页面的布局、锁等实现。

SMGR:存储管理器。CynosPG可以支持多种类型的存储,MD即是其中一个。为了支持新的分布式块存储,也引入了新的SMGR类型,即 CynosStore存储。在此主要使用CynosRead接口从存储中读取页面。

MD:磁盘存储实现。CynosPG通过MD模块完成从磁盘文件读写数据。

CynosReadJournal Write对应于对CynosStore中的数据读、写。CynosStore也将文件的创建、扩展、删除、以及截断(文件DDL)实现成了日志记录,与数据日志一起写入到CynosStore中。因此CynosStore是一个完整的并且完全的日志系统存储。

读IO进程:用来处理CynosPG对CynosStore发起的读请求;写IO进程:处理CynosPG写入到CynosStore的日志流。

CynosStore Client:封装了对CynosStore的访问接口,例如:读取页面接口,生成CynosStore日志接口等

      CynosPG将原生PostgreSQL的XLOG转化成CynosStore要求的日志格式,同时在原生的Primary和Replica传输的信息由原来PostgreSQL的XLOG修改为CynosStore的日志格式。TDSQL-C 架构最终脱离了PostgreSQL原生的XLOG,同时对XLOG相关的一些特性也做了调整和优化。

日志系统

      可以理解存储层CynosStore是一个支持日志的、提供多版本读的、分布式的块设备,其日志是幂等的,每条日志记录的是页面的物理修改结果,由 <页面号,页面偏移,修改内容,修改长度> 四个部分组成,这样,在同一页面上多次回放同条日志不会造成数据破坏——幂等。以表插入元组为例,原生PostgreSQL的日志格式是(简化方便理解):

      relfilenode+pageno来确定一个页面,offsetnum位置插入一条元组,插入的元组是在恢复时由informask2, infomask, hoff, tuple_data等信息进行重构。而CynosPG的日志结构如下,假设在页面号为1的页面上插入元组tuple,CynosPG会生成多条日志格式如下:

这些日志记录了页面在插入元组时的所有修改,相对于原生日志这种格式更像是物理日志。这些日志最终会在CynosFileSystem形成一个MTR加入到日志流中,发送到CynosStore存储。CynosStore合并日志时,需要将此MTR完全应用到数据页面才算成功,因为MTR的部分应用会造成页面结构的不正确,因此我们只要能够保证按照MTR的粒度读取页面,就能够保证读取到的页面的结构是正确的。

      目前看CynosPG的日志量比原生PostgreSQL的XLOG的要大,其实确实是这样。但我们基于最新的日志格式,并且利用其天然的幂等性,对CynosPG的内核中的一些特性进行优化,而优化之后的日志开销与原生PostgreSQL的日志系统的日志开销几乎相等。这些优化和设计包括:

      1) 移除原生PostgreSQL中full page write(FPW)特性。FPW是为了防止系统crash再重启之后,那些半页写(torn page)的页面没有正确恢复,类似MySQL的double write。由于新的日志格式是幂等的,当出现半页写时,系统直接重新在此页面的回放日志,即可将页面修复到一致状态。因此CynosPG无须原生的FPW,从而减少了日志量。

      2) 移除系统中脏页面刷盘操作。CynosPG通过日志保存页面的修改,并且可以通过在基页上合并日志而读取到任何时间点的页面,因此无需原本系统的刷脏操作,仅仅刷日志就足够。

      3) 移除计算并填充页面的CRC操作。在原生PostgreSQL中,页面在刷盘前会计算并填充页面的CRC属性,而在CynosPG中,如果要正确计算页面的CRC属性,应该是在每条页面修改日志之后,都追加一条修改本页面CRC属性的日志。这样势必会造成日志条数增加以及计算CRC而引起的CPU时间增加。为了减少CPU时间和日志条数,我们将CRC的计算下放到CynosStore中,当CynosStore在刷页面前,计算并填充页面的CRC值。但是在CynosPG中,不是所有页面都有CRC值,因此为了支持这个特性,我们将CynosPG的页面布局做了修改,这也是CynosStore对页面布局的要求:保留前10个字节给CynosStore,其中:前8个字节记录LSN,第9和10字节记录CRC。通过这样的设计,我们减少了计算层的CPU使用和日志条数。

      4) 实现文件的异步扩展。在原生PostgreSQL数据库,使用的本地文件系统,其扩展操作是同步并实时的反映到磁盘文件上。但是CynosPG的扩展是通过日志实现,如果每次扩展都对日志做一次flush操作,让扩展实时地在存储上,必然会造成系统的性能下降。因此,我们实现了文件的异步扩展,即:文件扩展的日志可以先保留在系统的日志buffer中,当事务提交的时候再把日志刷到存储上。并且实现了在文件扩展时可以按照策略进行批量扩展。

除了以上在CynosPG内核的优化,CynosPG对日志的记录方式也进行了精简和压缩。CynosPG的日志都有一个大约40字节的日志头(LogHeader),同一个MTR的中的修改相同页面的日志可以合并,减少日志header数,如下图所示:

LH代表LogHeader,Log Element代表对页面的页一次修改。如上图,有两条对Block1的修改日志,并且每个修改都有一个日志头(LH),经过日志头合并优化后,形成新的MTR中,修改Block1的那些日志共享了同一个日志头,减少了40字节大小。同时,如果同一个页面的修改的两条日志是相邻的,那么可以将两条日志合并成一条日志,如果Offset2 <= Offset1 + Length1,则日志可以进一步合并如下:

      通过这种方式减少了日志条目,从而可以提高日志合并和页面的生成速度。CynosPG对日志写也进行了并行、流水线及批量优化,通过以上的各种优化,可以很大程度上减少了网络IO和日志量,优化之后的日志开销与原生PostgreSQL的日志系统的日志开销几乎相等。

三、CynosFileSystem 分布式用户态文件系统

      TDSQL-C采用计算和存储分离的架构,所有计算节点共享一份数据(share storage架构),其弹性扩展和高性价比的基石则是分布式用户态文件系统CynosFileSystem,向上为CynosPG提供Pool维度的文件存储服务,提供实例读写访问所需要的分布式的文件管理,负责将文件的读写请求翻译为对应的BLOCK读写,向下访问分布式云存储系统CynosStore(一个支持日志的、提供多版本读的、分布式的块设备存储)。

      CynosFileSystem(CynosFS)与传统的文件系统不同,CynosFS专为TDSQL-C数据库设计的用户态文件系统,它基于CynosStore的共享块存储服务,向上提供非POSIX接口的文件服务,CynosFS对接的CynosStore存储服务接口主要有写日志,读Page构成。CynosFS Pool维度布局如下图所示:

pool: 一个pool与一个数据库实例是一一对应的关系,pool向上提供一个逻辑的存储池并隐藏分布式存储的细节。 segment group (sg): pool逻辑上划分为多个sg,sg是分布式存储层数据复制与迁移的最小单位,同时也是pool扩缩容的最小单位,当前sg的大小为10G。一个sg包括的3个segment实际存储同一份数据,通过一致性协议(Raft)进行同步。 block group: sg逻辑上划分成40个block group,每个block group都bitmap,inode blocks等元数据用于管理本block group的资源。当前一个block group大小为256M,由8k大小的block组成,即一个block group有 256M/8k = 32768个block。block group在逻辑上划分成由如下几个区域:

  • group descriptor block: 占一个block, 存放数据其实就是sg文件系统的控制信息部分,也可以说它是sg资源表。每个sg内仅第一个block group的group descriptor block有效,其它block group的group descriptor block未使用,仅为了保持所有block group结构一致。
  • inode bitmap block: 占一个block, 仅前4k用来标识本block group内inode的空闲情况。
  • block bitmap block: 占一个block, 仅前4k用来标识本block group内blocks的空闲情况,初始时group descriptor block、inode bitmap block、block bitmap block、inode table blocks对应的bit被标识为非空闲状态。
  • inode table blocks: 占多个block, 每个block划分成多个inode, 并且inode不会跨越两个block。
  • extent blocks:占多个blocks, extent tree除根节点以外的节点从这个区域分配。
  • data blocks: 占多个blocks, 用于存放实际的数据, 文件扩展时从这个区域分配blocks。

      inode: 文件名、inode与extent tree三者是一一对应的关系。inode内部存储文件的大小等文件详细属性,同时也存储extree tree的根节点。

      extent tree: 文件和Pool在逻辑上都是由block组成。把文件的block称为logical block, pool上的block称为physics block, 从logic block到physics block的映射就是由extent tree完成的。如下图如示,logical blocks被映射pool上由多个连续的physics blocks组成的区段(extent)。

extent tree类似于B+树,不同之处在于extent tree的插入与删除都是尾部,故extent tree也不存在节点的分裂。extent tree的根节点直接存储在inode中,其它节点从block group中的extent blocks区域分配,即除根节点外其它节点都是一个完整的block。每个节点都由头部和entries两部分组成。

CynosFS 两种队列完成日志写及页面读流程:

1)日志发送流程(写通信队列):日志产生到日志写入Journal Buffer,通知CynosStoreAgent下发日志; 2)页面读取流程(读通信队列):Backend发起读取页面,到返回页面并通知Backend页面可用。

CynosFS是专为云原生数据库而打造的分布式用户态文件系统,将传统分布式文件系统优势与云原生数据库相关特征进行融合,是TDSQL-C弹性扩展、海量存储、高性价比的基石,目前单实例存储容量高达128TB,架构设计理论上是容量大小无限制。CynosFS如何做到在海量存储下(百TB存储,海量小表场景)做到高性能,以及其他模块细节原理,我们后续文章会逐步给大家介绍。

四、CynosStoreAgent 内核实现

      CynosStoreAgent为CynosFS提供SG维度的存储访问,提供SG的读写接口,对于读写请求有不同处理。 写请求:将修改日志通过LOG API发送到CynosStoreNode,读请求:直接通过BLOCK API读取CynosStoreNode数据。CynosStoreAgent 除了承担计算层与存储层的读写交互,也负责主备间日志流同步,对于CynosPG主备实例间只通过CynosStoreAgent进行交互。

      CynosStoreAgent主要系统的模块结构,以及与CynosPG、CynosFS、CynosStoreNode、CynosStoreMeta核心交互关系如下图所示,CynosStoreAgent与TDSQL-C所有的内核组件均有关联。下面主要介绍将其核心部分,后续文章也会给大家分享其他细节实现。

CynosStoreAgent(简称SA)作为一个独立的进程服务本机上的所有实例。(一台机器上可以配置多个CynosStoreAgent)。包含如下几个主要模块:       1) 初始化(Startup Pool Task):完成Pool的初始化工作。当CynosPG实例连接到StoreAgent时,会执行这个过程,初始化会话。包含:Fence功能(防止多主同时对Segment Group进行写的一种机制)、GetWalList/TruncWal返回APPDATA、从Meta获取PoolInfo,以及初始化JournalControlData结构;       2) Read Page Task:接收处理读请求,将数据直接放到CynosPG中的Shared Buffers中,元数据放入CynosFS Cache中;       3) Set MRPL/VDL Task:MRPL下发到CynosStoreNode(简称SN)、VDL随锚点日志下发SN;       4) ReadSlot:记录当前发起的读请求以及他们关联的VDL。ReadSlot用于计算MRPL,发送到SN进行日志回收;       5) Error Handle:处理读流程和日志流程发生的错误,例如:断连接后日志重发,切主等;       6) Journal Flush Task:异步刷新日志到SN;       7) Calculate VDL Task:推进VDL回收日志Buffer;       8) Journal Bitmap:记录哪些日志在SN中已经持久化;       9) 以及上图未标识的主备日志同步逻辑,与StoreMeta(简称SM)存储信息同步逻辑等等;

日志发送

      CynosStoreAgent日志发送流程如上图所示分为两层。Session(pool)层:保存于Pool相关的信息,对CynosPG(CynosFS)实例提供服务。每一个实例对应一个Session数据结构,包含:日志相关的Bitmap、Fence值、PoolInfo。CynosFS启动初始化的时候,需要向CynosStoreAgent进行注册,创建Session数据结构。 Session Task会在UnixSocket上进行wait,当有日志发送请求时,Session Task会将发送的日志通过Segment ID提交到固定的Shard CPU进行处理:调用Fill Journal Task填充日志头部、Term(并:计算CRC)。将填充好的日志根据Segment ID 再dispatch到其他CPU上,通过网络发送到SN(Send Journal Task)。

页面读取

      CynosPG Backend把请求放到读请求队列中,CynosStoreAgent从读队列中获取读请求,并根据映射将请求转发到对应的Read Task,由网络发送出去。读请求的返回值将会先将数据copy到Buffer Pool中,再唤醒Backend。对于错误处理:Net层收到读请求相关的错误,发送到对应的Session Class进行处理,包括:网络错误、TransLeader、NotInService、SN crash、Fence失败等。写日志和读数据的返回都在同一个CPU上的Session进行处理,因此,对于TransLeader需要更新Pool的配置时,可以无锁处理。

  CynosStoreAgent在工程实现上,由于现代多核和多插槽计算机对于跨核间的数据共享(atomic instruction、cache line bouncing、memory fences)代价非常大,CynosStoreAgent(无锁实现)工程实现上使用了创新的share-nothing编程异步框架,一种无需耗时锁定即可在 CPU 内核之间共享信息的设计,面向现代新硬件技术的优势实现了极致性能。由于篇幅限制,StoreAgent其他具体模块细节就不展开了,后续文章会逐步给大家深入每一点进行分享。

五、总结

本文从内核开发的角度介绍云原生数据库TDSQL-C计算层CynosPG、CynosFS、CynosStoreAgent内核实现原理,后续文章内核团队会由点及面的分享计算层的各个技术点细节。

广告时间:TDSQL-C PG数据库团队广招天下英才,这里拥有全球顶尖的数据库专家,汇集了一批极具极客精神的数据库爱好者,团队不断的吸收业界最新的理论和工程成果,最新的硬件和软件技术,将其应用到自研系统中,设计和实现高性能新一代云数据库,TDSQL-C PG还有很长的路要走,如果你也对数据库领域技术感兴趣,欢迎加入我们,让我们一起见证TDSQL-C PG 攀登世界的顶峰。

六、相关概念

Mini-transaction:MTR用来保证硬盘上页数据的ACID特性 Write Ahead Log(WAL LOG):增量日志流水,里面的日志记录保存了页面修改操作,物理日志 Log Sequence Number(LSN):唯一标识一条日志记录,按生成顺序连续单调递增 Consistency Point LSN(CPL):MTR可能对应多个磁盘数据页的修改,相应的会产生多条日志记录,这批日志记录中的最后1条(也就是LSN值最大的1条)标识为CPL,也是MVCC的读一致性点 Segment Group Complete LSN(SGCL):表示SG已经将所有小于等于该值的日志记录持久化到磁盘上了。对于TDSQL-C来说,因为采用了Raft协议,SGCL就是Raft的CommitIndex Volume Complete LSN(VCL):标识已经持久化存储的Pool级别连续日志中的最大LSN值 Volume Durable LSN (VDL):小于等于VCL中的CPL最大值 Read Point LSN (RPL):CynosPG实例的事务读LSN Min Read Point LSN (MRPL):MRPL是read-point的低水位,每个存储节点根据全局的MRPL,不断推进数据页版本,segment的block与对应的block_wal_list进行合并,CynosPG实例的访问的RDL必须大于MRPL,在实例层LSN是连续的,而每个segment内LSN是不连续的,Index是segment内LSN概念,通过lsn_mgr管理lsn与Index映射关系

参考资料: 1. 源代码:《CynosPG源码》、《CynosFS、CynosStoreAgent源码》 2. 《深入了解CynosDB for PostgreSQL实现》 3. 《腾讯云CSIG数据库产品中心PG内核团队WIKI

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、概述
  • 二、CynosPG 内核实现
    • 日志系统
    • 三、CynosFileSystem 分布式用户态文件系统
    • 四、CynosStoreAgent 内核实现
      • 日志发送
        • 页面读取
        • 五、总结
        • 六、相关概念
        相关产品与服务
        TDSQL PostgreSQL 版
        TDSQL PostgreSQL 版(TDSQL for PostgreSQL, 原 TBase)是腾讯自主研发的分布式数据库系统,具备高 SQL 兼容度、完整分布式事务、高安全、高扩展、多级容灾等能力,成功应用在金融、政府、电信等行业核心业务中。同时提供完善的容灾、备份、监控、审计等全套方案,适用于GB~PB级海量 HTAP 场景。
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档