云数据库 TDSQL 是一种兼容 MySQL 的关系数据库,是腾讯云提供的金融数据库解决方案。在腾讯 TEG 计费平台技术总监潘安群看来,金融的数据库要满足一下要求:新环境下的金融数据库应满足以下要求:要保证数据安全性、高可靠性,对数据丢失零容忍;需要高可用的容灾架构,以解决分布式环境中各种异常灾难;完善的水平扩展能力,以适应当前互联网爆发式增长带来的业务请求量和数据量。那具体而言,TDSQL 为了实现其适用于金融行业目标都做了哪些技术功课?InfoQ 采访了腾讯云 TDSQL 的潘安群先生。
嘉宾简介
潘安群,腾讯 TEG 计费平台部技术总监,目前主要负责金融级分布式数据库 TDSQL 的研发工作。2007 年加入腾讯,一直专注于分布式存储系统研发,尤其是分布式 NoSQL 及 MySQL 体系在支付金融领域的研发与实践。
InfoQ:TDSQL 目前线上使用情况如何?有哪些想和大家分享的亮点?
潘安群: TDSQL 诞生于腾讯计费平台部,2014 年开始为微众银行提供核心交易层数据库解决方案,2015 年正式上云,作为腾讯云金融政企行业的数据库解决方案,为客户提供公有云及专有云数据库服务。目前 TDSQL 的客户已覆盖第三方支付、互联网金融、银行、保险、证券、互联网 + 等多个金融政企行业。
数据库技术圈子不大,大家技术交流很开放,开源社区也很活跃,彼此会互相借鉴,所以技术层面,最终可能会殊途同归,只不过是各自产品针对不同场景,做不同的取舍而已。我们认为 TDSQL 有两点比较能打动人的:
- 良好的用户体验:无论是开发体验,还是运营体验。腾讯很重视用户体验,集团内部有场景并大量使用 TDSQL,腾讯产品上线都会要求腾讯自己首先是重度用户,而刚好我们计费平台部有这么一个场景,全公司所有计费系统全部承载在 TDSQL 上,包括近 100 亿的账户或订购数据,其中就有大家熟知的 Q 币,各类包月服务、游戏点券等等。数据分布在深圳、上海、香港、加拿大等多地。可以说我们积累了很多经验,支付体系对数据库层的要求是什么;也会从运营和开发两个层面去不断思考什么样的数据库才好用。
- 细节的把控:腾讯 10 多年来在 MySQL/NoSQL 等方面趟过的坑可谓一部血泪史,这促使我们不断的优化细节。就以容灾切换为例,在各种异常、各种灾难下,系统是否表现如预期一样是需要一个长期积累的过程。从 08 年以来,我们几乎每个月都会挑选系统,进行容灾演练确保容灾机制符合预期。即便如此,我们系统在线上依然发现很多场景没有覆盖到,因为故障的表现总是五花八门。所以一个系统的细节完善程度,一定是通过趟坑,在实践中去逐渐完善的。
InfoQ:为什么基于 MySQL?为什么当时没有选用其他类型的开源关系型数据库?
潘安群: 为什么会选择 MySQL 作为 TDSQL 的底层存储引擎,核心还是下面几点:
- 团队技术能力:从技术上来说对 MySQL 更有把握。从 03 年开始,我们的团队基本都是基于 MySQL 定制开发,积累了不少相关经验。
- MySQL 的技术成熟:本身的成熟度。金融场景要求的是稳定可靠,这正是我们技术选型的一个重要考查点。
- 广泛使用和传播:MySQL 使用广泛;我们看好其 MySQL 社区的发展。尤其是 MySQL 近些年的几个大版本表现都非常出色。
InfoQ:TDSQL 的技术研发积累已经十二年,可否分阶段介绍下技术的发展?
潘安群: TDSQL 作为腾讯计费平台数据存储层的第四代产品,虽从严格意义上来说,其研发始于 2012 年,但实际上是我们十二年数据存储层容灾、扩容等方面的经验沉淀:
1)2003-2008,MySQL 热备 + 分库分表 最原始的数据库容灾扩容方案,备份直接使用 MySQL Replication 机制,一主一备或一主两备;容量或性能问题,通过在应用层的分库分表来实现。
整体来说,这套机制是简单有效,但也存在明显的问题,就是在极端情况下,可能会丢数据:因当时 MySQL 只有异步复制模式,并没有现在的 Semisynchronous
Replication,在主机异常或网络异常情况下,应用层直接切换到备机,是有可能丢失数据的。2)2008-2010,MySQL 7*24 容灾 针对在异常情况下,会丢失数据的问题,我们设计了一套名为“7*24”的容灾体系,其核心思路就是增加了一个一致性控制中心(CC)。CC 会不断的比对主备数据之间的数据同步情况,当故障发生时,CC 会知道所有未同步数据(黑名单),应用层在切换到备机之前,需要先到 CC 拉取这份黑名单,将这些数据置为不可用状态,而其他数据则可以直接访问备机。通过牺牲极少部分用户的可用性,换取了 DB 主备切换的一致,一致性问题得到了系统的解决。整个故障检测、切换、恢复过程均由系统自动完成,基本无需手工操作。
通过大量的容灾演练和实战积累,这个阶段有了相对完善的孤岛检测、黑名单、多级切换等机制。同时,故障检测、主备一致性保障、数据恢复等机制也被提炼出来。当然,这套机制也有着她自身的问题:与应用层是高度耦合的,这要求每个业务开发人员必须对整套容灾逻辑非常熟悉,开发难度和运营维护难度都很高,而且容易出错。
3)2010- 至今,HOLD,一套基于全内存的,高一致性的 KV 存储系统 为了解决应用层与数据层高度耦合的问题,我们又开发了第三代产品,但是她的底层存储不再基于 MySQL,而是自研的一个全内存 KV 存储引擎。
后来的 TDSQL 架构和其中重要技术,几乎都在这个项目里进行了系统性的验证,包括在数据底层实现跨 IDC 的强同步、跨城容灾、切换一致性保障、数据自动的 Sharding、集群管理等。目前 HOLD 存储系统在公司内还大量使用,尤其是一些超高并发的业务系统。而她的缺点也是 KV 系统固有的问题,仅仅支持简单的 Get/Set 等接口,开发不够灵活。
4)2012 年 - 至今,TDSQL 经过近 10 年在数据存储层的摸爬滚打,我们希望能更优雅地解决数据层的问题,于是 2012 年立项 TDSQL:把 HOLD 中验证过的集群架构直接移植过来,将金融场景下最关注的一致性保障和水平伸缩等关键特性,都直接被融入底层架构中,这次直接使用了 MySQL 作为数据存储引擎,以获得成熟的关系型数据存储和访问能力。当然这里也有软硬件行业发展的契机:
- SSD 在公司内开始大规模使用,SSD 的 IOPS 相对于机械硬盘有质的变化,能满足大部分系统的性能需求。利用好 SSD 优势,能极大简化存储系统的架构体系。
- 以 KV 为代表的 NoSQL 无法形成业界标准及规范,反观以 Oracle、MySQL 为代表的传统关系型数据库生态完善,使用范围也更广,更容易被开发人员接受。
- MySQL 在 5.5 版本引入半同步复制,5.6 版本引入 GTID 等机制,而且经过 5.5,5.6,5.7 等几个版本,开源的 MySQL 在性能、数据强一致性方面有了质的提升。
InfoQ:可否给介绍下你们的分布式数据库架构和设计思路?
潘安群: 在架构上,TDSQL 的核心思想只有两个:数据的复制(replica)和分片(sharding),其他都是由此衍生出来的。其中:
- replica 配合故障的检测和切换,解决可用性问题;
- sharding 配合集群资源调度、访问路由管理等,解决容量伸缩问题。 对应地,replica 引入了数据多副本间的一致性问题和整体吞吐量下降的问题,而 sharding 的引入也会带来一定的功能约束。
在最终实现上,TDSQL 由 Scheduler、Proxy、Agent 三个核心组件加上 MySQL 组成,其中:
- Scheduler 是管理调度器,内部又分为 zookeeper 和 scheduler server;
- Proxy 为接入网关,多个网关组成一个接入层;
- Agent 是执行代理,与 MySQL 实例一起,构成数据节点。多个数据节点构成服务单元 SET。
InfoQ:是怎么保证服务器的高可用的?
潘安群: 分布式系统中,完整的容灾体系一般都是基于这样一个理论而设计的:多个独立的小概率事件同时发生的几率极低。
举个例子:假如一台服务器在一年内会出现故障的可能性为 0.5%,且服务器之间都是独立的。那么,在某一天内,任意指定的两台服务器均出现故障的几率为:(0.5%
\* 1/365)^2=0.00000002%,也就是常说的 7 个 9 的可用性,远远高于金融要求的 5 个 9。注意,这里的时间跨度是“同一天”,如果缩小到“同时”,如小时甚至分钟级别(一般一个节点故障恢复所需时间是几秒至几小时),这个几率还要小很多。在这个理论中,实际有四个变量:多个、独立的、小概率、同时。传统的 IOE 架构,专用的软硬件使得其在
“小概率”这个方向上优势明显。而在互联网分布式架构中,正是因其分布式,在“多个”和“同时”这两个方向天生就有优势,所以容灾设计的大部分精力都是投在“独立的”这个方向上。TDSQL 中,直接在 SET 这个级别上针对上述四个方向进行了优化:
- “多个”,实际就是冗余度,这里是数据副本的个数。理论上是越高越好,但是太多副本一方面资源投入太大,另一方面保持副本间的一致性成本也很高;所以,在 TDSQL 中,一般要求一个 SET 三个节点;
- “小概率”,就是节点的故障率,具备一定冗余度的节点会有更低的故障率,比如双电源、双网卡、磁盘 RAID 等。TDSQL 中,通过采用更加可靠的物理服务器来降低单机故障概率;
- “同时”,实际上可以认为就是单节点的故障恢复时长,如果在一个节点的故障恢复时间内,同一个 SET 的另外一个节点又发生故障,则这两个节点就是“同时”故障。这个时长越短越好。在 TDSQL 中,节点故障是优先本地恢复,若本地恢复不了,会在异地通过物理快照 + 增量 binlog 实现快速重建;
- “独立的”,就是说一个 SET 内的节点间,越独立越好。但是,这个世界所有东西都是相关的,绝对的独立是没有的。以两台服务器为例,如果它们在同一个机架、或者接入同一个交换机、或者在同一个 IDC,当机架电源故障、或者接入交换机故障、或者 IDC 故障,则两台服务器对外将表现为同时故障。更不用说更大范围的故障了,比如一个城市故障,整个城市的服务器都将“同时故障”。更好理解的例子是:如果地球没了,整个人类世界都没了。所以,“独立”只能是某个级别上的独立。在传统金融方案中,两地三中心的容灾方案,就是城市级别的。在 TDSQL 中,采用的“同城三中心,异地三中心”的方案,相比具有更高的容灾级别。
在同城部署时,一个 SET 至少三个节点,且三个节点跨三个 IDC。当一个 SET 中主机故障时,系统自动切换至备机,对上层应用没有任何影响;由于节点间的数据副本是强同步的,数据也不会有任何的丢失或错乱。当出现 IDC 级别的故障(如整个 IDC 网络孤岛或掉电宕机)时,仍然能保障每个 SET 至少有两个节点,服务也不会受到影响。
在跨城(两城地理距离 >1000km)时,SET 内在异地增加 watcher 节点。Watcher 节点不参与主备切换选举,仅通过异步方式从 master 复制数据。当整个主城故障且无法短时间内恢复服务时,可以直接将服务切换到异地的 TDSQL 上。这时候可能会存在几秒的数据的丢失,但在城市级灾难情况下,这属于可容忍范围。
所以,TDSQL 是将容灾直接做到 SET 内部:每个 SET 可部署为跨机架、跨 IDC、跨城容灾。在不考虑双重灾难的前提下,一个 SET 就是一个永不停服、永不丢数据的服务单元。
InfoQ:什么情况下建议采用数据库的分库分表?什么情况下不建议?
潘安群: 目前 TDSQL 有两个分支版本,一个是 No-Sharding(NS)版本,一个是 Group-Sharding(GS)版本。NS 版本不支持自动扩容;GS 版本支持自动扩容,但是该版本对 SQL 有一些限制,例如只支持 Group 内的 Join,不支持跨 Sharding 的 Join 等。
什么情况下选择 NS 版本,什么情况下选择 GS 版本,对于客户来说主要基于业务的数据量与并发量。目前 NS 版本,在我们现有机型下,最大支持 6T 存储空间,12 万 QPS,如果业务的数据量和并发量小于这个值,推荐使用 NoSharding 版本,因为其 SQL 是 MySQL 全兼容的。如果超过这个值,或者未来增长会比较快,那么就必须选择 GS,虽然在 SQL 语法支持上达不到 NS 版本,但是胜在业务可以近乎无感知的平行扩容。目前我们 TDSQL 在腾讯云上最大的 GS 库表是汇通天下,目前服务器数 12 台(一主一备模式),数据量已达 20T,请求量峰值达到 180 万笔每分钟。
InfoQ:数据库进行分库分表之后怎样保证数据一致性?数据的备份和复制工作应该遵循怎样的基本原则?你们有哪些优化?
潘安群: 我们谈数据的一致性的时候,通常有两种:一种就是 CAP 理论里的一致性,重点描述的是一份数据多个副本的数据一致性;另外一种是 ACID 理论里的一致性,重点描述的是事务的一致性。
首先解决的是多副本的数据一致性,在实现时,主要依赖如下两个机制:
- 强同步机制:基于 MySQL 半同步复制优化,对于进入集群的每笔更新操作,都将发到对应 Set(每一个 Set 包含 3 个 MySQL 实例:一主两备)的主机上,主机会将 binlog 发往两个备机,且收到其中任一一个备机应答后(仅仅是备机收到了 binlog 并刷盘,但可能还没有执行这个 binlog),然后才本地提交,并返回给客户端应答,这就能确保数据零丢失。此外,强同步机制势必会影响系统吞吐量,所以我们也引入线程池实现,并做了异步化,以提升并发性能。
- Leader 选举:MySQL 节点本身无法直接参与选举,于是我们由 Scheduler 模块来负责这个事,如果主机发生故障,Scheduler 会从两个备机中,选择一个数据较新的备机(因为 MySQL
binlog 是严格有序的,所以谁同步主机 binlog 的偏移量更大,谁的数据就更新。当然也可以基于 GTID 来判断)提升为主机。其次是事务一致性问题:TDSQL 水平 Sharding 后,数据被分布在不同的物理节点,如果一个事务涉及到多个物理节点上的数据,那就存在事务的一致性问题。目前 TDSQL 提供 2PC 的分布式事务功能,当然这里会有性能损耗,业务开发需要做一个选择和平衡:是由数据库的分布式事务来保障数据强一致,还是在应用层来实现。在大并发量的场景下,把这个提前到应用层,并做一些取舍,成本会更好控制。
InfoQ:各个分片数据怎么保证高可靠?如果某个分片出现故障了,该分片数据会怎样,系统会有什么响应措施?
潘安群: 在 GS 版本中,一个 Group 包含多个 Set,每个 Set 负责一个或者几个分片的数据,前端 Proxy 根据路由信息分发 SQL 到对应的 Set。
在 Set 内部,一般使用一主两备的架构,同时主备间使用强同步。如果一个备机挂了,不影响业务;如果主机挂了,由于至少一个备机是和主机同步的,因此该备机升为主机,同时修改路由信息,使得 SQL 能够正确地分发到主机。如果之前的主机能够修复,降为备机加入 Set;否则新增备机加入 Set。整个过程都不需要人为参与,单点故障不影响业务,不丢数据。
InfoQ:在您看来,分布式数据库的运维难点和重点在哪里?
潘安群: 分布式数据库运维体系里面,我们认为最重要的两点就是:自动化和监控。
关于自动化,现在 TDSQL 几乎所有的运营操作,例如购买、扩容、切换、备份、定点恢复、升级等等,均可以通过 HTTP 接口来完成。
关于监控,其实这里挑战很大,误告和漏告对运维来说都是极不友好的事。目前 TDSQL 的做法,基本围绕两个基本原则:
- 透明。这是最基础的,系统应充分的将自身内部状态呈现出来,并上报到监控系统,目前我们已经采集了 100+ 指标,包括 InnoDB 锁信息、MySQL 活跃线程数,请求时耗等。此外我们也对每一个操作的进度及结果都有严格的采集要求。
- 告警分级。例如我们对于主备切换,这是非常高级别,需要 NOC 电话告警,而对于慢查询这类,跟业务相关性较强的告警,会配合 IO、CPU 等使用率做告警聚合, 减少告警量。
InfoQ:TDSQL 适用什么样的场景,不适用什么样的场景,为什么?
潘安群: 当前 TDSQL 版本定位主要还是金融、政企的 OLTP 数据库场景,对于 OLAP 这类数据分析场景支持略显不足,对比 Oracle、PostgreSQL 系列产品,在统计函数和统计语法支持方面,例如分析函数等支持不够。此外我们的分布式版本,在分布式 JOIN 等,还有不少优化空间。所以我们并不推荐客户在 TDSQL 上做非常大量的数据分析工作。如果客户有大量的报表需求、关联查询,且数据量较大,我们的解决方案就是通过消息队列,将 TDSQL 数据实时同步到大数据平台(例如 Hadoop)进行计算。
评论