前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >孙旭:CynosDB for PostgreSQL一主多读架构

孙旭:CynosDB for PostgreSQL一主多读架构

原创
作者头像
腾讯云开发者社区技术沙龙
发布2019-04-01 15:34:26
1.3K0
发布2019-04-01 15:34:26
举报

3月16日在北京举行的腾讯云自研数据库CynosDB交流会圆满落下帷幕。现将技术团队分享的内容整理如下。

本次主要是想和大家分享一下我们CynosDB for PostgreSQL的一主多读的设计以及优化。

先看一下我们为什么需要CynosDB?当前云上数据库在使用上仍存在问题,这些问题包括:资源利用率低、扩展能力不足、资源规划难以及备份难。

CynosDB的解决思路是:通过计算存储分离获得计算资源弹性调度能力。第二个是日志下沉及异步回放,同时我们移除了数据页面的刷脏逻辑,这样降低了计算存储分离的架构下的网络开销。 第三个是共享分布式存储,存储资源可以弹性扩展。最后一个是后台的持续日志备份,用户可以不必关心备份策略和备份存储资源规划。

这个是我们CynosDB的架构,它是一个云原生数据库,核心设计是日志下沉和日志异步回放,同时在此基础上,我们提供基于日志的数据页面多版本读。这个架构的上层是一个PostgreSQL数据库实例,它通过CynosStore Client与分布式存储CynosStore进行通信,完成读写操作。CynosStore是一个分布式块存储,后台服务会持续把日志发送到对象存储上,实现基于时间点的数据恢复功能。对象存储还会备份其它一些东西,后面会介绍。

先看一下日志下沉、异步回放设计。日志下沉是指我们DB层产生的日志都会发送到分布式存储中,而不是存到本地。在分布式存储中会划分一块固定的存储空间来专门存储日志,由于空间大小固定,因此在CynosStore中会有特定的线程定时地把日志异步地合并到数据页面上,通过日志回收机制可以有效的利用这个有限的日志空间,保证写的连续性。CynosStore中需要合并的日志点由DB层产生,介绍这个前先看几个概念,MTR、CPL和VDL,MTR,全称是Minimal Transaction Record,是指的对存储的一个原子修改,这些修改不能部分地应用到存储页面,否则会造成数据破坏,比如:数据库中的索引页面分裂过程,会涉及多条修改不同数据页面的操作,这些操作的集合就是MTR,它让索引从一个一致状态到另一个一致状态,任何部分地应用都会导致索引破坏,因此MTR要么完全应用,要么完全不应用。CPL就是MTR的最后一条日志,VDL是存储层持久化的最大CPL。另外,我们的日志是异步写入的,也就是在计算层会有一个日志buffer,PostgreSQL的backend先把日志写到buffer里面,再由后台的异步线程的写入到存储。日志向日志buffer的插入过程也是并行的,而非串行。

再看一下多版本读,在系统中我们的读是同步的。通过这个图说明一下我们读的过程,假设某个读请求需要页面A的30号版本,但是页面A在内存中只有20号的版本,并有对这个页面的更新日志:25、30、40、50。如果要满足刚才的读请求,CynosClient会把25和30日志合并到基础页面上,也就是版本为20的页面中,然后再返回给上层。同样的道理,如果有个请求需要版本是50的页面,系统也会一样处理。到此,我们就有一个RPL的概念,即Read Point LSN,就像上例子中的30就是一个RPL。系统在读页面的时候都会以RPL作为读点,它其实就是一个持久化的VDL。在系统中,所有的读版本都是向前推进的,这就意味着某些老的版本将永远不会读取到,因此我们将全局最小的RPL,也就是MRPL,作为CynosStore日志的回收点,由DB层发往存储,存储会将小于MRPL的日志回收掉。

介绍完CynosDB for PostgreSQL的架构后,大家对系统的体系结构有了一定的认识,现在我们看一下一主多读的设计,在这个设计里,我们也遵循了前面的一些思想。首先,我们看一下为什么需要多读。第一个,传统PostgreSQL的主备模式有缺点,这个架构图就是传统PG的主备模式,其过程是Master会写日志文件,然后再从日志文件把日志读出来发送到备机,备机将接收到的日志再写入到磁盘,然后读出来进行恢复,这样就会增加一些写日志的IO、并且还需要额外的存储来存放日志。当备机切机或者启动的时候都要恢复完这些存量日志。我们再创建一个备机实例的时候,还需要全量的copy数据,当数据量大的时候,会特别耗时。再一个问题就是PostgreSQL的备机只读需要解决一些冲突,比如说一个只读事务正在访问一个页面buffer,比如Heap页面,这时候主机可能产生并发送一些清理这个页面上死元组的日志,这些日志在备机进行恢复的时候,恢复进程需要等待访问这个页面的事务释放这个buffer,此时日志恢复就会被block,当超过一定时间,日志恢复进程会cancel掉这个读事务,这样就无形的拖慢了备机的日志恢复过程。以上这些所有的问题会导致备机恢复日志慢,切换慢,浪费存储。但是我们还是需要多读,来提高系统的横向扩展能力,并提高系统的可用性。

这个是我们一主多读的一个架构,其实我们所需要做的是提升系统的读能力,快速完成主备快速切换。我们这个架构的核心设计是,首先Replica不存数据和日志,主节点直接将日志发送到Replica的内存,Replica直接恢复日志,Replica无须写盘保存日志。其次由于日志在内存中,为了保证内存不会用满,在日志恢复的时候,我们采用并行恢复。第三,为了减少日志恢复和读事务的锁冲突,我们的数据buffer的支持多版。最后,主实例中的配置文件我们会存放到CynosStore和对象存储(COS)中。通过这样的处理,整个实例就变成无状态的,可以在任何地方快速地启动和初始化它。

