专栏首页腾讯云数据库(TencentDB)从TencentDB for MySQL到CynosDB的演进

从TencentDB for MySQL到CynosDB的演进

随着腾讯云业务高速发展以及 MySQL 生态的演进,TencentDB for MySQL迎来了最快增速的时代,通过参与开源协同,TencentDB for MySQL 团队从服务、管控、内核和架构等几个维度,不断夯实稳定性、降低成本、丰富 DBaaS 产品功能,为公司自研业务和腾讯云客户提供不断进步的 MySQL 生态数据库产品服务。

1

PartⅠ 最美年代遇到的最大挑战

随着性能与稳定性的不断提高,腾讯云数据库 TencentDB for MySQL 在近两年得到了快速发展,实例个数、行业覆盖率、业务口碑也快速正向迭代。其中,TencentDB for MySQL 内核 TXSQL 为了满足 TencentDB for MySQL 自身发展,也在性能优化、企业级特性、稳定性、强一致方面快速进化,但是尽管内核做了很多事情,有两个问题却依然存在:

(1) 因用户对数据库不合理使用造成的线上运维压力,随着实例个数、存储规模、行业覆盖率的讯速增长逐渐显露了出来;

(2)线上资源的售卖不能够充分利用机器资源,造成了一定程度的资源浪费。

我们从 TencentDB for MySQL 的常用操作来分析云数据库在运维与优化过程中存在的问题:

1. 数据库备份及恢复

TencentDB for MySQL(TXSQL) 数据库的备份分为物理备份与逻辑备份,我们分别描述一下它们的过程:

(1)物理备份

总体上说是开启两个线程分别 copy redo log  & 数据文件,当数据文件 copy 完成后,上全局读锁,阻塞写操作并获取 binlog 位点,备份非 innodb 表数据,待 redo log copy 完成后,释放全局读锁并结束复制。FTWRL在执行过程中会关闭表,如果在执行 FTWRL 的过程中有大查询,会导致FTWRL等待,进而导致DML堵塞的时间变长,如果并发不断增大,则很有可能引起实例可用连接数用完,严重时还会导致实例 hang 住;

(2)逻辑备份

逻辑备份是通过 SQL 语句的形式对数据进行备份,其过程简单描述为:执行全局读锁(FTWRL),备份 非 innodb 数据文件,获取全局 binlog 位点,设置事务隔离级别为 RR 并开启快照读(执行此语句会立即创建该事务对应的 read view,之后所有读是基于些 read view 的快照读,此时创建的 read view 和获取的 binlog 位点相对应),释放锁并备份 innodb 数据,待 innodb 表数据备份完成后即完成备份。

从物理备份以及逻辑备份的过程我们不难看出,实例数据量过大会导致备份时间较长,并在备份的过程中占用大量的 IO、CPU、内存等资源。

2. 主备数据同步

MySQL 主备之间使用逻辑日志 binlog 进行数据同步,主库事务完成后将产生的 binlog 发送给备库,备库 IO thread 将收到的 binlog 写入到磁盘,然后由 SQL thread 执行或由分发线程分发到 worker thread 后执行,从而保证主备之间的数据一致,但是基于 binlog 的逻辑复制存在着以下问题:

(1)主备数据延迟, 即 binlog 执行性能所引起的主备延迟问题。binlog 在执行过程中需要完整的 B+ 树遍历过程,并在执行的过程中产生 redo log & binlog,当主库压力大到一定程度时,备库执行性能由于并发的原因会跟不上主库的执行速度,最终产生延迟问题;

(2)数据不一致问题, binlog 内部 bug 会导致主备之间的数据不一致性,如 replace into 导致的主备 auto_increment 不一致性,repository 所导致的主备数据不一致问题, insert ... select 所导致的主备不一致问题等;

(3)大事务问题,binlog 在事务完成后才被发送到备库,备库则执行和主库相同的逻辑,执行需要的时间和主库基本相同或更多(row 模式无主建更新等),所以没有办法解决大事务所带来的延迟问题。

虽然 MySQL-5.6 及后续版本有并行复制功能,如基于 database、logical clock 或者 writeset 的并行复制来提升备库执行性能,但都没有彻底解决由于大事务或主库压力过大所导致的复制延迟问题。

