日前,Distributed Cloud|2021全球分布式云大会·上海站隆重召开。腾讯云凭借其信息技术系统的安全可控性和前沿技术的创新性,在一众企业中脱颖而出,荣获**“分布式数据库创新技术奖”**。这是继腾讯云数据库TDSQL获第四届中国保险大数据分析与人工智能创新国际峰会年度“保险创新者大奖”之后再次获评。


在云原生专题论坛上,腾讯云数据库产品专家梁文灿先生就腾讯云企业级分布式数据库TDSQL生态发展战略、落地应用案例做出了详细介绍。同时腾讯云数据库专家工程师张远先生**对云原生数据库TDSQL-C的内核关键技术进行了深入解读。**


TDSQL-C基于全新计算存储分离的分布式数据库架构,融合传统数据库和云计算技术优势,100%兼容MySQL和PostgreSQL,极致计算能力突破千核,存储容量达PB级别,使得性能和安全性媲美商用数据库。并且TDSQL-C支持集群和Serverless灵活弹性部署,克服传统架构下的存储量受限、扩展难、主从延迟高等缺点,充分释放领先技术的成本效益,最高可为企业节省90%的成本。


下面带来腾讯云数据库专家工程师张远老师主题为《腾讯云TDSQL-C云原生数据库技术》的文字版。


# TDSQL-C 简介


张远介绍说,TDSQL-C是腾讯云自研的新一代企业级云原生分布式数据库,经过五年的打磨,在总体架构上,TDSQL-C是基于共享存储的存储和计算分离的架构。


