前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >腾讯云国产数据库CynosDB架构分享

腾讯云国产数据库CynosDB架构分享

作者头像
腾讯云数据库 TencentDB
发布2019-05-20 14:41:06
1.6K0
发布2019-05-20 14:41:06
举报

点击上方蓝字关注我们吧

作者简介:孙旭,腾讯云高级工程师。10年数据库内核研发经验,熟悉PostgreSQL、Teradata数据库内核,熟悉数据库的查询优化、执行、事务并发以及存储等子系统;对分布式数据库有深入的研究和研发经验。目前在腾讯云从事CynosDB数据库研发工作。


2019年5月8日-10日,DTCC2019年中国数据库大会上,腾讯云数据库高级工程师孙旭,受邀做了主题为《CynosDB for PostgreSQL一主多读架构》的技术分享,以下为大会现场演讲内容。

关注“腾讯云数据库”官方微信,回复“0517”,即可下载本文PPT。

孙旭在会议现场

本次大会我主要就腾讯云自研数据库CynosDB做一个分享,详细讲一下它与传统的数据库的区别,重点分享CynosDB for PostgreSQL的架构及关键技术,一主多从的设计,以及我们做的一些性能优化。

一、为什么需要CynosDB?

1. 传统数据库在云上的缺点

第一就是资源利用率低。我们会发现云上的很多数据库实例,主机的CPU空闲,但是存储已经用满,这样的话如果用不上的闲置资源,就会造成一定浪费,也无法支撑更多的用户业务。

第二是扩展能力不足,这里主要指单机的扩展能力,它可能受限于单机主板支持的CPU个数以及内存等。如果使用主从架构的话,当扩展一个从节点或者读节点的时候,可能需要大量拷贝数据到备机,这个过程会耗费比较多的时间。此时如果说业务流量上来了,可能不会立即扩展出读节点,来响应业务高峰。

第三个是资源规划难,就是说有时候我们云上的一些业务,建设初期没法预估需要多少计算能力才能满足业务需求。比如说做一个促销活动,业务量可能突然增大,这时候如果你再去增加读写点,可能会花费半小时、一个小时或者更长时间,这个读节点才会在线处理增加的业务流量。因为一般来说像我们传统数据库,比如像PG这种,我要去做一个从节点,这时候我要把主节点数据全部拷贝一下,这个是很花时间的。

第四是备份难,我们的数据库备份可能都会选择比较空闲的时间进行,比如晚上或者一个不会去影响业务的时段。但是CynosDB它是后台持续备份的,它不会对线上业务产生影响,将来也会有我们的其他同学会去介绍我们的备份设计。

2. CynosDB解决问题的思路

首先是计算存储分离,比如把运算CPU或者内存放在一起统一分配,可以获得弹性调度能力。第二个是日志下沉及异步回放,同时我们移除了数据页面的刷脏逻辑,这样降低了计算存储分离的架构下的网络开销。第三个就是分布式存储,这也是我们腾讯云自研的分布存储系统,共享的分布式存储可以横向扩展。第四个是后台的持续日志备份,传统数据库我们的备份是在数据库主机上去拷日志完成备份,而CynosDB的持续备份是在存储上进行,不干扰数据库实例,减轻由于备份的工作任务对数据库的冲击,并且用户可以不必关心备份策略和备份存储资源规划。

二、CynosDB for PostgreSQL架构-关键设计

1. CynosDB-云原生数据库

这个是我们CynosDB的架构,这个架构其实跟传统数据库是不一样的:传统数据库的计算和存储都在一台机器上,而我们的通过计算存储分离,将存储从原来的数据库中剥离出来,使用共享存储实现。这个架构的上层是一个PostgreSQL数据库实例,它通过CynosStore Client与分布式存储CynosStore进行通信,完成读写操作,CynosStore是一个分布式块存储,里面有一些StorageNode存储服务组成,CynosStore Client封装了很多关于存储和管理日志的一些任务。后台服务会持续把日志发送到对象存储上,实现基于时间点的数据恢复功能。我们的核心设计是日志下沉和日志异步回放,同时在此基础上,我们提供基于日志的数据页面多版本读。