3. 数据库升降级 

由于业务自身的发展,当前实例配置对 CPU、内存、磁盘空间有新的要求,现有机器可能满足不了实例对于资源的要求,此时则需要对于实例进行迁移,迁移本身则是备份+增量数据(binlog)来实现的,整个过程可以描述为:利用逻辑备份或物理备份来创建一个新的实例,实例创建完成后使用change master 命令,以备库的形式连接到主库,拉取备份所记录的 binlog,当主备没有延迟或延迟较小的情况下,设置原实例只读,保证没有延迟后,切 vip 到新的实例上面,从而完成迁移操作。

当主库压力不断或者存在延迟的情况下,迁移时间就会持续较长,影响用户体验或数据安全。

4. 数据库崩溃恢复

MySQL 崩溃恢复过程简单描述为:从上一次 checkpoint 点开始执行 redo log,扫描最后一个 binlog 文件,收集此文件中 xid 事务集合,根据回滚段重构读写事务链表(详情参考:trx_list_init_at_db_start),根据事务的状态,对于未开始的事务执行回滚操作,对于处于 prepared 的事务,如果收集的 xid 集合中包含事务的 xid ,则提交事务,否则回滚事务;

当写压力较大时,崩溃恢复的过程持续时间较长,导致 RTO 较长,影响可用性;另外,备理备份也需要 apply redo log ,当 redo log 的量较大时,影响 RTO & RPO。

5. 数据库性能优化

写性能方面,MySQL 在事务提交时,先写 redo log,再写 binlog ,最后提交事务。一次事务提交涉及多个 IO (如 redo log, binlog, 刷脏等),binlog 是串行化提交,顺序写 binlog,虽然引入了 binlog 的 group commit (flush binlog stage, sync binlog stage, commit trx stage,具体参考:ordered_commit), 但很容易成为 bottleneck ,影响整体性能;TXSQL 的性能优化在 redo log, 刷脏、binlog 方面做了相当多的工作,如 redo log 的 group commit, 并行写 redo log, 异步刷 redo log, 独立刷脏线程、owned gtid 的预先分配优化、binlog rotate 优化算法等。做了这些工作后,虽然系统性能已远超官方,但是仍然无法充分利用系统资源,单机写性能也存在着天花板。

读性能方面,备机基于逻辑日志(binlog)的复制,无法深入到存储层做更多性能优化,遇到大事务回放会产生高延迟;同时逻辑复制对数据库读写模型为:用户读数据、回放线程写数据,而在innodb层的事务上无法做到对此感知,读写事务冲突像主机一样严重,性能瓶颈明显,基于逻辑日志的回放束缚住了性能的手脚。

从以上的分析可以看出,传统云数据库存在着以下几个问题:

(1)弹性扩展能力弱,资源浪费严重

当业务需要扩展只读结点时,只能通过备库 + 追加 binlog 的方式进行扩展,时间的长短取决于数据量的大小、增量 binlog 的多少以及备库回放 binlog 的速度;另一方面,大多数业务对于CPU、内存、磁盘中磁盘需求量较大,导致现有机器资源在磁盘售卖完成后,还有较多的 CPU & 内存资源,不利于整体资源的售卖,造成了资源浪费的问题。

(2)稳定性弱,RTO、RPO 不可控

MySQL 通过逻辑日志 binlog 实现数据同步,当主库压力较大或出现大事务时,备库容易出现延迟,当主库出现问题,备库存在延迟,则会造成实例不可用,影响实例可用性。

(3)一次操作多次 IO,如 binlog , redo log ,异步刷脏等 IO操作,且串行化写 Binlog 也是系统很大的瓶颈。

从上面的分析可以看出,本地磁盘容量限制 & 备库回放 binlog 的速度是影响 RTO & RPO & 扩展能力 以及资源售卖的关键,为了彻底解决磁盘空间&binlog 回放速度问题,TEG云架平和CSIG云产品部数据库联合团队实现了云原生自研数据库 CynosDB。

1

Part Ⅱ CynosDB横空出世

