做者介绍:胡盼盼,微众银行数据平台室室经理。硕士毕业于华中科技大学,毕业后加入腾讯,任高级工程师,从事分布式存储与云数据库相关的研发与运营工做;2014 年加入微众银行,负责微众银行的数据库平台的建设与运营。mysql
黄蔚,微众银行数据库平台室高级 DBA。2011 年加入腾讯互动娱乐运营部,担任英雄联盟在内的多款海量用户产品的数据库运维工做。2015 年加入微众银行担任高级 DBA,负责监控优化、性能优化以及新技术预研,目前致力于行内 NewSQL 的推广与生态建设。算法
本文将介绍微众银行的数据库架构演进过程,并分享 TiDB 在微众银行实践经验和部分业务案例。sql
2014 年微众银行成立之时,就很是有前瞻性的确立了微众银行的 IT 基础架构的方向:去 IOE,走互联网模式的分布式架构。IOE 即 IBM、Oracle、EMC,表明了传统基础架构领域的服务器、商业数据库和存储产品体系,众所周知传统银行 IT 架构体系很是依赖于 IOE,每一年也须要巨大的 IT 费用去维护和升级 。从数据库角度来看,当时除了 Oracle,能知足金融级银行场景的数据库产品并很少,微众银行基础架构团队通过多轮的评估和测试,最终肯定使用腾讯主推的一款金融级别数据库 TDSQL。数据库
<center>图 1</center>安全
TDSQL 是基于 MariaDB 内核 ,结合 mysql-proxy、ZooKeeper 等开源组件实现的数据库集群系统,而且基于 MySQL 半同步的机制,在内核层面作了大量优化,在性能和数据一致性方面都有大幅的提高,同时彻底兼容 MySQL 语法,支持 Shard 模式(中间件分库分表)和 NoShard 模式(单机实例),同时还集成了管控平台,智能运维等功能模块。2014 年,TDSQL 已经支撑了腾讯内部的海量的计费业务,因为计费业务的场景和银行的场景有所相似,对数据库的可靠性和可用性要求也相近,因此咱们当时选择了 TDSQL 做为微众银行的核心数据库。性能优化
<center>图 2</center>服务器
肯定了数据库的选型以后, 下一步就是架构设计。咱们设计了基于 DCN(Data Center Node)的分布式架构。DCN 能够认为是一个“自包含单位”,它会包含应用层、接入层、数据库层,每一个 DCN 承载规定数据量的用户,通俗的理解,每一个 DCN,就至关于微众银行的一个小的分行;基于 DCN 能够实现集群规模的水平扩展。这种架构对于数据库来讲,实际上是比较友好的,由于每一个 DCN 的用户规模是肯定的,那么对数据库的容量和性能要求也是可肯定的,所以咱们没必要再采用复杂的中间件分库分表的方式构建数据库,而只用单实例模式承载,极大简化了数据库架构,也下降了业务开发成本。网络
如图 2 所示,为了实现 DCN 架构,这里有两个关键组件:RMB 和 GNS。RMB 负责各个模块以及各个 DCN 之间的消息通讯;GNS 负责全局的 DCN 路由,即某个用户保存在哪一个 DCN。另外这里有一个比较特殊的地方就是 ADM 管理区,它是一个统一的管理区,保存着没法进行 DCN 拆分的全局业务数据,和经过各 DCN 进行汇总的数据。后来 ADM 区成为了一个 TDSQL 的瓶颈,这是咱们引入 TiDB 的动机之一。架构
<center>图 3</center>并发
接下来看一下咱们的 IDC 的架构。目前咱们是两地六中心的架构,深圳的 5 个 IDC 是生产中心,而位于上海的跨城 IDC 是容灾中心。同城的任意两个 IDC 以前的距离控制在 10~50 千米之内,并经过多条专线互联,以此保证两个 IDC 之间的平均网络延迟能够控制在 2ms 左右,并保证网络的稳定性。
<center>图 4</center>
基于以上的 DCN 架构和 IDC 架构,咱们设计了数据库的基础架构,如图 4 所示:咱们采用同城 3 副本+跨城 2 副本的 3+2 部署模式,同城 3 副本为 1 主 2 备,跨 3 个同城 IDC 部署,副本之间采用 TDSQL 强同步,保证同城 3 IDC 之间的 RPO=0,RTO 秒级,跨城的 2 副本经过同城的一个 slave 进行异步复制,实现跨城的数据容灾。基于以上架构,咱们在同城能够作到应用多活,即同城的业务流量,能够同时从 3 个 IDC 进来,任何一个 IDC 宕机,能够保证数据 0 丢失,同时在秒级内能够恢复数据库服务。这个架构在微众银行内部运行了四年多,当前已有 1500 多个实例在运行,数据量达到 PB 级,承载了银行数百个核心系统,总体上来讲还比较稳定的。但同时也遇到一些瓶颈。由于咱们采用的是单实例的部署模式,对于有些没法经过 DCN 拆分进行扩展的业务场景,单实例的性能和容量就很容易到达瓶颈。
固然,TDSQL 也提供了 TDSQL-Shard 模式,也就是经过中间件分库分表的方式把一个表 Shard 以后再处理,但咱们当时评估以后认为该模式对应用的侵入性比较大,好比全部的表必须定义 shard-key,有些语法可能不太兼容,有些分布式事务的场景可能会有瓶颈,进而致使业务迁移的成本会比较高。
因此在这个背景下,咱们开始寻求其它的解决方案,大约在 2018 年,NewSQL 的概念逐渐被提了出来,同时也有一些商业和开源的 NewSQL 数据库出现。咱们很惊喜的发现,NewSQL 数据库的特性,能够较好的解决咱们当时面临的问题。NewSQL 比较通用的定义是:一个能兼容相似 MySQL 的传统单机数据库、可水平扩展、数据强一致性同步、支持分布式事务、存储与计算分离的关系型数据库。通过大量的调研,对比与分析,咱们最终决定重点考察开源 NewSQL 数据库产品 TiDB。
<center>图 5</center>
除了 TiDB 的 NewSQL 特性以外,咱们选择 TiDB 的另外一个主要缘由,就是 TiDB 是一个开源的项目,并且社区很活跃,版本迭代快速,咱们以为这是一个很好的模式,并且微众自己也是很是拥抱开源的。
<center>图 6</center>
这里展现了 TiDB 的基本架构模型,很是简洁优雅的架构,相信稍微了解 TiDB 的同窗,对这个架构都比较熟悉了,在这里就再也不赘述。固然,如今 TiDB 3.0 版本有了新的特性以及模块加入,好比 Titan 引擎, 针对 RocksDB 大 Value 写放大问题作了很大的优化和性能提高,再好比列式存储引擎 TiFlash ,为实现真正的 HTAP 奠基了基础。
<center>图 7</center>
咱们对 TiDB 作了一些评估和测试,对语法和 DDL、负载均衡、一致性、扩容等特性都作了不少测试。下面重点介绍如下 3 点:
这个特性对咱们来讲,有很大的好处。由于在 MySQL 里面作 DDL,是风险比较大或者说比较重的一个操做,特别是一些超大的表的 DDL;但在 TiDB 里,这个操做变得轻量而安全。
这和 MySQL 的悲观锁模型是不太同样的,这个特性对于某些业务场景的兼容性可能会有问题。
TiDB 3.0 版本中已经试验性支持了悲观锁,而且在今年下半年有望成为一个正式功能,这是一个很好的消息。在金融场景里面悲观锁应用仍是比较普遍的。
咱们将 TiDB,TiKV,PD 节点跨 3 个同城机房部署,而后把其中一个机房的网络所有隔离,来测试 TiDB 的可用性,包括 Raft Group 的 Leader 的切换等等,测试结果总体符合预期。
<center>图 8</center>
图 8 是 TiDB 在微众银行的部署模型,从一开始就选择了同城三机房部署,也就是位于最底层的存储层 TiKV 跨 3 个机房,3 个副本分布在 3 个机房,而且每一个机房有 1 套独立的 TiDB Server 集群负责接入与计算;PD 模块也是跨 3 个机房部署。另外,针对比较重要的业务,咱们会在容灾 IDC 挂一个容灾 TiDB 集群,这个容灾 TiDB 集群会经过 TiDB Binlog 工具从生产集群实时同步数据。
TiDB 在微众银行的应用场景包括 OLAP、OLTP 及部分混合场景,大部分场景在 TB 级别的业务数据规模。下面详细介绍贷款核心批量系统在测试 TiDB 过程当中的实践和优化,以及数据存证系统 TiDB 迁移实践。
<center>图 9</center>
这个业务场景的特殊性在于天天晚上 0 点以后,须要经过线上数据库生成数亿级别的批量数据,并进行一系列相关计算,而后 ETL 到大数据平台去,其中涉及大量的增删查改操做,而且对总时效要求很高,必须在两个小时内跑完,不能出现任何问题。
存在的问题:
因此咱们尝试经过 TiDB 来承载这个批量场景,把天天的批量数据,汇总到一个大的 TiDB 集群中,再进行跑批,最后再 ETL 到大数据平台中去作处理。整个流程如图 9 右半部分所示,其中 “DM 工具”即 TiDB DM(TiDB Data Migration),是由 PingCAP 开发的一体化数据同步任务管理平台,支持从 MySQL 或 MariaDB 到 TiDB 的全量数据迁移和增量数据同步。
咱们对应用侧和 TiDB 2.1 版本进行了一系列的调优,总体的优化效果达到预期,批量的耗时缩短了 45% 左右。咱们同时也测试了 3.0 beta 版本,3.0 相对于 2.1 版本,总体批量耗时又缩短了 30% 左右。总体来看,TiDB 让咱们的贷款核心批量场景下效率获得大幅度的提高。
在整个业务测试的过程当中。咱们在应用侧和数据库侧,都作了大量优化,也踩了很多坑,这里也分享几点。
<center>图 10</center>
该业务对批量数据导入的时间很敏感,但咱们测试时发现虽然底层有 6 个 TiKV 节点,但每次数据开始导入时有 3 个 TiKV 节点负载特别高,另外 3 个节点负载很低,同时写入也有瓶颈。经过排查发现这个问题的缘由在于,对于快速的超大表的数据写入,TiKV 的热点调度并不及时,没有办法作到负载均衡,进而致使热点。
咱们和 PingCAP 伙伴们讨论解决方案后,增长了 Region 预打散的功能。就是在建表时,就对表进行 Region 打散操做 ,至关于一个空表就分散成多个 Region 分布在 6 个 TiKV 节点上,当数据导入的时候就直接写入各个 Region。从图 10 能够看到增长预打散功能后,6 台 TiKV 的负载很是均衡,而且耗时也变短了。
<center>图 11</center>
另一个遇到问题就是无效 Region 过多的问题。前面提到,该业务数据在天天跑批完成以后须要删掉,次日所有数据须要从新生成,因此该场景下天天都有大量的数据表删除和重建,会累积大量无效 Region,致使 PD 元数据管理压力过大,Region 副本之间的心跳也会大量增长 grpc 调用,致使整个系统运行比较慢。因此咱们后来灰度上线了 Region merge 功能,这个功能在 TiDB 2.1.8 之后的版本中(含 3.0 GA)引入,
从 图 11 能够看到上线 Region merge 功能以后,Region 数量直线降低, 这个功能让系统性能的提高提高了 30% 左右。
数据存证系统是微众银行很是重要的系统,存储了具备法律效力的证据类数据,这些数据对客户来讲是很是重要的。
<center>图 12</center>
随着愈来愈多的业务系统的接入,该场景的数据增加速度很是快,好比每一次转账都须要生成一个证据,而且不是简单的一条记录,而是发生纠纷时法院承认的证据,因此也决定了这些数据不能删除。这些数据划分在 ADM 区,没办法作横向扩展,遇到了很大的瓶颈。基于这些场景特色,微众选择了 TiDB 的解决方案。咱们有几个基本的迁移原则:1)数据不能错、不能丢;2)服务敏感度很是高,须要尽可能无缝切换到 TiDB 架构上;3)由于是比较严肃的金融场景,若是在迁移过程当中有任何困难,咱们指望可以随时回切到 MySQL。
<center>图 13</center>
迁移总体方案如图 13,步骤流程比较长,但会更加安全。接下来介绍每一个步骤中咱们碰到的困难和解决方案。第一个步骤是前置检查。首先表必须有主键,若是是短期海量连续写入,不建议用自增 ID,能够把自增 ID 改为由雪花算法生成,再把雪花算法的时间戳后几位提到最前面,这样能够保证主键足够随机 ,而后使用咱们以前提到的 Split Region 的功能,提早把 Region 切分,并打散到全部的 TiKV 节点里,这样能够在写入的时候实现负载均衡,解决短时大量写入瓶颈问题。
<center>图 14</center>
触发器、存储过程、视图、function 这些特性在咱们行内是禁止使用的,因此对咱们是没有影响的。总体来看,前置检查这一步咱们重点关注的是性能问题,尤为是保证写的性能,该场景是大批量数据,短期的数亿数据写入性能的瓶颈问题仍是值得关注并解决的。
<center>图 15</center>
前置检查完成后,接下来就是将数据同步到 TiDB,PingCAP 提供了实时同步工具 TiDB DM,在简单配置以后能够“一键式”将 MySQL 中的数据导入 TiDB,让数据迁移变得很是便捷。固然,咱们也遇到了几点问题:
咱们和 PingCAP 研发同窗们讨论以后,临时解决方案是部署两个 dm-worker(冗余部署),一旦某个 dm-worker 发生问题,就启动另一个 dm-worker,从下游记录的 pos 点继续同步数据。另外,咱们很是期待将来 DM 整合到 TiDB 的 K8s 部署生态工具链中, 再配合云盘(好比 ceph)作状态信息的存档 ,这样会更加完善 DM 的高可用性,咱们也深度参与了 PingCAP 研发同窗们关于 DM 高可用方案的设计讨论。
由于目前“一主多备”架构下,咱们把 DM 挂载在其中一台备机,若是这台备机因为服务器故障缘由致使宕机,就须要人工把 DM 挂载其余正常的备机,处理时效上会没那么及时;很是期待将来 DM 可以把这个切换操做作成自动化。
一方面,DM 回放 binlog 时须要作并发处理,可是处理以前会作冲突检测,若是没有主键就作不了冲突检测,也就不能作并发回放,致使同步效率比较差。另外一方面,幂等操做,好比 DM task 重启或者恢复,会从下游记录的 pos 点继续同步数据,但由于 pos 点不是实时记录,因此会致使重复回放 binlog,若是没有主键,好比重跑两次 insert,数据就重复写入了。所以就要求表必须有主键,DM task 重启或者恢复的时候,DM 内部作一个幂等转换,好比把 Insert 转换成 replace ,把 update 转换成 delete+replace,这样的话就算重跑不少次,它的结果是不会受影响的。
<center>图 16</center>
做为一个金融场景,尤为是异构的数据同步,数据校验是一个很是严肃的确认过程。熟悉 MySQL 的同窗应该了解过 pt-table-checksum 工具,它的原理和 PingCAP 提供的数据校验功能相似,将这个数据切片以后,对数据切片进行 checksum 计算,而后比对上下游全部切片的 checksum 值是否同样来判断数据一致性;可是它当前还作不到相似 pt-table-checksum 的在线校验,若是上游 MySQL 的数据一直在变,就没办法作校验了。另外,Chunk 切分偶尔不许、上下游排序规则不一致,这两个问题已经在新版本有了优化。
<center>图 17</center>
接下来是灰度切读流量的过程。基于安全性考虑,咱们先把非关键的读流量灰度切换到 TiDB 中去,观察一段时间,再把关键的读流量也逐渐切换到 TiDB。当时遇到了执行计划不许的问题,致使把读流量切换到 TiDB 后发现,有些 SQL 查询变慢了,这个问题在新版本中已经解决了,包括 TiDB 3.0 中也有执行计划绑定(Plan Management)、增量统计信息更新等优化。
实际上,执行计划不许的问题在 MySQL 等一些数据库产品中比较广泛,由于统计信息不能 100% 实时更新。之前使用 MySQL 产品时,用户可能须要强制指定某个索引 Index,这个操做对业务侵入性很大,而基于上面两个功能,TiDB 在这点上对业务的侵入是不多的。
<center>图 18</center>
在读流量切换到 TiDB 没有出现什么问题以后,咱们再把写流量切换到 TiDB,但也不是一次性切换,咱们选择先双写 TiDB 和 MySQL:先写 MySQL 返回自增 ID,再用该 ID 加上业务数据异步写入到 TiDB;上下游的 ID 保存一致方便咱们进行数据校验。在双写改造完成后,架构如图 18 所示。应用准备发版时,为了保证业务暂停的时间足够短,咱们临时调大了消息队列 MQ 的长度,由于在整个应用关闭以后,消息队列仍在存储消息,可能会把消息队列存满。调大消息队列长度以后,再逐个关闭应用,等到全部应用都停掉后,在确认 DM 的数据同步已经追平后,就能够把 DM 断开,接下来就能够逐个启动新版本的应用了。业务中止时间(最后一个应用关闭到新版本第一个应用启动的时间间隔)控制在 1 分钟之内,对应用的影响很是小。
到这一步骤为止,其实整个服务读写都采用了 TiDB,但为了保证数据出现问题时可以及时回迁,因而咱们把灰度上线的的周期拉长,使用 TiDB Binlog 把 TiDB 中的数据反向同步到 MySQL,以下图所示。
<center>图 19</center>
咱们观察到 Drainer 与同城 IDC 的下游 MySQL 部署在一块儿,RPC 延迟会更短,性能会更好。在几个月以后,咱们最终把反向同步关闭了,彻底由 TiDB 提供服务。
<center>图 20</center>
如图 20 所示,咱们还会作例行的数据备份的操做,包括使用 mysqldump 每周全量备份,使用 drainer pb 每 5 分钟备份增量 binlog,另外数据备份最好使用单独的 tidb-server 节点,对联机的请求影响最小。
在观察一段时间以后,观察到各方面的性能指标都很是稳定,而后决定将反向同步 MySQL 断掉,也就意味着数据存证系统这样一个很是重要的系统,彻底跑在了 TiDB 上,回顾整个迁移过程,仍是比较流畅且顺利的。
TiDB 是一个很优秀的分布式关系型数据库产品。对银行场景来讲,灰度和上线的节奏没有互联网行业那么快,随着 TiDB 产品的日趋成熟,咱们正在更多适合的场景试用 TiDB,也会有更多的经验和你们分享。
本文根据胡盼盼、黄蔚在 TiDB TechDay 2019 北京站及深圳站上的演讲整理。