2. 日志下沉、异步回放

先看一下日志下沉、异步回放设计。日志下沉是什么意思?对传统数据库比如我在一个页面做插入操作,生成的日志会放到日志管理子系统的日志buffer里,日志buffer的重用、刷新、并发管理、等都是由数据库来做。在cynosdb里,我们把日志管理做成独立模块,在CynosStore Client中实现。任何数据库如果想接入这个系统的话,都不用去关心日志管理,直接调相关接口完成日志记录即可。这里的日志和普通日志有些区别,比如PG的日志更偏向逻辑的概念,而我们现在的日志,记录的是物理修改:对某页面的什么位置做了什么内容的修改。另外,日志向日志buffer的插入过程是并行的,若有5个用户同时生成日志,往日志buffer copy都是并行进行的而非串行。

总结一下就是,日志下沉是指我们DB层产生的日志都会放到CynosStore Client的buffer中,然后异步发送到分布式存储中,而不是存到本地。而在分布式存储中有一块固定的存储空间来专门存储日志,由于空间大小固定,因此在CynosStore中会有特定的线程,定时地把日志异步地合并到数据页面上,通过这种日志回收机制可以有效的利用日志空间,保证写的连续性。

CynosStore中需要合并的日志点由DB层产生,我们先简单介绍一下这几个概念:MTR、CPL和VDL。MTR,全称是Minimal Transaction Record,是指的对存储的一个原子修改,这些修改不能部分地应用到存储页面,否则会造成数据破坏,比如:数据库中的索引页面分裂过程,会涉及多条修改不同数据页面的操作,这些操作的集合就是MTR,它让索引从一个一致状态到另一个一致状态,任何部分地应用都会导致索引破坏,因此MTR要么完全应用,要么完全不应用。CPL就是MTR的最后一条日志,VDL是存储层持久化的最大CPL。

3. 多版本读(同步)

再看一下多版本读,如果只有一个主节点没有从节点,其实是没有多版本的概念的,之所以说多版本其实就是在主从同步的时候。当主机向读节点发送日志的时候,会有一个日志的延迟,这个延迟会导致备机读取的页面内容是落后于主机的。通过这个图说明一下备机读的过程,假设某个读请求需要页面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一主多读

1. 为什么需要一主多读?

为什么需要一主多读,第一个就是提高系统的横向扩展能力,第二个是提升系统的可用性。并且传统PostgreSQL主备模式仍有许多的缺点。例如图里传统PG的主备模式,其过程是Master会写日志文件,然后再从日志文件把日志读出来发送到备机,备机将接收到的日志再写入到磁盘,然后读出来进行恢复。这样做的会有这样写问题:增加了写日志的IO、需要额外的存储来存放日志、启动和切换需要恢复比较多的存量日志等。当我们再创建一个备机实例的时候,还需要全量的copy数据,当数据量大的时候,会特别耗时,速度会很慢。

再一个问题就是PostgreSQL的备机恢复线程和读数可能会发生冲突,发生BufferPinLock冲突。比如一个读事务正在访问一个页面buffer,正好主机发送量一个日志,要物理上删除这个页面上的死元组,当这些日志在备机进行恢复的时候,恢复进程需要等待访问这个页面的读事务释放buffer pin,此时日志恢复进程就会被block,当超过一定时间,日志恢复进程会cancel掉这个读事务,这个冲突会形成日志堆积,从而导致恢复慢、切换慢。而CynosDB会对这些问题都有自己的解决之法,后面会我们会讲到。

2. 一主多读架构

2.1 核心架构设计

这个是我们一主多读的架构图,其实核心目的还是提升系统的读能力,快速完成主备切换。具体来说这个架构的核心设计原理是,在Replica本地不存数据和日志,主节点直接将日志发送到Replica的内存,Replica直接恢复日志,这样就保证了Replica无须写盘保存日志。Replica会将最小的事务号和MRPL发回主机,主机收集MRPL发送到存储,进行日志回收。其次由于日志在内存中,我们会先将日志放到日志buffer中,然后将这些日志按照block number放到hash表中,在日志恢复的时候,我们采用并行恢复,保证buffer不会被日志撑爆。如果日志对应的block没有在buffer中,我们会跳过日志恢复,这样又进一步提升了日志恢复效率。第三,为了减少日志恢复和读事务的锁冲突,我们的数据buffer的支持多版。最后,主实例中的配置文件我们会存放到CynosStore和对象存储(COS)中。通过这样的处理,整个实例就变成无状态的,可以在任何地方快速地启动和初始化它。