为了解决传统数据库领域的诸多难题,提升云数据库产品竞争力,CynosDB 在千锤百炼中淬炼出炉。

CynosDB 是TEG云架平和CSIG云产品部数据库联合团队自研的基于 log is database 的 share disk 的云原生分布式数据库产品,log is database 是 CynosDB 的典型特征。CynosDB 的主要目标有以下几个:

(1)资源池化,有效提升公有云售卖比;

(2)提升稳定性,彻底解决 RTO、RPO 问题;

(3)按需收费,极致的弹性扩展能力;

(4)极致的性能与功能体验,提升产品性价比;

简单的说,CynosDB 分为计算层与存储层两大部分,通过 redo log & 相应的消息协议协议进行沟通,共同提供计算服务。计算层不具备状态,可以线性扩展,最大支持 15 个备库只读节点;存储层最大存储空间为 128T,可以满足绝大多数业务的需求。

本文主要从计算层的角度来分析 CynosDB 如何解决传统数据库存在的问题,以及 CynosDB 的计算层 (TXSQL) 内部实现的细节。

 CynosDB 整体架构

CynosDB 分为计算层与存储层,计算层有以下特征:

(1)计算层没有本地数据文件、临时表文件、表定义文件、redo log 文件,即卸载本地 IO;

(2)计算层不再产生 binlog,只产生数据同步的 redo log,并将 redo log 发送至存储层 & 备库;

(3)计算层接收并处理用户请求,将语句处理结果返回给客户端;

(4)计算层不再进行 checkpoint, 不需要刷脏,所以没有 redo log 产生过快所导致的同步 checkpoint, 异步 checkpoint,同步刷脏,异步刷脏等行为;

(5)计算层在 crash recovery 时不需要 apply redo log 部分,只需要回滚未完成的事务;

(6)备机将收到的 redo log 仅作同步内存使用,apply 过程中不产生 redo log;

(7)主库的 Purge, 备库的 MVCC 行为均发生变化。

存储层主要特征为:

(1)存储层收到备库下推的 redo log,可以并行 apply redo log 到相应的 page;

(2)存储层根据计算层请求页面时所指定的 LSN 返回不同版本(LSN)的 page,即 page 多版本功能;

(3)存储层负责数据持久化功能;

(4)存储层根据计算层下推的 recycle 来进行数据空间的管理。

计算层分主结点与备结点,主结点接收读写请求,备结点只接收读请求,备库不产生 redo log, 只接收主库生成的 redo log 做 buffer pool & global read view & DDL 的同步使用。

1. CynosDB 如何解决传统数据库存在的问题

(1)弹性伸缩,资源充分利用

CynosDB 将计算与本地存储进行解耦,计算层负责SQL 语句的执行操作,存储层负责数据页的存储与数据多版本的读取操作,计算结点将CPU、内存资源的售卖与存储资源的售卖分开,进而可以充分利用单机资源,用户则可以进行按需购买,不存在资源在传统云数据库中资源浪费的情况。

(2)高可用、高可靠,极大缩短RTO、RPO

CynosDB 中的主备数据同步、回档、数据恢复都是基于 redo log 来进行处理的。redo log 直接操作数据页,不存在查询 B+ 树定位数据的过程,且并发度高,回放速度快,因此可以解决由于主库压力过大导致的主备延迟问题。整个过程中不产生 redo log & binlog ,即不产生本地IO;区别于 binlog 的产生,redo log 边执行边产生,不需要等待事务结束再进行发送,因此可以彻底解决大事务、DDL 所导致的主备延迟问题。

(3)redo log复制,性能大幅提升

伴随着计算存储的解耦,CynosDB 摆脱 了 binlog 的束缚,复制协议从逻辑日志升级为物理日志(redolog), 在写性能方面, 逻辑日志(binlog) 所带来的性能瓶颈不复存在,取而代之的是内核中相关锁资源的优化,经过内核团队的相关优化,本地 CynosDB 的纯写性能是打开 binlog 性能的两倍左右,极大的提升了单机性能。在读性能他方面,重构、优化了备机事务模型,做了大量的索引优化,备机的读性能得到大幅提升,读性能向数据库只读模式(read only)靠拢,而同时主备间延迟降低到毫秒级别,彻底摆脱了主备之间大事务大时延的问题

