为帮助开发者更好地了解和学习分布式数据库技术,2020年3月,腾讯云数据库、云加社区联合腾讯TEG数据库工作组特推出为期3个月的国产数据库专题线上技术沙龙《你想了解的国产数据库秘密,都在这!》,邀请数十位鹅厂数据库专家每周二和周四晚上在线深入解读TDSQL、TBase、CynosDB三款鹅厂自研数据库的核心架构、技术实现原理和佳实践等。三月为TDSQL专题月,本文将带来直播回顾篇《腾讯自研分布式数据库TDSQL核心架构及特性拆解》。
分享分为五个部分:
TDSQL是什么:腾讯如何打造一款金融级分布式数据库
我们先初步了解TDSQL产品,以及它的适用场景。章包括四个方面:使用场景、发展历程、核心特性,以及兼容性。
首先,TDSQL是腾讯推出的一款兼容MySQL的自主可控、高一致性分布式数据库产品。这里我们强调一点,高度兼容MySQL——TDSQL完全兼容MySQL协议,并且做到完全自主可控、数据强一致性。第二是TDSQL具备分布式的特性,具备一个弹性扩展、高可用的架构。在互联网行业,海量的用户流量场景很常见,如果数据库不具备可伸缩性、可扩展性,是很难应对如:电商的大型促销,春节抢红包等突增流量的场景,这些其实都是对数据库应对海量用户流量的考验。
目前TDSQL已经服务超过500+的金融政企,行业覆盖银行、保险、证券、政务、互联网金融等各个领域。
我们再看一下TDSQL的前世今生。TDSQL早可以追溯到2002年,那个时候其实还不叫TDSQL,它是腾讯计费平台部的一个数据库服务,当时使用了开源的MySQL。2002年-2007年随着公司业务的发展,腾讯所面临的用户量的压力也越来越大。这个时候我们提出了7×24小时不宕机的高可用设计方案,来保证数据库能提供7×24小时不间断连续高可用服务。那个时候,腾讯的增值业务日渐成规模,业务对数据也越来越敏感,对数据可用性的要求越来越高,甚至平时还要防备一些像运营商的光纤被挖断等各种各样的异常场景。
在2007年-2012年,这可能是互联网时代从互联网到移动互联网的发展的快速5年。当然,公司的业务也是突飞猛进。我们开始把这个高可用的数据库产品化。到2012年,TDSQL的雏形就已经出来了,作为一款内部产品,开始在公司内部提供金融级的数据强一致性、可靠性服务。
从2012年起,TDSQL已经在腾讯内部做得已经比较成熟,已经是一个知名的产品了,但是它一直没有对外做商业化。2014年恰逢一个很好的机会——微众银行的成立。微众银行做数据库选型的时候关注到了TDSQL,经过反复测试验证,发现当时的TDSQL已经完全具备了微众银行对数据可用性和一致性的要求。借此机会,TDSQL成功在微众银行投产,成为微众银行的数据库,覆盖了银行的核心业务。
所以说2014年,TDSQL完成了商业化,也实现了私有化部署。2014年以后,TDSQL推广到了很多银行、金融机构,这过程中是借鉴了2014年TDSQL在微众银行成功实施的宝贵的经验。
因为在2014年微众银行的部署中,我们也踩了很多坑,也认识到在私有化部署环境的各种各样的挑战,并一一攻克了这些挑战。当2014年在私有化部署完成之后,再到2015年TDSQL上公有云,我们继续通过公有云服务打磨自己的产品。
所以从2012年作为一个内部产品到2014年的私有化部署,再到2015年公有云上的部署,TDSQL已经逐步从一个内部产品逐渐走向行业,成为一个正式对外的商用数据库。从2015年到2019年,TDSQL已经推广到许多银行和金融政企。但是很重要的一点是,虽然服务了很多银行、金融客户,但是在银行领域有一块比较难动的蛋糕叫银行的传统核心系统。传统核心系统数据库长期以来一直是被国外的商用数据库所垄断,比如说ORACLE、DB2啊,像TDSQL这类分布式数据库是很难介入的。
2018年,我们关注到张家港银行有更换核心系统的需求,就此建立联系并成功达成合作,终,2019年,我们将腾讯这套分布式数据库系统成功应用到了张家港银行的传统核心系统。张家港行也是作为家传统核心系统上分布式数据库的银行,分布式数据库不再是只局限于银行的互联网核心、互联网银行等外围系统的尝试,而是真真正正切入到银行系统的心脏—传统核心,这也是国产数据库领域一个具有里程碑意义的事件。
所以在未来,我们也将继续“走出去”深入到更复杂、更新核心的业务系统,打磨我们的产品。
这是TDSQL的发展历程。
TDSQL核心特性:极具挑战的“四高”服务与安全可运维
我们再看TDSQL的核心特性。
首先作为适应于金融场景的数据库,数据强一致性是立命之本,因为数据不能丢、不能错。在金融场景,你没有办法去估量——假如错一条数据,到底这条数据是1分钱还是1个亿,所以数据强一致是我们根本的一个特性。不允许丢,不允许错,这是对数据库起码的要求。
第二是金融级高可用。TDSQL确保99.999%以上的可用性,并支持跨IDC、多机房、同城多活的部署方式。我们先切入金融场景是因为金融场景的挑战是大的。中国金融行业受监管要求为苛刻,同时也对数据和业务的可用性、可靠性、一致性更是有极高的要求。我们要求99.999%的可用性,也就是说这个数据库全年故障的时间不能超过5分钟。
第三是高性能、低成本。互联网时代的企业,都是海量业务、海量机器,性能稍微提高一个10%,可能就节约成百上千台机器的成本,这个经济效益还是比较大的。所以高性能、低成本也是TDSQL的一个关键特性。
第四点是线性水平扩展。因为无论是互联网还是其他企业,随着数字化的发展,比如说出现突增流量,搞个活动等,现在单机的承载量越来越容易凸显出瓶颈。所以我们提出这种水平线性扩展的能力,要求它可进行水平伸缩。可能一台机器的负载、硬盘、机器资源容纳不了,但可以把它拆到多台机器,不需要考虑太多,它可以自动地提高自身吞吐量和负载量。
第五点是企业级安全。金融数据是敏感的,一些敏感的金融数据需要在当前数据基础上再做一层更别的企业安全防护,比如数据库防火墙,以及透明加密,等等。
第六点是便捷的运维,私有化部署中,很多情况下其实他们的网络环境和部署环境跟我们是隔离的,如果银行客户有问题,那其实我们时间是切入不了去帮助解决的,所以就需要一套完善的配套设施,简单容易上手,可以自动帮用户去定位问题、解决问题,同时也尽量减少运维的复杂度。
TDSQL核心架构
接下来我们了解TDSQL的架构以及模块划分。通过这一章节的了解,我们更能切入TDSQL的技术细节,它为什么要这样设计,这样设计有什么好处,如何通过这样的架构和设计实现高可用、线性扩展等能力。
TDSQL系统总览
资源池
这张图从下往上看,首先底层是资源池,属于IaaS层服务,可以是物理机,也可以是虚拟机,只要是给TDSQL添加机器就好,TDSQL是在一个机器的资源池上实现了数据库实例的管理。当然,这里推荐的还是物理机,如果增加一层虚拟机服务,无疑在稳定性和性能方面都会引入一些隐患。
存储节点
从资源池再往上是存储节点。存储节点要强调的是TDSQL的两种存储形态,一种是Noshard数据库,一种是分布式数据库(也叫Shard版TDSQL)。简单来说,Noshard就是一个单机版的TDSQL,在MySQL的基础上做了一系列的改造和改良,让它支持TDSQL的一系列特性,包括高可用,数据强一致、7×24小时自动故障切换等。第二种是分布式数据库,具备水平伸缩能力。所以TDSQL对外其实呈现了两种形态,呈现一种非分布式形态,一种是分布式的形态。至于这两种形态的区别,或者说什么场景更适合于哪种数据库,后面我们有专门的章节去分析。
计算节点
再看计算节点。计算节点就是TDSQL的计算引擎,做到了计算层和存储层相分离。计算层主要是做一些SQL方面的处理,比如词法解、语法解析、SQL改写等。如果是分布式数据库形态,还要做分布式事务相关的协调,所以我们看到计算层不存储数据,只运行SQL方面的实时计算,所以它更偏CPU密集型。此外,TDSQL计算节点还具备OLAP的能力,对一些复杂的计算可以进行算法上的优化——什么时候该下推到存储引擎层,什么时候需要在计算层做汇总等,这是计算节点需要做的事情。
赤兔运营管理平台
再往上,是赤兔运营管理平台,如果说把下面这一套东西比作一个黑盒,我们希望有一个用户界面操纵这个黑盒,这个界面就是赤兔运营管理平台。通过这个平台,DBA可以操纵TDSQL后台黑盒,所以相当于是一套WEB管理系统。让所有DBA的操作都可以在用户界面上完成,而不需要登陆到后台,不需要关心计算节点是哪个,存储节点是哪个,或者怎么样管理它,要加一些节点或者减一些节点,或者把这个节点从哪里要迁到哪里……这些都可以通过界面化完成。DBA操作界面不容易出错,但如果登陆到后台很容易一个误操作,不小心把机器重启了,就可能会造成一定的影响。
“扁鹊”智能DBA平台
有了赤兔之外,为什么还有一个“扁鹊”智能DBA平台呢?可能正常情况下,我们机器是好的,但是,机器如果发生了故障,或者说哪天磁盘有坏块了,或者是IO性能越来越差……SSD其实有一个衰老的过程,到了后期的话,吞吐量和IOPS可能会有一定下降,导致数据库的响应速度变慢。这种情况如果DBA要排查,得先去看到是哪一个实例、涉及到哪一台机器、这个机器有什么问题、检测机器的健康状态……这些都是机械性的工作,有了扁鹊智能管理平台,当出现故障的时候就可以自动分析故障的原因,举个例子,可以找出是因为什么导致SQL变慢了,或者又是因为什么原因发生了主备切换,突然IO异常了或者其他什么原因导致机器故障。
此外,扁鹊智能DBA平台还有一个智能诊断系统,可以定期由DBA发起对实例进行的诊断。比如有些数据库实例,CPU常年跑得很高,其实是一些比较差的SQL导致的。这个时候扁鹊智能DBA系统,可以很方便地到用户实例上做巡检,得到一个健康状况图,并对它进行打分,发现这个实例比如他的CPU超用了,需要扩容,但是没有扩容,就会减分;然后其他表的索引没有建好,要减分……以此生成一个诊断报告。所以,有了扁鹊,再加上赤兔运营管理平台,DBA的工作其实是非常轻松的,可能每天只需要点几下按纽,然后就解决了一系列的麻烦,包括高可用,性能分析,锁分析等,完全把DBA从繁杂的工作中解放出来。
此外,我们看到这里其实还有几个小的模块。调度系统,调度系统主要是负责整体的资源调度,比如说数据库实例的增加删除、过期作废,还有一些容量的调度,即扩容、缩容,还有一些多租户的管理。也就是说这是整个管理台的调度器。
另外还有一个备份系统,这个是冷备中心,后面有一个专门的章节去讲,这里就不再赘述。此外,我们还提供了一些服务模块作为辅助,比如审计,还有数据库之间的迁移服务——我们TDSQL怎么能够帮助异地数据库迁进来,或者从TDSQL再迁出。此外,还包括数据校验、数据订阅、SQL防火墙、注入检测等安全方面的模块,以及一个辅助模块——帮助我们的DBA也好,用户也好,完成一些个性化的丰富的需求。
以上是TDSQL系统总览。
TDSQL架构模块及其特性
我们再看一下核心架构。核心架构其实是上一个图的缩览,我们把核心的模块挑选出来。
首先用户的请求通过负载均衡发往SQL引擎。然后,SQL引擎作为计算接入层,根据这个SQL的要求从后端的存储节点去取数据。当然,无论是SQL引擎还是后端的数据库实例都存在一个元数据来管理调度。举个例子,计算引擎需要拿到一个路由,路由告诉SQL引擎,这个SQL该发往哪一个后端的数据节点,到底是该发往主节点还是发往备节点。所以我们引入了ZK(Zookeeper)来储存类似于路由这类元数据信息。当然ZK只是静态的存储元数据,维护和管理这些元数据信息,还需要有一套调度以及接口组件,这里是OSS、Manager/Schedule。所以我们这张图可以看到是TDSQL整体来说就分为三部分:管理节点、计算节点和存储节点。当然这里还有一个辅助模块,帮助完成一些个性化需求的,比如备份、消息队列,数据迁移工具等。另外,这里的负载均衡其实不是必需的,用户可以选用自身的硬件负载,也可以用LVS软负载,这个负载均衡根据实际的用户场景可自定义。
了解了整体架构以后,我们继续再看一下每个节点的特性是什么、对机器的依赖程度如何,要求机器有哪些特性,等等。
管理模块:轻松通过web界面管理整个数据库后台
首先,我们要看的是管理模块。作为一个集群只搭建一套的管理模块,一般可以复用一组机器。同时,管理模块对机器的要求相对来说比较低,比如资源紧张的时候,我们用虚拟机就可以代替。在我们内部,一套管理模块承载大的管理单集群近上万实例。
管理模块包含前文说的几个关键模块:Zookeeper(ZK)、Scheduler、Manager、OSS和监控采集程序、赤兔管理控制台。那么它们是怎么联合工作的呢?首先,DBA用户在赤兔管理台——这一套WEB前台发起一个操作——点了一个按纽,这个按纽可能是对实例进行扩容,这个按纽会把这个https的请求转移到OSS模块,这个OSS模块有点像web服务器,它能接收web请求,但是它可以把这个转发到ZK。所以,OSS模块就是一个前端到后台的桥梁,有了OSS模块,整个后台的工作模块都可以跟前台、跟web界面绑定在一起。
好,捕捉到这个请求之后,在ZK上创建一个任务节点,这个任务节点被调度模块捕获,捕获之后就处理任务。处理完任务,再把它的处理结果返回到ZK上。ZK上的任务被OSS捕获,后也是https的请求,去查询这个任务,后得到一个结果,返回给前端。
这是一个纯异步的过程,但是有了这套管理模块,让我们可以轻松的通过web界面去管理整个TDSQL的后台。当然,这整个过程都有一个监控采集模块去采集,对整个流程的审计及状态进行获取。
DB模块:数据库无损升级
DB模块,即数据节点,数据存取服务属于IO密集型的服务,因此,数据节点也是我们的存储节点,它对IO的要求比较高,一般建议配置SSD硬盘,好是PCI-E的SSD。因为对数据库服务来说,CPU再高,如果IO跟不上,仍然是小马拉大车。比如只有1千的IOPS,CPU根本就跑不起来,用不起来。所以这里一般建议至少IOPS要达到1万以上。
我们再看一下SET的概念。SET就是数据库实例,一个SET包含数据库的——比如我们默认要求的是一主两备,一个Master节点和两个Slave节点。当然在DB节点上有一个Agent的模块。MySQL在执行中,我们要监控它的行为,以及进行操作。如果把这些东西做到MySQL里面为什么不可以呢?这其实存在一个问题,如果对数据节点进行升级,可能就要涉及到重启,一旦重启就影响用户的业务,影响服务。这个时候我们就考虑,在它上面加一个模块Agent,它来完成对所有集群对MySQL的操作,并且上报MySQL的状态。有了它之后,对TDSQL数据节点的大部分升级,都会转变为对Agent的升级,而升级Agent,对业务没有任何影响,这就实现了无损升级。相比于Agent我们对数据节点MySQL不会频繁升级,一般情况下一年、半年都不会动它。这是我们DB模块,也是存储节点。
SQL引擎模块:分布式复杂SQL处理
接下来再看另外一个比较重要的模块:SQL引擎模块。SQL引擎处于计算层的位置,本身属于CPU密集型,所以我们在选机型上尽量要求CPU高一些。其次是内存,作为计算接入层,它要管理链接,如果是大量的短链接或者长链接,非常占内存,所以它对CPU和内存的要求比较高。此外,它本身不存储数据,也没有主备之分,所以对硬盘没有太大要求。
我们看一下SQL引擎的特性。SQL引擎首先还是从ZK上拉取到元数据,作为SQL引擎,包括权限校验、读写分离,以及统计信息、协议模拟等相关的操作。
可能有些人会问,其实这个SQL引擎岂不是一种中间件?其实并不是这样,SQL引擎如果是一个中间件,它都可以脱离MySQL。但是我们这个SQL引擎,需要做词法、语法分析,以及作为查询引擎等工作。而且在分布式的场景下,SQL引擎复杂的功能性就会凸显,比如要处理分布式事物,还要维护全局自增字段,保证多个数据、多个存储节点共享一个保证全局自增的序列;如果是分布式的话,要限制一些语法,包括词法和语法的解析;还有在一些复杂计算上,它还要做一些SQL下推,以及后数据的聚合。所以SQL引擎还是一个相对来说比较复杂的模块,作为计算层,并不是一个简单的中间件那么简单。这就是一个SQL引擎。
TDSQL金融级特性之:数据强一致性保障
前面我们了解了TDSQL的整体架构和核心特性。接下来我们要重点聊一聊它重要的特性——作为金融场景下不可或缺的数据强一致性的保障。我们将从四个方面来聊一聊数据一致性的保障:
- 主备数据复制方式
- 数据复制比较:TDSQL主备数据复制方案 VS MySQL原生方案
- 核心功能:容灾切换,数据强一致、0丢失0出错
- 数据强一致性
TDSQL主备数据复制:高性能强同步
首先在讲数据一致性之前,我们先了解一下MySQL原生的数据复制的方式。
首先种是异步复制:主机在不等从机应答直接返回客户端成功。这个在金融场景是不能接受的,这样的话相当于数据是没有多副本保障。
第二种是半同步:主机在一定条件下等备机应答,如果等不到备机应答,它还是会返回业务成功,也就是说它终还会退化成一个异步的方式,这同样也是金融场景所不能接受的。
除此之外,原生半同步其实是有一个性能方面的缺陷,即在跨IDC网络抖动的场景下,请求毛刺现象很严重。所以原生的异步复制和半同步复制都存在一些问题,并不能完全适应金融场景。
TDSQL引入了基于raft协议的强同步复制,主机接收到业务请求后,等待其中一个备机应答成功后才返回客户端成功。比如这张图,我们一主两备下一条业务请求到达了主机之后必须等其中一个备机应答成功,才能返回客户端成功,否则这个请求是不会应答的。所以说,强同步是TDSQL基础的一个特性,是TDSQL保证数据不会丢、不会错的关键。
讲到这里的话,可能有些同学会问,你们这个强同步其实也不复杂,不就是在半同步的基础上把这个超时时间改成无限大同时应答的备机设置为1。并不是这样的,TDSQL强同步这里的关键不是在解决备机应答的问题,而是要解决这种增加了等待备机的机制之后,如何能保证高性能、高可靠性。换句话说,如果在原生半同步的基础上不改造性能,仅把超时时间改成无限大的时候,其实跑出来的性能和异步比甚至连异步的一半都达不到。这个在我们看来也是无法接受的。相当于为了数据的一致性牺牲了很大一部分性能。
TDSQL强同步复制的性能是在原生半同步的基础上做了大量的优化和改进,使得性能基本接近于异步。
所以这里强同步强调的是,实现强同步的同时还具备高性能特性,所以确切地说是一个高性能的强同步。
那么我们如何实现高性能的强同步?我们继续往下看。这里TDSQL对MySQL主备复制的机制做了改造。首先,我们先引入了一个线程池的模型。
原生的MySQL是——一个互联请求是一个线程,这样对操作系统的资源消耗还是很大的:5千个连接,5千个线程。那1万个连接,1万个线程,操作系统能扛得住吗?肯定扛不住。而且原生的数据复制的方式,其实串行化比较高,比如说一个用户请求发过来后,等备机应答;这个过程中用户线程在这里是完全不能做事的,只有等备机应答之后,它才能够返回前端。也就是说大量的线程都处于一个等待的状态,这是半同步效率低的根本原因。
引入了线程池模型之后,我们还需要考虑怎么调度线程池。我们希望MySQL维护一小部分工作的线程,比如说有1万个用户连接,真正干活的可能就那么100个、50个MySQL的工作线程。如果是非工作的线程,他在等IO应答时可以先去睡眠,并不让它去影响我们的吞吐量。所以TDSQL对原生的复制做了改造,除了引入线程池模型,还增加了几组自定义的线程。这个线程是做什么呢?当一笔用户请求过来之后完成了写操作以及刷新了binlog,第二步他应该要等备机应答。这个时候被我们新引入的线程组所接管,把这个用户对话保留下来,释放了工作线程,工作线程该干什么继续干什么,处理其他的用户连接请求。真正备机给了应答之后,再由另外一组线程将它唤醒,回吐到客户端,告诉客户端这个应答成功了。
所以经过这样一个异步化的过程,相当于把之前串行的流程异步化了,这样就达到了接近于异步复制的性能,这就是TDSQL在强同步的改造的核心。所以我们这里强调不单单是一个实现强同步的过程,更是一个接近异步性能的强同步。
再看一下改造之后的性能对比:异步TPS大概是6万左右,平均时耗小于等于10毫秒。再看半同步,明显有三分之二的性能损耗,并且这个时耗波动还是比较大的,比如说IDC网络抖动一下。强同步一主两备模式下,首先性能已经接近于异步的性能,此外时耗并没有额外增加。
TPS跟场景相关,数据仅提供横向对比参考
因为一主两备,除非两个机房网络同时抖动,否则的话强同步的时耗不会有明显波动。所以我们看到基于TDSQL的强同步实现了数据的多副本又保证了性能。
有了这个多副本保障,又怎么如何实现故障自动切换,数据不丢不错呢?这其实还是需要一套切换选举的流程。这就引出了TDSQL的容灾切换功能。
自动容灾切换:数据强一致、0丢失0出错
自动容灾切换在有了强同步特性的基础,就变得非常容易实现了。我们先看一下这个结构图:
SQL引擎将请求发给主节点,主节点被两个备机所同步,每个节点上都有对应的Agent上报当前节点的状态。这时,主节点发生了故障被Agent觉察,上报到zk被Scheduler捕获,然后Scheduler首先会把这个主节点进行降级,把它变成Slave。也就是说此时其实整个集群里面全是Slave,没有主节点。这个时候另外两个存活的备机上报自己新的binlog点,因为有了强同步的保障,另外两个备机其中之一一定有新的binlog,那么两个备机分别上报自己新的点后,Schedule就可以清楚的知道哪个节点的数据是新的,并将这个新的节点提升成主节点。比如说发现Slave1的数据是新的,这个时候Schedule修改路由,把主节点设置为Slave1,完成了主备的切换。有了前述这个切换机制,保证了整个切换无需人为干预,并且切换前与切换后的数据是完全一致的。这里做一个总结,正是由于强同步的保证,所以当主机发生故障的时候,我们一定是可以从一个备节点上找到新数据,把它提成主节点。这就是TDSQL容灾切换的整个过程,容灾切换需要建立在强同步的基础上。
极端场景下的数据一致性保障
聊完了容灾切换,我们再聊一聊故障节点的后续处理事宜。故障处理可能有几种情况:一种是故障以后,它还能活过来。比如说像机房可能突然掉电了,掉完电之后它马上又恢复。或者机器因为硬件原因不小心发生了重启,重启完之后节点是可以被拉起,被拉起之后,我们希望它能够迅速的再加入到集群中,这时数据其实是没有丢没有错的,我们不希望把节点故障之后又重新建一份数据,把之前的数据全抹掉。而是从它后一次同步的数据为断点,继续续传后面的数据。带着上述问题,我们看一下节点的故障后恢复过程。
首先我们考虑一种场景,比如说A节点作为主节点,B、C是从,正常去同步A节点的数据,A+1、A+2,接下来该同步A+3。当A+3还没有同步到从节点的时候发生了故障,这个时候根据B、C节点的数据情况,C的数据是新的,因此C被选成了主节点,进而C继续同步数据到B。过了一阵,A节点拉起了,可以重新加入集群。重新加入集群之后,发现它有一笔请求A+3还没有来得及被B、C节点应答,但已经写入到日志。这个时候其实A节点的数据是有问题的,我们需要把这个没有被备机确认的A+3的回滚掉,防止它将来再同步给其他的节点。所以这里强调的是一个数据的回退,相当于每一个Slave新加入节点的时候,我们都会对它的数据进行检验,将多写的数据回滚。当然刚刚的假设是运气比较好,A节点还能重启。有些时候A节点可能就挂了,这个机器就再也起不来了,我们需要把这个节点换掉,即换一个新机器,比如:加一个D节点。我们希望它快速重建数据并且对当前线上业务尽可能无影响。如何让它快速去重建呢?当然是基于物理拷贝,而且它是从备机上去拉数据,这样它既不会对主节点产生影响,又能够快速把数据重建好。
分布式TDSQL的实践
第四部分,我们开始讲分布式TDSQL的实践。前三章我们在聊TDSQL的高可用、强一致。这些作为金融场景是一个必备的特性,还没有涉及到分布式,当涉及到分布式时就开启了TDSQL的另外一种形态。接下来我们就聊一聊分布式TDSQL跟单节点的TDSQL有什么不同,以及这种分布式架构下又是如何实现一系列的保障,同时如何做到对业务透明、对业务无感知。
分表
分表,当在单机模式下,用户看到的一张逻辑表,其实也是一张物理表,存储在一个物理节点(物理机)上。在分布式形态下,用户看到的逻辑表的实际物理存储可能是被打散分布到不同的物理节点上。所以TDSQL分表的目标,希望做到对业务完全透明,比如业务只看到一个完整的逻辑表,他并不知道这些表其实已经被TDSQL均匀拆分到各个物理节点上。比如:之前可能数据都在一台机器上,现在这些数据平均分布在了5台机器上,但用户却丝毫没有觉察,这是TDSQL要实现的一个目标——在用户看来是完全的一张逻辑表,实际上它是在后台打散了的。
这个表在后台如何去打散,如何去分布呢?我们希望对用户做到透明,做到屏蔽,让他不关心数据分布的细节。怎么将这个数据分布和打散呢?这就引出了一个概念:shardkey——是TDSQL的分片关键字,也就是说TDSQL会根据shardkey字段将这个数据去分散。
我们认为,shardkey是一个很自然的字段,自然地通过一个字段去将数据打散。举个例子,腾讯内部我们喜欢用QQ号作为一个shardkey,通过QQ号自动把数据打散,或者微信号;而一些银行类的客户,更喜欢用一些客户号、身份证号以及银行卡号,作为shardkey。也就是说通过一个字段自然而然把这个数据分散开来。我们认为引入shardkey后并不会增加额外的工作,因为首先用户是了解自己得数据的,知道自己的数据按照什么字段均匀分布佳,同时给用户自主选择分片关键字的权利,有助于从全局角度实现分布式数据库的全局性能佳。
所以这里可能有些人会想,是不是主键是好的或者尽可能地分散?没错,确实是这样的,作为TDSQL的分片关键字越分散越好,要求是主键或者是索引的一部分。确定了这个分片关键字后,TDSQL就可以根据这个分片关键字将数据均匀分散开来。比如这张图,我们按照一个字段做了分片之后,将1万条数据均匀分布在了四个节点上。
既然我们了解了shardkey是一个分片关键字,那怎么去使用呢?这里我们就聊聊如何去使用。
举个例子,我们创建了TB1这个表,这里有若干个字段,比如说ID,从这个名字上来看就应该知道它是一个不的,或者可以说是一个比较分散的值。我们看到这里,以“ID”作为分配关键字,这样六条数据就均匀分散到了两个分片上。当然,数据均匀分散之后,我们要求SQL在发往这边的都需要带上shardkey,也就是说发到这里之后可以根据对应的shardkey发往对应的分片。如果不带这个shardkey的话,它不知道发给哪个分片,就发给了所有分片。所以强调通过这样的改善,我们要求尽可能SQL要带上shardkey。带上shardkey的话,就实现了SQL的路由分发到对应的分片。
讲完数据分片,我们再看一下数据的拆分。
水平拆分
对于分布式来说,可能初我们所有的数据都在一个节点上。当一个节点出现了性能瓶颈,需要将数据拆分,这时对我们TDSQL来说非常简单,在界面上的一个按纽:即一键扩容,它就可以将这个数据自动拆分。拆分的过程也比较容易理解,其实就是一个数据的拷贝和搬迁过程,因为数据本身是可以按照一半一半这样的划分的。比如先是这么一份数据,我们需要拆成两份,需要把它的下半部分数据拷到另外一个节点上。这个原理也比较简单,就是一个数据的拷贝,这里强调的是在拷贝的过程中,其实业务是不受任何影响的。终业务只会终有一个秒级冻结。
为什么叫秒级冻结?因为,后一步,数据分布到两个节点上涉及到一个路由信息变更,比如原来的路由信息要发到这个分片,现在改了之后需要按照划分,上半部分要发给一个分片,下半部分发给另一个分片。我们在改路由的这个过程中,希望这个数据是没有写入相对静止的。
当然改路由也是毫秒级别完成,所以数据拆分时,真正后对业务的影响只有不到1s,并且只有在后改路由的冻结阶段才会触发。
讲完数据拆分,我们开始切入分布式里面难解决的这个问题,分布式事务。
健壮、可靠的分布式事务
单节点的事务是很好解决的,但是在分布式场景下想解决分布式事务还是存在一定的困难性,它需要考虑各种各样复杂的场景。
其实分布式事务实现不难,但首要是保证它的健壮性和可靠性,能应对各种各样的复杂场景。比如说涉及到分布式事务的时候,有主备切换、节点宕机……在各种容灾的测试环境下,如何保证数据总帐是平的,不会多一分钱也不会少一分钱,这是分布式事务需要考虑的。
TDSQL分布式事务基于拆的标准两阶段提交实现,这也是业内比较通用的方法。我们看到SQL 引擎作为分布式事务的发起者,联合各个资源节点共同完成分布式事务的处理。
事务也是根据shardkey来判断,具体来说,对于SQL引擎读发起一个事务,比如条SQL是改用户ID为A的用户信息表。第二条SQL是插入一个用户ID为A的流水表,这两张表都以用户ID作为shardkey。我们发现这两条SQL都是发往一个分片,虽然是一个开启的事务,但是发现它并没有走分布式事务,它实际还是限制在单个分片里面走了一个单节点的事务。当然如果涉及到转帐:比如从A帐户转到B帐户,正好A帐户在个分片,B帐户是第二个分片,这样就涉及到一个分布式事务,需要SQL引擎完成整个分布式事务处理。
分布式事务是一个去中心化的设计,无论是SQL引擎还是后端的数据节点,其实都是具备高可用的同时支持线性扩展的设计。分布式事务比较复杂,单独讲的话可能能讲一门课,这里面涉及的内容非常多,比如两级段提交过程中有哪些异常场景,失败怎么处理,超时怎么处理,怎么样保证事务终的一致性等等。这里不再深入,希望有机会能单独给大家分享这块内容。
所以,这里只对分布式事务做一个总结,我们不再去探讨它的细节:
- 首先是基于两阶段提交,我们在MySQL原生XA事务的基础上做了大量的优化和BUG修复。比如说原生的XA在主备切换时会发生数据不一致和丢失,TDSQL在这个基础上做了大量的修复,让XA事务能够保证数据一致性。
- 第二个是强劲的性能。起初我们引进原生分布式事务的时候,分布式事务的性能还达不到单节点的一半。当然经过一系列的优化调优,后我们的性能损耗是25%,也就是说它能达到单节点75%的性能。
- 第三个是对业务透明,因为对业务来说其实根本无需关心到底是分布式还是非分布式,仅需要按照正常业务开启一个事务使用即可。
- 第四个是完备的异常容错。分布式事务是否健壮也需要考虑容错性的能力。
- 第五个是全局的锁检测。对于分布式环境下锁检测也是不可或缺的。TDSQL提供全局视角的分布式死锁检测,可清晰查看多个分布式事务之间的锁等待关系。
- 第六点是完全去中心化。无论是SQL引擎还是数据节点,都是支持高可用并且能够线性扩展。
以上是TDSQL分布式事务的总结。如果说用户要求保持跟MySQL的高度兼容性,那可能Noshard版TDSQL更适合。但是如果对于用户来说,单节点已经达到资源的瓶颈,没有办法在单节点下做数据重分布或者扩容,那必须选择Shard模式。但是在Shard模式下,我们对SQL有一定的约束和限制,后面会有专门的一门课去讲分布式TDSQL对SQL是如何约束的。
我们看到无论是Noshard还是Shard,都具备高可用、数据强一致、自动容灾的能力。
同时TDSQL也支持Noshard到Shard的迁移,可能早期我们规划的Noshard还可以承载业务压力,但是随着业务的突增已经撑不住了,这个时候需要全部迁到Shard,那么TDSQL也有完善的工具帮助用户快速进行数据迁移。
TDSQL数据同步和备份
接下来是TDSQL另外一个辅助特性:数据同步和备份。
TDSQL数据同步组件
数据同步的重点分为三个场景:
个场景是一个数据汇总。比如,多个数据库实例的数据同步到一个数据库实例,如保险行业用户多喜欢将多个区域数据库实例的数据同步到总库进行统计分析。
第二个就是跨城的容灾。跨城容灾,一般一个城市的分布式数据库的数据需要同步到另外一个城市异构的分布式数据库中做灾备。有的时候我们要做异构数据库的跨城容灾,比如说主城是一个十六节点的数据库,它非常庞大。但是备城,可能我们基于成本考虑,选用的设备数量、机房都要差一些。比如灾备实例只有两个物理分片。一个是两分片数据库实例,而另外一个时十六分片的数据库实例。从十六分片同步到;两分片,这是一个异构的数据库的同步,这时候我们就需要利用数据同步这个组件。
第三个是迁移。异构数据库的迁移,将数据从TDSQL同步到MySQL、Oracle、PostgreSQL等数据库。当然,从TDSQL到TDSQL是一种同步方式,更有一种是TDSQL到其他异构数据库。举个例子,张家港农商行,它的核心系统需要从传统的国外商用数据库替换为TDSQL,可能还是需要做一定的风险的防范。终我们给了一套用Oracle作为TDSQL灾备示例的方案,通过数据同步组件,将TDSQL的数据准实时同步到Oracle。假如在极端情况下需要将业务切到Oracle,我们也是有这个能力的。
当然数据迁移也体现了TDSQL开放的思想,既然允许用户将数据迁移到TDSQL,如果有一天用户可能觉得TDSQL不是很好,觉得有更好的产品可以替代它,TDSQL支持用户把数据迁走。
TDSQL数据备份
后我们聊一聊TDSQL的备份。
TDSQL支持在线的实时热备,同时这个备份是基于备机上做的备份,备份支持镜像和binlog的备份。镜像又支持物理镜像和逻辑镜像(也叫物理备份和逻辑备份)。
物理备份的好处是速度快,直接操纵物理文件,缺点是只能备份整个数据库实例,无法选择指定库表。逻辑备份的好处是通过SQL的方式备份,它可以选择单个库表备份,但是如果对整个实例备份效率不及物理备份。比如说有1T的数据,只有100兆是我的关键的数据,如果为了节省存储空间就没有必要用物理备份,就用逻辑备份,只备份我们关心的库表。
有了物理备份和逻辑备份之后,基于数据的镜像,再结合binlog轻松实现数据的定点恢复。对于binlog的备份TDSQL的Agent模块完成准实时的异步备份。比如说我们每天的0点备份镜像,同时各个时间段的binlog准实时备份。当需要恢复到一个早上6点的数据,利用0点的数据镜像再结合0点到6点的binlog,即可完成6点的数据恢复。
备份是在备机上做不会影响主机。整个备份过程也是有监控有告警,实现整个备份过程的追踪。
因为架构这一块内容确实是比较多,本次也作为所有TDSQL系列分享的一个前瞻,后面更多的系列分享会根据这门课的部分章节详细展开。这次分享主要是想帮助大家了解TDSQL的整体架构和模块划分,以及它的关键特性是如何设计,是基于什么样的考虑。听完本次分享再听后面的专题,会更容易去理解。
好的,以上就是今天分享的全部内容,谢谢大家!
Q&A
Q:TDSQL 1.0版本没有SQL引擎模块吧?
A:早在2002年的时候我们使用单机版MySQL作为数据存取服务,而后衍生出了TDSQL,这种计算存储相分离的架构,进而引入SQL引擎。
Q:请问存储节点的MySQL是使用官方原生的么 ?
A:TDSQL在原生MySQL基础上做了大量调优,如线程池、强同步的优化,以及安全限制,分布式事务XA优化等等。
Q:银行核心要做到分库分表,开发的聚合查询如何实现?
A:SQL引擎屏蔽了分表的细节,让业务在逻辑上看到的和单节点模式一下一样,仍然是一张独立的库表。此外,SQL引擎会自动做数据聚合,业务开发不需要关心。
Q:a+3,如果掉失了,B和C节点都没有同步过来,怎么办?A机器已经已经无法恢复。
A: a+3如果没有被B,C确认,即不满足被多数派确认,是不会应答给业务成功的,终会以超时的错误返回给业务。如果A机器无法恢复,这时新加入节点会通过物理拷贝方式快速度“克隆”出一个备节点继续代替A节点提供服务。
Q:Shard版本的可以通过pt工具,或者gh-ost加字段不?会有什么限制不?
A:TDSQL管理台提供online ddl的功能,会自动对多分片做原子性变更,不需要业务再用第三方工具;分布式TDSQL在做DDL的时候不允许调整shardkey字段,比如原来以id作为shardkey,现在要调整成name作为shardkey,这样是不允许的。
Q:TDSQL Shard算法有几种?建表语句必须需要修改语法吗?
A:TDSQL shard算法对业务屏蔽,即基于MySQL分区表做的hash拆分(该算法不允许用户修改),这么做也是为了对业务屏蔽TDSQL分表细节。这里并不是限制用户只能做基于hash的分区,用户可以在TDSQL-shard的基础上做二级分区(比如:按照日期、时间)。建表以及使用方面的语法,后面有一门关于分布式开发的课程,敬请期待。
Q:谁来解决zk的可靠性?
A:一方面,zk自身做了高可用跨机房部署,同时奇数个zk部署当发生故障时只要剩余存活zk数量大于集群zk的一半,zk就可以继续提供服务;另外一方面,就算zk节点全部宕机,各个模块自身对zk也不是强依赖的,即zk在不工作的情况下,数据库实例的正常读写请求不会受到任何影响,只是不能处理切换、扩容等调度相关的触发式操作。
Q:老师刚才讲两种模式,如果是用Shard模式,应用层对Sql语法有要求?是我听错了吗?
A:兼容MySQL 99%的SQL语法,shard模式与noshard模式主要的区别是shardkey的引入。引入shardkey之后,为了发挥出shard模式下的性能优势,建议所有sql都带shardkey访问,同时在shard模式下,一些数据库的特性如:存储过程、触发器、视图等特性会受到一定的使用限制。更详细的内容后面会有专门的分布式开发的课程会专门介绍。
Q:分支到总行数据同步汇总, Oralce 同步到TDSQL是双向都支持么?
A:支持双向同步。这里的支持是有条件的,从TDSQL到Oracle是可以做到准实时同步,但是从Oracle到TDSQL目前还无法做到准实时同步,后续会支持。
Q:分布式表和非分布式表如何做join?
A:分布式TDSQL下存在两种表分别是大表和小表,大表就是shard表(分布式表),小表又叫广播表,会冗余到所有数据节点上。分布式表和非分布式表做join时,由于分布式表所在的物理节点上存在非分布式表的一份冗余,因而可以在单个数据节点上完成join。
Q:是否支持自建私有云?如果公有云,成本会不会上升?
A:支持私有云的,TDSQL大部分的银行客户都是采用私有云部署模式,并和外网隔离。公有云的成本相比私有云明显是前者更低,像私有云需要自建机房,搭建光纤设备,但是好处是独占物理硬件资源。公有云的话都是和公有云上的其他用户公用一套IDC、网络环境。
Q:是否支持K8S部署?
A:TDSQL自带了一键部署解决方案,不依赖K8S。
Q:分局分表支持大表关联查询吗?
A:支持的
更多干货,尽在腾讯技术。