2.2 一主多读-日志恢复和管理

如上图,首先主实例会把日志发送到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接收日志,并把日志放到XLOG File中,然后Startup进程会从日志文件中读取日志,现在在做一个判断:如果要恢复页面在buffer中,则直接恢复日志,否则,从数据文件中读取页面,再进行恢复。而在我们的CynosDB for PG中,我们的恢复过程是在CynosStore process中完成,CynosStore Process会有一个日志接收线程先接收日志并放到日志buffer中,再由日志追加线程读取日志buffer的日志放到Hash表中,在接收到的日志达到一定的批量时,会通知日志apply线程,并行合并这些日志:此时apply线程会调用DB“获取buffer page”接口,获取需要合并的页面,如果数据页面不在buffer中,系统会跳过这些日志。

2.3 一主多读-读页面

这个图是我们读页面的过程,核心就是日志的回放与读事务互相不冲突。backend在读取PageA的15版本页面的时候,后台的日志恢复线程可以不被阻塞继续恢复日志,例如将日志合并到100版本页面。当15版本页面访问结束后,在此访问PageA时,就会使用100版本页面,15版本页面被淘汰。当返回给读事务的页面的时候,CynosStore Client会以较新版本为基础去合并日志形成期望版本页面返回,提升效率,比如CynosStore Client接收到读页面的请求,需要30版本页面,那么CynosStore client会在DB的buffer pool中返回最接近30的那个版本页面,即20版本页面,然后以此为基础合并日志到30版本;而不是以10版本页面为基础合并日志到30版本,这样可以快速的返回目标页面给Replica。当然,如果请求的页面在DB的buffer pool中找不到基础页面,那么会直接从存储读取。

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

2.4 一主多读-切换

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

但是在CynosDB for PostgreSQL中,我们的切换和PostgreSQL还是有一些区别的,传统PostgreSQL需要恢复比较多的日志,但是CynosDB for PostgreSQL不会,因此切换速度会快很多。传统的PostgreSQL需要恢复日志是因为,只有将需要的日志恢复完之后,数据页面才能达到一致状态,但是我们是存储计算分离,并且是日志异步回放,我们只要按照VDL进行读取,而不用完全合并完日志,也能读取到正确的存储数据。

下图是产品上的一个架构。HA组里的备机实例不参与读,RO组里面的实例参与读,但是不参与HA切换,这样可以保证HA切换的时候,不影响读业务;读实例仅仅参与读事务的请求。

2.5 一主多读-启动

传统PostgreSQL 备机启动会有这么一个问题,即启动的时候需要将日志至少恢复到MinRecoveryPoint,才能到存储的一致状态。可以看这个图里MinRecoveryPoint的工作过程:当刷新数据页面的时候,会将当前已经恢复的最新日志点记录到控制文件中,作为MinRecoveryPoint,即图中的1000,虽然这时刷下去buffer对应的日志点是100,然后在下次启动的时候,会将日志同步恢复到1000,将存储文件刷新到一致状态。而CynosDB for PG在启动的时候,无须将日志同步地合并到页面,我们也能读取到一致的数据状态,实际上只要提供给计算层一个RPL即可,就可以在存储中读取到正确的数据。

2.6一主多读-防止多写