首先看一下核心设计中的日志恢复,看看我们是怎么做到读事务和日志恢复是不相冲突的。如上这个架构图,首先主实例是首先会把日志发送到RO这边,RO实例会把日志按照数据块放在哈希表里面,CynosStore Process的后台线程会定时、并行地将哈希表里的日志合并到数据版本里面,同时更新运行时信息。这里有2个主要设计点,第一点我们的恢复是并行恢复,第二点如果页面不在内存里面,我们就会跳过这个页面的日志的合并,而不是从存储中读取这个页面,再进行日志恢复,这样我们就加快了日志恢复过程。

下面那我们说下,我们的DB层和CynosStore Process如何配合完成数据buffer恢复的。如上图,其实我们的主要思路是将日志管理、恢复都放在CynosStore Client/Process中实现,并在DB层和CynosStore Client层抽取一些功能做成回调或者钩子,通过这种配合完成多版本页面读取和数据buffer的刷新。

在DB层,我们提供这些功能给CynosStore Client使用:第一个是根据某个VDL获取一个buffer page ,第二个是分配空白的buffer槽位,第三个是提供接口更新运行状态,例如事务提交信息。CynosStore Client提供的接口比较简单:获取当前的RPL,以及提供多版本读接口。通过这些回调,我们看一下在恢复过程中,是如何完成数据buffer恢复的。

这里面的第一个图就是传统PostgreSQL的备机恢复流程:首先walreceiver接收日志,并把日志放到日志文件中,Startup进程会从日志文件中读取日志,如果要恢复页面在buffer中,则直接恢复日志,否则,从数据文件中读取页面,再进行恢复。而在我们的CynosDB for PG中,我们的恢复过程是在CynosStore process中完成,CynosStore Process会有一个日志接收线程接收日志,放到日志buffer中,再由日志追加线程读取日志buffer的日志放到Hash表中,如果接收到的日志达到一定的批量,会通知日志apply线程,并行合并这些日志:apply线程会调用DB“获取buffer page”接口,获取需要合并的页面,如果数据页面不在buffer中,系统会跳过这些日志,同时也会调用DB的“更新运行状态”接口,更新内存状态。

这个图是我们读页面的过程,backend在读取PageA的15版本页面的时候,后台的日志恢复线程可以不被阻塞的恢复日志,这里是100号页面。当15版本页面访问结束的时候,就会被buffer淘汰算法淘汰掉。下面这个图CynosStore Client可以快速返回需要的页面,也就是说,CynosStore Client接收到读页面的请求,比如需要30版本页面,那么CynosStore client会调用DB回调“获取buffer Page”接口,在DB的buffer pool中返回最接近30的那个版本页面,即20版本页面,然后以此为基础合并日志到30版本;而不是以10版本页面为基础合并日志到30版本,这样可以快速的返回目标页面给Replica。当然,如果请求的页面在DB的buffer pool中找不到基础页面,那么会直接从存储读取,比如图中的B页面。

在Replica,我们也会按照MTR粒度访问页面,以保证对象结构的完整性。比如我们遇到索引页面分裂的时候,如上图的MTR<J1、J2、J3、J4>这种,我们是按照J4这个LSN点去访问A、A’页面,而不是J2或者J3,这样可以保证索引结构的完整性。

上图是一个PostgreSQL的备机切换为主机的过程,首先收到切换信号,系统会停止walreceiver进程,终止日志接收,然后startup进程会恢复完存量日志,并退出,此时可以接收写事务。

但是在CynosDB for PG中,我们的切换和PostgreSQL还是有一些区别的,传统PostgreSQL需要恢复比较多的日志,但是CynosDB for PostgreSQL不会,因此切换速度会快很多。传统的PostgreSQL需要恢复日志是因为,只有将需要的日志恢复完之后,数据页面才能达到一致状态,但是我们是存储计算分离,并且是日志异步回放,我们只要按照VDL进行读取,而不用完全合并完日志,也能读取到正确的存储数据。上面这个图是产品上的一个架构。HA组里的备机实例不参与读,RO组里面的实例参与读,但是不参与HA切换,这样可以保证HA切换的时候,不影响读写业务。

最后说一下Replica启动,其实传统PostgreSQL 备机启动会有这么一个问题,即启动的时候需要将日志至少恢复到MinRecoveryPoint,否则可能无法获取一个一致的数据状态。上图揭示了MinRecoveryPoint的工作过程:当刷新数据页面的时候,会将当前已经恢复的最新日志点记录到控制文件中,作为MinRecoveryPoint,即图中的1000,虽然这时刷下去的日志点是100。而CynosDB for PG在启动的时候,无须将日志同步地合并到页面,我们也能读取到一致的数据状态,实际上只要提供一个合法的RPL给Replica,就可以读取到正确的数据,与日志是否完全被合并到数据页面没有关系。

Q:你好我想问一下咱们持续备份,是每天一个时间定点吗?还是说发现数据有变化才会去备份?

A:这个备份是基于日志的备份,下层的存储日志会持续备份。

Q:如果日志真的回收了,我还想找回还能找到吗?

A:我们的日志回收就在底层,但是回收前会先备份。

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

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

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

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

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
对象存储
对象存储(Cloud Object Storage,COS)是由腾讯云推出的无目录层次结构、无数据格式限制,可容纳海量数据且支持 HTTP/HTTPS 协议访问的分布式存储服务。腾讯云 COS 的存储桶空间无容量上限,无需分区管理,适用于 CDN 数据分发、数据万象处理或大数据计算与分析的数据湖等多种场景。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档