2.  TXSQL千锤百炼的过程

CynosDB 主要是由计算层与存储层组成,由于篇幅有限,本文主要阐述 CynosDB 计算层(TXSQL)千锤百炼的部分过程。

2.1)CynosDB - 卸载本地文件系统

卸载本地文件系统主要是指将本地数据存储到网络云盘中,通过增量数据 redo log & 原始数据来实现数据的存储,为了实现这个目的,数据库内核团队做了以下事情:

(1)本地异步 AIO 转化为网络异步 RIO,实现网络异步 RIO 相关接口;  

(2)元数据信息的转换,添加额外数据字典表,将 frm, triggger, view, opt, directory 等文件下推至 innodb 进行存储,从而移除本地数据字典信息文件;

(3)数据文件的持久化,计算层对于非临时表没有刷脏操作,没有 double write buffer (之所以有 double write buffer 的行为来防止 partial write,其根本原因是 MySQL 的 redo log 并不全是 Physical 的,是Logical & Physical 相结合的), 当然也没有 checkpoint 的相关操作,事务 commit 时,只需将 redo log 发送到存储端,由存储端对数据文件 apply redo,进而构造数据页,然后进行存储;

(4)本地临时表的处理,临时表是用户线程或者系统线程在执行过程中产生的数据文件,临时表的操作过程中不产生 redo log,TXSQL 在操作临时表的过程中直接将数据文件下推至临时表空间进行存储,不涉及 redo log 的 apply,在系统重启过程中会对临时表空间进行重建操作;

(5)建立索引的处理,MySQL-5.7 在添加二级索引的过程中采用了新的算法,该算法建立 B+ 树时,从底向上建立 B+ 树,且为了提升效率,不产生 redo log, 为了实现计算与存储分离的架构,TXSQL for CynosDB 版本中添加了页面建立的 redo log,从而保证了数据的完全性;

(6)……

2.2)CynosDB - 主备内存同步

CynosDB 中,备库没有自己独立的数据,和主库共同使用同一份数据,主备之间使用 redo log 来同步 buffer pool,其详情可以参考下图:

备库根据接收到的 space_no & page_no 分配到不同的 apply 线程,apply 线程对 redo log 进行apply,当此 redo log 在内存页时,则进行 apply ,否则丢弃此 redo log,整个过程一个相对比较复杂的过程,因为要处理以下几个问题:

(1)在提升apply 的速度的同时,如何保证系统线程不阻塞用户线程的读取;

(2)apply 的过程中可能会涉及 B+ 树的分裂操作,如何保证用户线程在读取 B+ 树的过程所读取的数据页有效,即 physical consistent read;

(3)用户线程在读取数据时,当所要读取的数据页不在 buffer pool 时,需要向 TXStore 读取相关版本的数据面,此时会将 {space_no, page_no, LSN} 下推至存储端,由存储端返回相应版本的数据,而 redo log 的 apply 是 batch apply 的,那么 LSN 的读取是以这些 redo log 开始的 batch start_lsn 还是结束 batch end_lsn 呢,如果是以 batch start_lsn 读取,则有丢失 redo log 的问题,如果按 batch end_lsn 来读 page,就不会丢失更新,但是需要保证提前读入buf的 page 不应该再 apply 此 batch 中的 redo log;

(4)由于 MVCC 的存在,如何保证相关修改的 undo log 先于修改本身产生的 redo log 先执行,先执行系统 undo,再执行数据页的 redo log,但是这样的批量apply 对于用户读线程有一定的阻塞行为,将会导致用户线程等待 undo log 执行完释放数据页,进而导致 TXStore 中 redo log 的堆积,影响稳定性。

其它问题就不一一列举了,TXSQL 在 apply 线程的过程中使用了双缓冲区、无锁技术、page register、索引分裂算法等技术,并在不断的分析与调试的过程中一步一步的解决了各种问题,极大的提升了 apply 速度,彻底解决了主备延迟问题。

2.3)CynosDB - MVCC

