专栏首页腾讯云技术沙龙孙旭:CynosDB for PostgreSQL一主多读架构
原创

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

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:我们的日志回收就在底层,但是回收前会先备份。

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

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

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 许中清:CynosDB for PostgreSQL分布式存储揭秘

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

    云加社区技术沙龙
  • 尚博:CynosDB 计算存储分离架构的实现和优化

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

    云加社区技术沙龙
  • 罗冬日:深度学习在语音识别上的应用

    我今天演讲主要分四个部分,第一个是分享语音识别概述,然后是深度神经网络的基础;接下来就是深度学习在语音识别声学模型上面的应用,最后要分享的是语音识别难点以及未来...

    云加社区技术沙龙
  • 干货分享 | 腾讯自研数据库CynosDB分布式存储的核心原理

    点击上方蓝字关注每天学习数据库 作者简介:许中清,腾讯云自研云原生数据库CynosDB的分布式存储CynosStore负责人,负责数据库内核开发、数据库产品架...

    腾讯云数据库 TencentDB
  • 一起来学 SpringBoot 2.x | 第三篇:SpringBoot 日志配置

    摘要: 原创出处 http://blog.battcn.com/2018/04/23/springboot/v2-config-logs/ 「唐亚峰」欢迎转载,...

    芋道源码
  • 日志分析工具logParser的使用

    ——本文来自阿雷头

    用户2202688
  • 干货分享 | 腾讯自研数据库CynosDB一主多读架构设计及优化

    点击上方蓝字每天学习数据库 作者简介:孙旭,腾讯高级工程师,9年数据库内核开发经验;熟悉数据库查询处理,并发控制,日志以及存储系统;熟悉PostgreSQL(...

    腾讯云数据库 TencentDB
  • 应用的日志分析、对比以及实践

    到目前为止,参照我们系统( 某上市互联网保险中介 )应用,就日志而言,我们经历了以下几个时间段的变化,也经历很多方面的尝试。就目前我们的应用日志系统经历了以下的...

    明哥的运维笔记
  • 关于日志打印的几点建议

    操作日志,主要针对的是用户,例如在Photoshop软件中会记录自己操作的步骤,便于用户自己查看。

    lyb-geek
  • EFK实战二 - 日志集成

    在EFK基础架构中,我们需要在客户端部署Filebeat,通过Filebeat将日志收集并传到LogStash中。在LogStash中对日志进行解析后再将日志传...

    JAVA日知录

扫码关注云+社区

领取腾讯云代金券