![file](http://image.openwrite.cn/24379_CBE490317F814461B4380877691BF353)


与传统的MySQL主备架构对比,可以看到传统的MySQL主备通过binlog进行逻辑复制,而TDSQL-C是通过redo日志进行的物理复制;传统的MySQL需要向存储写多份数据包括data,binlog,redo log等, 而TDSQL-C只需向存储写一份redo日志即可;传统的MySQL主备各存储一份数据,而TDSQL-C基于共享存储只有一份数据。


**TDSQL-C具有以下几个关键的特性:**


(1)可靠性。TDSQL-C基于共享存储的云原生架构,存储是多副本的,能够保证数据可靠性。同时能够做到即时回滚,任意时间数据都可靠。


(2)极致性能。腾讯云对主备机读写性能做了全面优化,同时也根据不同规格做针对性优化。    


(3)可用性。TDSQL-C可以做到秒级的RTO,故障几乎无感知,同时主备延迟可以做到毫秒级。此外,基于共享内存,数据能够快速恢复、快速预热。


(4)弹性扩展。数据能够快速透明拓展,而且容量最大可以达到1PB,满足大的需求。  


# TDSQL-C 内核关键技术


张远演讲的第二部分围绕关键特性展开,详细介绍了TDSQL-C特性背后的技术原理和细节。


**1、TDSQL-C之高性能**


**(1)TDSQL-C 高性能-plan cache**


TDSQL-C 高性能的一个重要优化是实现了查询计划缓存,称之为plan cache。


图中展示了一条SQL在数据库中的执行过程,会经过以下几个阶段:


首先MySQL server接受到用户的SQL请求,在parse阶段解析为逻辑的执行计划树,接下来在查询优化阶段生成物理的查询计划,然后执行器从存储引擎获取数据进行计算。


![file](http://image.openwrite.cn/24379_2DC8642C247843A9B9E5413087798EFD)


经过plan cache优化后,一条SQL执行过程省略了前面的解析和查询优化阶段,SQL的执行时间大大缩短了。


![file](http://image.openwrite.cn/24379_2E4CD8035FF0418F8155D1FAEC5BBAC2)


优化效果以sysbench场景为例,不同颜色代表不同阶段的耗时,可以看到经过plan cache优化后,parse和查询优化时间减少了,性能提升了70%左右。


**(2)TDSQL-C高性能-异步组提交**


TDSQL-C是计算和存储分离的架构,因此计算节点和存储节点之间的网络IO存在一定时延。而写事务提交时需要保证redo先刷盘才能完成提交,因此Redo日志刷盘存在网络IO。TDSQL-C在线程池的基础上进行了异步组提交优化,事务提交交给后台线程异步完成,将线程池资源提前释放从而能够去处理更多的请求。优化后整体的读写事务QPS有70%的提升。


![file](http://image.openwrite.cn/24379_D747269C6F884E91BB218242645E1EC4)


**(3)TDSQL-C 高性能-Log Compaction**


TDSQL-C是基于日志的数据库,计算机节点和存储节点之间有日志的传输,同时Master节点和TXStore存储之间也有redo日志传输,TDSQL-C将redo日志进行了压缩。


![file](http://image.openwrite.cn/24379_DB2DF84B90434D9EAA42328B0B1B20CE)


下图是redo日志的结构,一条普通的redo日志包括日志头和日志内容,日志头主要包括日志类型以及Space ID和pageno等信息。通常情况下日志头占了日志的较大一部分。TDSQL-C对redo日志进行压缩存储,对于同一页面进行修改的多条redo日志可以共享一个日志头。优化后redo日志量减少了30%。


![file](http://image.openwrite.cn/24379_8E5E4BF634E543E89494DF50747007C6)


**2、TDSQL-C之高可用**


**(1)TDSQL-C 高可用-物理复制**


传统的MySQL的是通过binlog进行复制的。Binlog复制是在MySQL server层进行的,binlog记录的是逻辑的修改记录,binlog在备库apply需要经过server层的parser,optimizer后再经过engine的btree查找才能修改到对应的记录。这个路径比较长,复制速度慢。


![file](http://image.openwrite.cn/24379_E3380A1A4BAB47D5BC5E0073A8087344)


而腾讯云TDSQL-C采用的是redo物理复制。Redo日志记录的是页面物理修改记录,redo复制是在engine层进行的,备库apply redo日志不需要查找就可以直接定位到页面,在内存中完成页面的修改,因此复制速度快。


更重要的一点是,TDSQL-C是基于共享存储的,主备数据物理上是一致的,而binlog复制只能保证逻辑一致。


**(2)TDSQL-C 高可用-备库延迟优化**


TDSQL-C高可用另一个优化是备库延迟优化,TDSQL-C最多支持16个备库提供读服务,备库延迟可以达到毫秒级别。其中一个优化就是备库读写IO冲突优化。TDSQL-C备库是提供读服务的,同时备库也会apply主库传来的redo日志。


张远以场景举例说,首先用户读取pageA,pageA不在buffer pool中,那么会从TXStore中请求pageA,TXStore中请求pageA就存在网络和IO的开销。同时redo日志回放线程上有log1/log2/log3正在等待回放到pageA上。也就是说,用户发起的读操作可能阻塞redo日志回放线程,从而导致主备延迟。


![file](http://image.openwrite.cn/24379_61E3D09E4C7B4CD2BC13AAD5ECAEBEEE)


优化方案是将pageA上的redo日志缓存到page上的链表中,pageA IO完成以后再将链表中的redo日志回放到pageA上。这样pageA的IO过程就不阻塞redo的回放了。


**(3)TDSQL-C 高可用-独立buffer pool**


TDSQL-C 高可用的另一个优化是独立buffer pool。Buffer pool是InnoDB数据页的缓存。计算节点HA重启后,buffer pool需要重新加载进行预热,持续时间比较长,期间业务会受到较大影响。


TDSQL-C 将buffer pool从计算节点独立出来放到共享内存中,计算节点crash后buffer pool可以独立存在。这样一来,Buffer pool 不需要预热,重启时间也缩短了。


**(4)TDSQL-C 高可用-秒级RTO**


TDSQL-C在基于redo日志的架构下,计算节点crash recovery不需要apply redo,redo的apply由存储节点来完成。从而crash recovery时间相比传统MySQL要快很多。在此基础上,TDSQL-C做了更多的优化,可以做到秒级RTO。例如经过测试,大规格实例重启速度比较慢。


例如buffer pool为500G的大实例重启,初始化buffer pool耗时23秒,这对于用户来说是不可接受的。优化方案是并行初始化加pag上的mutex延迟初始化。并行初始化是指按InnoDB buffer pool instance来并行初始化。而page mutex延迟初始化,是指当page首次使用时才初始化,而不是在启动时全部都初始化。优化后buffer pool初始化速度提升近20倍,而且腾讯云将这个方案也贡献给了MySQL官方。另外回滚段并行初始化也贡献给了MySQL官方。


# TDSQL-C 之弹性扩展


TDSQL-C 备库可以提供读服务。为了提供更好的读服务,腾讯云做了许多读优化。Btree一致性读优化就是其中一个。


Btree在数据的更新过程中会发生SMO操作,即btree的分裂或合并。


![file](http://image.openwrite.cn/24379_2C34A3E49F6546918A563E20CAC21FA1)


如图所示,Btree发现分裂,page B分裂为pageB和pageC。Btree分裂时,用户查询pageB可能导致数据不一致甚至crash。但主库在Btree发生分裂时会通过index锁和page lock的方式保证正在发生分裂的page不被其他用户访问。但对于备库来说,备库通过redo日志不能感知Btree的SMO操作,SMO操作所产生的日志只有页面修改的信息,redo日志中没有index lock上锁信息。因此备库在SMO过程是没有被保护的,备库的查询可能异常。


这里有一个可选方案就是将SMO操作index lock记录到日志中,备库解析index lock日志对整个btree加index lock。但index lock会锁整个btree导致并发查询性能比较差。


![file](http://image.openwrite.cn/24379_85A8BE837E2A4FFC99B87BACFD8E4499)


TDSQL-C对此进行了优化,MySQL的SMO操作是原子的,所有产生的redo日志都在一个mini-transaction中。引入新的日志类型来标记redo日志中SMO操作的边界。这样用户在查询btree过程遇到page在SMO操作重新扫描btree即可。例如用户访问page A时会判断一下page是否在SMO,如果A在,则会在mtr start和end之间则重试。


这样优化后,备库读不会被主库更新产生的SMO操作所阻塞。


# TDSQL-C 其它特性


**TDSQL-C 秒改列(instant modify column)**


在官方MySQL8.0支持instant add column后,修改列类型操作便顺势成为MySQL中最不友好的DDL类型,修改列类型既不是inplace的同时也需要rebuild table。而在我们用户实践中,修改列类型也是用户执行比较频繁的DDL之一,而此操作会长时间阻塞用户的读写请求,对业务的影响非常大。


TDSQL-C创新的支持了instant modify column功能,达到了秒级修改列的效果。


![file](http://image.openwrite.cn/24379_C49B21D157A44AAC9F2BB7ED5CDFC95E)


具体的实现方式是:


a.元数据多版本化, 表元数据保存列的多个版本信息,用户只能看到的总是最新的表元数据。


b.行记录增加版本信息对应到不同版本的表元数据上。


c.修改列只修改元数据,修改列的过程中不修改实际的行记录。


d.行记录读取时,老版本记录会自动转换为最新版本的记录。


e.行记录更新时,老版本记录会自动更新为最新版本的记录。


![file](http://image.openwrite.cn/24379_2C20CCC07E224CE3B0DCD5903AD53E2A)


值得注意的是,这是业界首创的方案。


**TDSQL-C purge预读**


Undo 空间膨胀问题是MySQL历史老大难问题,TDSQL-C创新的通过purge预读解决了此问题。Purge会读取undo page并清理delete mark的记录,清理完成后会释放undo page,从而最终释放undo表空间。IO bound场景, purge时读取undo page更容易出现remote IO。而remote IO时占用时间比较长,导致purge不及时undo日志空间膨胀。


解决方法是实现purge预读机制:


a.根据事务提交顺序在内存中保存undo page的purge顺序用于预读。


b.Purge coordinator异步预读这些page。


![file](http://image.openwrite.cn/24379_FC7852CB5D20413C911BC4AA925A8F2C)


# TDSQL-C 展望


展望TDSQL-C的未来发展,张远表示,未来TDSQL-C会进一步加强查询优化的能力,比如增加新的join类型如SMJ, 以及在parallel query上做一些拓展。同时TDSQL在支持多写方面会进一步探索,未来TDSQL-C也会向HTAP方向演进,TDSQL-C会同时具备OLTP和OLAP的能力。