MVCC 是多版本读(multi version concurrency control)的简称,  MVCC 详细原理可以参考本公众号之前分享的文章《InnoDB MVCC 详解》。MVCC 的实质是读写事务链表,即产生事务 ID 的事务,有了写事务,才有数据多版本一说,在CynosDB 中,主库接收读写事务请求,备库接收只读请求,主库 MVCC 在实现过程中不需要改变实现,备库的 MVCC 则需要依赖主库所产生的事务日志来构造备库自身的 readview,进而将计算的全局 read view 赋值给用户线程,用户线程根据自身的 read view 判断数据版本的可见性,主库 MVCC & 备库 MVCC 之间关系可以用下图描述:

在实现的过程中需要保证库收到 start & commit 的日志与主库产生的顺序严格一致,否则会造成读脏数据以及读已 Purge 数据的问题。

2.4)CynosDB - Purge

Purge 用来回收空间,即从 undo log 中查找并删除打上 delete mark 的数据,然后回收 undo log 空间本身,该操作只会删除不可能使用到的数据,所以 purge 操作会从 read view 列表中找到一个最老的 read view ,以该 read view 的 low_limit_no 为基准来判断数据是否可以删除。在 CynosDB 中,主库 & 备库使用同一份数据,所以,purge 不仅仅依赖主库最老的 read view,而且依赖所有备库中最老的 read view。只有主库进行 purge 操作,备库不进行 purge 操作。

2.5)CynosDB - 性能优化

数据库内核团队在对 CynosDB 计算层进行写优化的过程中遇到了以下几个问题: 

(1)CynosDB 中对于数据页的读取与写操作所产生的 redo log 的发送都是网络 IO,相比于本地 IO,网络延迟要高一到两个数量级,RT 增大,性能不及预期;

(2)把 binlog 关闭后,系统瓶劲直接到了全局事务系统,即 trx_sys->mutex;

(3)主库执行 DDL 的同时,备库有可能会访问到主库被存储 free 掉的数据页,如果保证备库读的有效性;

(4)其它问题。

为了解决上述问题并进行相应的读优化,内核团队做了以下几个优化:

(1) thread pool 的异步组提交,增加单位时间吞吐量,提升读写性能;

(2)并行 copy redo log,降低占用 log_sys->mutex 的上锁时间,提升并发性;

(3) 事务系统优化,解决 trx_sys->mutex 瓶颈问题;

(4) 异步 DDL,保证 DDL 在主库操作的同时,不影响备库的读操作;

(5) 其它如异步唤醒操作的无锁队列、异步刷 redo log、备库 read view 等优化。

在做了上面的优化之后,单机 CynosDB 写性能可以突破到 25W 左右,一主一从的读版本中, 备库 oltp_read_only 达 100W+。

2.6)CynosDB – 正确性验证

CynosDB 中 Log is data,计算层将 redo log 下推至存储层(TXStore),TXStore apply 这些 redo log 然后进行数据存储,计算结点不需要刷脏,没有 checkpoint 等相关操作,备库使用 redo log 来同步内存 & read view & 锁相关信息,那么如何处理以下两个问题:

(1) 如何保证 IO 的正确性,即计算层中的数据与 TXStore apply 之后的数据一致;

(2) 主备共用同一份数据,redo log 只同步内存,如何验证备库 MVCC 的有效性以及读操作的正确性。

为了解决数据一致性问题,内核团队对页面以及 redo log 进行了特殊处理,将页面的 checksum, lsn 等信息也一并下推至存储层,存储层 apply 日志后会进行相应的比对,从而保证数据的可靠性,为了验证备库读的有效性以及正确性,数据库内核团队做了以下三个事情:

(1) 将 tpcc 的交易验证逻辑提取到了 CynosDB 的测试过程中,验证备库读的有效性;

(2) 根据数据库隔离级别,自定义 lua 脚本,从原理上保证了备库读数据判断逻辑的正确性;

(3) 从内核上进行测试,保证了主库任意 SQL 语句在备库中执行结果与主库执行结果相同。 

2.7)CynosDB – 添加只读结点

CynosDB 中主备共享一份数据,添加只读结点所消耗的时间是秒级的,其详细过程如下图所示:

1

Part Ⅲ 加入我们

数据库是一个很有吸引力的领域,数据库内核研发和算法、操作系统、文件系统、体系结构等有着很多的联系,如果你对数据库有着浓厚的兴趣、对数据库的原理比较清楚、又有着比较好的 C 或 C++ 的基础,最重要的,你有挑战各种困难的勇气与强烈的责任感,那么你就是我们要找的人。关注“腾讯云数据库”公众号,回复“招聘”即可查看招聘信息。

DBbrain诊断日 

第二期DBbrain诊断日就要来啦~~1月12日(本周日)20:00,小编将在分享群内图文直播分享「数据库库表中的细节设计-数据类型相关的案例」。在MySQL运维的工作中,大家往往会把精力集中在如何优化慢SQL、如何设计数据库架构上,对于库表设计往往都比较随意。其实良好的数据库逻辑设计和物理设计才是高性能的基石,在对于schema的设计中要求大家既要关注全局,也需要关注细节。通过本次分享,会让大家以一个全新的视角去回顾自己的MySQL知识体系,也会让大家发现一些网红资料的错误,避免在工作中重蹈覆辙,利用DBbrain来解决数据库运维的疑难杂症。

还没进群的小伙伴,请按照下方图片提示,火速入群~

年终回馈 

↓↓更多惊喜优惠请点这儿~

本文分享自微信公众号 - 腾讯云数据库(TencentDB)

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2020-01-09

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Redis:我承载了上千万人的火影青春

    作者:李世顺,腾讯游戏天美J1工作室游戏后台高级工程师,先后参与过剑灵手游、火影忍者手游、写实赛车项目的研发与维护工作。 ---- 火影忍者手游已经上线4年...

    腾讯云数据库 TencentDB
  • MySQL 8.0源码学习日记——redo log的一生

    | 作者:杨一迪,腾讯云数据库后台开发工程师,主要负责云数据库postgresql、云数据库CynosDB等产品的后台开发工作。 1 前言 1 最开始了解my...

    腾讯云数据库 TencentDB
  • 关注这些腾讯公号,助你走上人生巅峰(送价值万元的福利)

    来了?鹅厂小编们等你很久了!咱们闲话少叙,今天,10位小编携手为你奉上10份超级大礼:

    腾讯云数据库 TencentDB
  • 为什么 redo log 具有 crash-safe 的能力,是 binlog 无法替代的?

    昨天在复习 MySQL 日志相关的知识,学的东西过一段时间后就会遗忘,遗忘后再重新思考,往往会有新的收获。想到几个问题,把它记录下来。

    超超不会飞
  • MySQL深入学习第二十三篇-MySQL是怎么保证数据不丢的?

    今天这篇文章,我会继续和你介绍在业务高峰期临时提升性能的方法。从文章标题“MySQL 是怎么保证数据不丢的?”,你就可以看出来,今天我和你介绍的方法,跟数据的可...

    越陌度阡
  • MySQL Update语句是怎么执行的?

    最近在极客时间看丁奇大佬的《MySQL45讲》,真心觉得讲的不错,把其中获得的一些MySQL方向的经验整理整理分享给大家,有兴趣同学可以购买相关课程进行学习。

    AsiaYe
  • MySQL实战问题02 mysql是如何保证数据不丢失的

    fa只要保证redolog 和 binlog 持久化到磁盘, 就能保证mysql异常重启后, 数据可以恢复.

    历久尝新
  • MySQL基础篇2 mysql的日志系统

    我们还是从一个表的一条更新语句说起,下面是这个表的创建语句,这个表有一个主键 ID 和一个整型字段 c:

    历久尝新
  • MySQL(二)日志系统

    MySQL可以恢复到半个月内任意一秒的状态. mysql> create table T(ID int primary key, c int);

    JNJYan
  • 基于Docker的Redis高可用集群搭建(redis-sentinel)

      之前介绍了用docker来搭建redis主从环境,但这只是对数据添加了从库备份(主从复制),当主库down掉的时候,从库是不会自动升级为主库的,也就是说,该...

    happyJared

扫码关注云+社区

领取腾讯云代金券