最后一个问题是防止多写,这个多写是什么意思?一般来说像我们传统数据库主备如果管控判断有误的话,将备机提升为主之后,会产生新旧两主同时进行写操作,这会导致二者的数据有差异。要解决这样的问题比较麻烦,需要通过工具找出二者的差异,进行Merge。CynosDB for PG是如何解决这个问题呢?首先我们是共享存储,所以我们可以在一个集中点去识别是否在系统中有多个写入,我们选择的这个集中点就是下层的StorageNode服务。具体来说:数据库启动,会获取在Meta服务中当前的fencing 值,使用获取的fencing值连接到存储,存储会将小于这个值的写连接断开,并进行一些清理工作。比如这个图中说明的:初始是旧主的fence是值100,它会把所有Storage Node节点都设置为100, 当新主来了它取到的fence值是101。此时存储将fence小于101的写连接断掉,而读连接保持,即会将旧主100的写连接断开,当这时旧主的收到这个消息之后,会停止服务(shutdown),而Replica(90)会持续在线,不受影响。这样,即使管控无法判断或者无法kill旧主,而将新主拉起在线,也可以保证系统中不会出现双写的情况,导致数据损坏。


以下是提问环节:

Q: 我们在做切换时,从使用层面的角度来说,怎么样来保证平滑切换,应用不断?

A:现在我能想到的是一个是需要做一 个中间层作为缓冲,对于PG,可以使用pg_pool。它可以作为中间层进行连接保持。当后端的PG切换的时候,pg_pool可以重连,这样对前端业务层无感知。

Q: 中间层的话这样考虑是可以的,那么有没有计划就是说我们从pg的协议层来支撑?

A:如果要在协议层的实现的话,无非是把pg_pool的相关功能实现到接口和PG的前端协议中,这样性能会比较好,但是牵扯到修改内核代码,难度反而会大一些。

Q: 咱们现在看是分布式存储,要怎么去保证存储这一块性能?

A:从整个设计,包含存储,我们是做了很多优化。例如:我们的日志都是异步写入,存储里面的日志处理也做了异步化处理。同时我们也对日志进行了压缩,减少存储处理日志的条数,提升性能。

Q:我想问一下这个计算存储分离里面,生成日志的延迟和同步一致性问题。

A:我们主从(或者读)间会有延迟,但是会把延迟控制在毫秒级别。你也可以选择同步方式,但是这样会对写节点的写入性能造成影响。其实使用刚才提到的中间层也可以实现读取的一致性:通过中间层感知当前读节点的同步进度,然后再发起读请求。对于业务来说需要在同步和异步之间做权衡,决定是使用同步方式还是异步方式。

 关注“腾讯云数据库”官方微信,回复关键词“0517”,即可下载本文PPT。

往期推荐

《鹅厂老司机教你学习Innodb》

《腾讯数据库专家雷海林分享智能运维架构》

年中薅羊毛,可省18040元

云数据库MySQL年中疯狂折扣中,买MySQL高可用版送6个月数据迁移服务,1核1G内存100G SSD盘低至96.8元/月。免费数据管理DMC,双节点架构,自动容灾,最高可省18040元!点击左下角“阅读原文”立即参与~

↓↓点“阅读原文”享年中福利

好文和朋友一起看!

var first_sceen__time = (+new Date());if ("" == 1 && document.getElementById('js_content')) { document.getElementById('js_content').addEventListener("selectstart",function(e){ e.preventDefault(); }); } (function(){ if (navigator.userAgent.indexOf("WindowsWechat") != -1){ var link = document.createElement('link'); var head = document.getElementsByTagName('head')[0]; link.rel = 'stylesheet'; link.type = 'text/css'; link.href = "//res.wx.qq.com/mmbizwap/zh_CN/htmledition/style/page/appmsg_new/winwx45ba31.css"; head.appendChild(link); } })();

孙旭

赞赏

长按二维码向我转账

受苹果公司新规定影响,微信 iOS 版的赞赏功能被关闭,可通过二维码转账支持公众号。

阅读原文

阅读

分享 在看

已同步到看一看

取消 发送

我知道了

朋友会在“发现-看一看”看到你“在看”的内容

确定

已同步到看一看写下你的想法

最多200字,当前共字 发送

已发送

朋友将在看一看看到

确定

写下你的想法...

取消

发布到看一看

确定

最多200字,当前共字

发送中

微信扫一扫 关注该公众号

微信扫一扫 使用小程序

即将打开""小程序

取消 打开

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-05-17,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 腾讯云数据库 微信公众号,前往查看

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

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

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