分布式数据库 TiDB 在百融云创的探索与实践
作者: magongyong 原文来源:https://tidb.net/blog/6a7d12e0
【是否原创】是
【首发渠道】TiDB 社区
【首发渠道链接】其他平台首发请附上对应链接
【正文】
背景介绍
和每个技术公司类似,当一个公司的业务和数据高速增长的时候,是最考验技术部门的时候了,2021 年中的时候,百融云创的 SaaS 端数据分析和 AI 业务快速发展,给我们的技术平台带来了新的挑战。我们云端运行的几个主要的数据分析和 AI 业务的子系统 A,B,C,D,E 都是以 Hadoop + MySQL + ElasticSearch 体系为主的存储架构,当时面临几个问题:
A 系统是目前数据量和增长量最大的系统,在当初架构设计的时候已经做过一次分库,单表的数据量最大达到 3 亿,每个月预估以 1 个亿左右的速度增长。这个系统的 QPS 目前也是最高的,数据库的 QPS+OPS 合计达到 3 万,我们预估半年后可能达到 6 万以上。
B 系统则是 2021 年我们新研发的中台系统,数据日处理量非常大,单表每日最大增长达到 1 亿以上的数据量,同时有大量的统计类实时查询需求,经过读写分离后,仍然会产生大量的慢查询,在 MySQL + ElasticSearch 的存储架构下,优化过多轮,但是效果并不明显。
C,D,E 系统都是业务增长比较快的 AI 业务子系统,我们设计的架构都是比较相似的:有分表分库,横向扩展分库的成本都比较高,不便于后期进行数据库分库扩容。
基于上面这些问题,我们经过几轮内部的评估,得出的结论是一年内我们在不做大规模的分库重构下,MySQL 应该还能支撑住,但是到 2022 年中的时候,上述这些业务特别是 A,B 业务将会有严重的性能影响。在这个情况下,百融的产品研发部、基础架构部和运维部联合发起了对分布式数据库的调研,经过技术委员会的几轮讨论,大家的焦点集中在这几个点:
大家都达成一致的是:2022 年数据库的存储、处理速度会面临巨大挑战,我们必须提前 1 年开始考虑怎么应对这些问题。
大家不完全一致的是:我们是继续对 MySQL 数据库进行扩容并且对系统进行大的重构,继续进行分库,把存储和访问压力分布到多个集群上去,还是考虑从 MySQL 存储体系完全升级到分布式数据库架构体系?
如果升级到分布式数据库,如何选型,是否满足我们的业务形态?
既然问题提出来了,我们面临的是如何选择,如何证明分布式数据库存储比我们现在使用的 MySQL 体系更合适?
技术选型
在经过多轮内部 PK 之后,我们又达成一个共识:大家都一致认为对系统进行重大的 MySQL 分库重构是不可行的,技术人员会花费大量的时间在这种不断的重构中,而且这种体系是有瓶颈的,我们不可能无限进行分库的,每次上线都要对历史数据进行迁移,是一个非常麻烦而且低效的事情。剩下的意见就是我们选择一种合适的存储架构来应对我们不断增长的存储和处理速度需求,研发人员可以专注在产品研发的工作上,而数据库的扩容可以几乎无限的快速扩容,不影响业务的发展,也不会让研发人员花费额外的时间去处理数据库扩容升级的事情。结论也应运而生:我们需要分布式数据库体系。很快我们对市面上的一些分布式数据库进行了调研:
首选 TiDB,国内已经有不少大厂开始使用了 TiDB,积累了不少的经验,我们可以参考。有社区版本可以使用,技术社区比较大,PingCAP 公司支持也还不错。
同时还研究了 OceanBase,但是找不到太多的使用资料和最佳实践经验,所以没有做太多调查就放弃 OB 了。
还有阿里云的 PolarDB,是云原生关系型数据库,我们的业务需求不能接受云数据库,所以就没有详细去研究了。
最后大家的结论达成一致是选择 TiDB 作为我们升级的分布式数据库,技术委员会讨论后也给出了几个指标调研的要求:
TiDB 的性能指标
TiDB 对比 MySQL 数据库的性能分析
TiDB 集群的服务器数量
对于问题 a,DBA 和测试部门给出了压测报告,下面表 1 是 TPS/QPS 随着压测线程增加的变化趋势:
表 1 - TiDB 双机房压测结果
压测 TiDB 集群服务器使用了 6 台服务器,配置如下:
下图是 TPS 和 QPS 随数据线线程数量的变化趋势:
从压测结果来看:
通过观察 TPS 的趋势图,在 100-1400 的线程中,TPS 随着线程数的增加呈递增趋势,并且有五处线程段的 TPS 峰值,300 线程内,290 线程 TPS 出现峰值为 3696.94,所测全部数据中 1400 线程内,1200 线程 TPS 出现峰值为 4119.23。
性能测试中有后台报错经运维排查是内存问题,tikv 内存默认为占用总内存空间的 40%,有 4 台 tikv 服务器时在 TPS 较大时内存将超 100% 导致各类服务器问题。经过将内存调整为原来的一半后。问题解决。
1400 线程测试中有 700 线程不限制请求事务数,700 线程每秒事务数 15 次。模拟正常业务场景。
综上:TPS 峰值最高为 4119,QPS 最高峰值为 82384, 1200 线程的并发执行未发现异常,能满足业务需要。所以对于 a 问题,目前根据 6 台集群的 TiDB 能达到这个性能,已经超过我们的预期了,完全可以支持我们未来的业务发展需求,所以这个问题大家一致通过。同时在压测过程中,DBA 也发现不少有意思的问题,比如 V4.0.12 版本的 TiDB 有不少的 bug,V4.0.13 版本相对稳定很多,最后我们选择了 V4.0.13 的版本。还有一些内存报错的问题,也是发现并解决了。
对于问题 b,主要考虑的是切换从 MySQL 切换到 TiDB 以后对我们的业务处理速度是否会下降,大家会有疑问,因为 TiDB 由于是分布式部署架构,天然支持无感的水平扩容来应对业务的增长,但是单个的查询、插入和更新操作对比 MySQL 是否有优势呢?同样我们做了一轮对比 MySQL 和 TiDB 的压测对比测试,下图是测试对比环境:
下面是 DBA 和测试部门给出的压力测试结果:
从测试得出的结论是:
并发低于 50 时,MySQL 在 TPS,QPS 和响应时间上,都优于 TiDB
并发高于 50 时,TiDB 的性能逐步上升,TPS,QPS 和响应时间都优于 MySQL
MySQL 在并发达到 100 时,TPS 不再上升,并发达到 300 时,响应时间出现明显下降,并发达到 800 时,报错退出
TiDB 通过持续扩容能够获得更好的性能和更大的数据容量,MySQL 由于单机限制,性能达到一定程度后无法继续提升
我们目前的情况是对于 A,B,C 业务,并发远高于 50,最高能达到 500 以上,2022 年甚至能到 1000 以上。在 TiDB 上将获得长久的 TPS、QPS 获益,对于响应时间而言,TiDB 比 MySQL 有差异,这个是显而易见的,毕竟分布式的数据库肯定会牺牲单个操作的响应时间,但是这点对我们业务形态而言是可以接受的。同时,我们还对一些比较复杂的统计类 SQL 在 MySQL 和 TiDB 上做了对比测试,效果也很惊人,TiDB 的查询速度比 MySQL 快 5 倍以上,这个对我们是个惊喜,以前 30 秒能统计的业务需求,在 TiDB 上 6 秒就能出来。
为了进一步验证,我们还做了 TiDB 的疲劳测试,长时间运行的情况下看性能是否有显著下降,测试报告如下:
测试结论是长时间测试过程中,性能没有明显下降,TPS 和 QPS 波动比较小。
最终问题 b 也得到了一致通过,可以推动进一步调研我们的 TiDB 集群架构设计了。经过大家的讨论,我们的初步方案如下:
整个 TiDB 初始集群一共有 10 台物理机,4 个 TiDB server 服务器,6 个 TiKV 服务器,3 个 PD 混合部署在一起,有一个 MySQL 接管机,和 TiDB 集群保持数据完全同步,能随时接管 TiDB。
整体技术选型和调研花费了我们 2 个月的时间,其中我们还请过 PingCAP 的技术专家们给我们进行咨询,提出了一些架构方面的意见。
升级过程
技术选型完毕后,技术委员会一致通过了技术方案,运维部门在准备 TiDB 集群的同时,交给研发部门的一个最主要的问题是升级到 TiDB 的兼容性,PingCAP 的官网介绍和各种技术文档都提到的 TiDB 基本 100% 建议 MySQL 的迁移升级。但是到真正操作的时候,还是有不少兼容性的改造的,我们遇到的主要的几个改造问题如下:
因为有一些系统的表里面,不是所有的表的 ID 都是使用发号器发号生成的,有不少表使用了自增 ID 的方式,这带来了许多的问题,TiDB 是分布式的数据库,自增 ID 不能保证是连续自增的,每台机器产生一批自增 ID(默认是 30000 一批),使用完后跳转到新的 ID 范围,导致 ID 的顺序并不一定随时间增加而增大,这样如果代码里面有依赖 ID 的自增逻辑的话,上线后肯定出现不可预估的问题
索引优化的问题,实际测试过程中我们发现 TiDB 对索引的选取逻辑和 MySQL 有一些差异,对多索引的命中逻辑不一致,需要进行优化
TiDB 的数据访问热点问题,在高并发批量更新、插入操作时,是比较关注的一方面。因为 TiDB 为一款分布式数据库,它的结构决定了会有这一问题。单机数据库是不会存在热点问题,性能的上限就是物理机器的上限。而分布式的结构为集群模式,存在多个节点。为了达到存储、读写能力可随时拓展的目的上,希望的是能较好的均匀分摊在每个节点上。当表主键为连续的时候,批量更新和插入会出现性能急剧下降。由于 PD 的热点调度只能以 Region 为最小单位,小表通常在一个 Region 上或者一段数据范围在一个 Region 上,PD 这时就无能为力了。目前 TiDB4.0 大幅度优化了 AutoRandom、新热点调度器、热点可视化,还在整个社区的帮助下、不断改进和探索。
慢查询的发现和预判也很重要,慢查询可以和 QA 同学一起协助来发现,预判对于研发人员则要求较高一些。首先要了解业务、业务代码逻辑、代码操作数据源的方式,其次要了解 TiDB 的基本原理和特性,最后需要使用经验来补充。
在迁移过程中发现 TiDB 与 MySql 数据库变量值不一致的问题,例如参数“max_allowed_packet”,“only_full_group_by”等等。类似这些参数在升级过程中需要注意一下,否则影响业务。
数据库连接地址后面的参数(例如 jdbc:mysql://[host:port]/[database][? 参数名 1][= 参数值 1][& 参数名 2][= 参数值 2]…),在迁移过程中要以原 MySql 的参数为基础进行调试,不可直接换掉或者抛弃某些参数。例如:rewriteBatchedStatements 和 allowMultiQueries 需要同时使用,否则批量更新的时候会报错。
事务检查,查看项目中是否存在大方法在一个数据库事务里,或者一个事务处理的数据量特别大,尽量将大事务拆分成小事务,减少前序执行耗时(由于锁冲突或错误,计划可能会执行失败并重试执行多次,该时间是不包含最后一次执行的前序执行自然时间)和发送结果的耗时(发送 SQL 语句执行结果给客户端的耗时)。
切换后前期需要持续性 SQL 调优。
这些改造点都是根据同行不少技术人员给出的建议和我们实践的实际经验得出的,非常宝贵,给我们后续更多应用的迁移带来了不少的帮助,但是实际上线后还遇到了一些其他问题,后面也会阐述。这是百融云创第一次使用准备 TiDB,我们讨论后采用了分步的升级过程:8 月份开始升级 B,C 业务系统,3 个月的线上运行,运行稳定后升级 A 业务系统。
兼容性的代码改造并不复杂,针对上述几个问题集中兼容改造测试后面临比较重要的问题是升级过程,如何无缝切换到 MySQL,不会影响线上业务,出现问题后如何快速回滚到原 MySQL,数据一致性的检查。切换的执行方案如下图所示:
(1) 切换前应用服务连接 MySQL 集群,数据通过 DM 保持和 TiDB 集群保持同步
(2) 切换后通过域名切换到 TiDB 集群,这时候通过 TICDC 保持数据和接管的 MySQL 库同步数据
如果发生回退,应用直接切换到接管的 MySQL 集群,保证数据一致性没有问题
其中我们用到了 TiDB 的两个工具:
第一个是 TiDB Data Migration (DM),将 MySQL 数据迁移到 TiDB 集群,使用这个工具可以提前将 MySQL 数据全部实时同步到 TiDB 上,保证迁移时数据一致。
第二个是 TiCDC,通过拉取 TIKV 的日志来同步 TiDB 的数据到 MySQL,确保托管的 MySQL 数据库能和 TiDB 数据保持一致。
为了稳定升级,我们将整个过程分解到 3 个阶段:
第 1 阶段:兼容性代码的改造,改造完毕后在 MySQL 环境下进行线上运行,确保兼容改造在 MySQL 上是运行稳定的。
第 2 阶段:由于考虑到 TiDB 是否在应对业务高峰的时候不会带来意外,我们将读写分离中的读业务切换到 TiDB,然后对 TiDB 进行慢查询和性能进行观察,确保运行稳定。
第 3 阶段:最后将主要的写业务切换到 TiDB 数据库,同时切换完毕准备压力测试,如果性能不达标,立即进行回滚,回滚到使用上述 TICDC 同步的 MySQL 接管库,确保数据不会丢失。
升级过程比较顺利,但是值得一提的是,TiDB 的单机热点 update 语句比 MySQL 慢一些,在大量数据形成单机热点 update 的时候还是能体现出来性能差异,如果是对 update 时间要求非常严格的业务,切换到 TiDB 要谨慎考虑。整个改造升级过程持续了 3 个月时间,终于在 2022 年 1 月份,我们主要的几个业务子系统 A,B,C,D,E 全部迁移到 TiDB 上了。
有一点的需要补充的是,为了应对 TiDB 数据库在实际运行过程中遇到不可恢复的问题,我们可能随时要切换回 MySQL 集群,为此我们做了切换的演练,从 TiDB 无缝切换到 MySQL 集群,然后又切换回 TiDB 集群,切换过程能发现一些想不到的问题,比如两边索引不一致的小问题可能导致线上出现大问题,只有经过真实的演练才能确保出现问题后切换方案是可行的。
线上遇到的问题
在迁移完毕后,带来的显著的好处是 TPS 和 QPS 的提升,根据目前的线上情况预估,QPS 在达到 10 万的时候 TiDB 仍然压力不大,就算不能应对时,动态扩容也很容易,在以前的 MySQL 集群的情况下单机扩容基本已经到瓶颈,QPS 在 3 万的时候 CPU 使用率超过 70%。另外的好处是研发人员不再担心业务压力造成数据库瓶颈,底层架构对研发人员更加透明。同时我们也遇到了一些问题,和大家分享下:
最大的问题是索引的问题,在 MySQL 上以前执行非常快的查询,在 TiDB 上会突然发现出现慢查询,仔细分析后发现要么就是没有命中索引或者选择的索引不是最优的,这个问题反馈给社区后,基本得出的结论是 TiDB 选择索引的算法和 MySQL 有一些微小的差异。典型的问题是 MySQL 中,如果主键 ID 是 Primary Key 并且 ID 有 NULL 值的情况下,根据 ID 查询索引可以命中,速度非常快,但是在 TiDB 里面不能命中索引,只能全表扫描,导致出现慢查询,解决的办法是只能处理数据,处理掉 NULL 值,这应该是 MySQL 兼容 NULL 值索引,TiDB 没有。同时查询字段与查询值的数据类型不匹配,会导致索引失效。
索引还有一些问题,联合索引的选择逻辑和 MySQL 不一样,导致选择了更慢的一些索引,处理办法是优化索引,调整字段顺序或者使用强制索引来处理。 (当 select 查询条件的字段为联合索引的非首字段时,tidb 也支持走索引,但是 mysql 不会走索引)
- 还有一些表的索引在切换后需要重新 ANALYZE TABLE 索引才能生效。 (tidb 的执行计划类似于 oracle,定时任务时间段内统计,如果表在白天变化较大,可能会触发错误的执行计划,这时需要手动 analyze 处理解决)
- 热点问题,主键为连续的值,多并发写入会出现排队的情况,因为同时写入到一个 region 里,无法发挥分布式并发的特性,官方推荐解决方法是主键采用 auto_random 随机数,或者业务采用发号器生成随机数。
TIDB 选择索引问题,即使是完全相同的 sql,只是修改了查询条件等号后面的值,会选择不同的索引。这种情况出现在查询条件字段为其他联合索引中的一个字段,后来经过专门创建联合索引解决。
TiDB 主键问题,其他合作部门会操作我们的 com 表,执行插入操作后出现主键 ID 特别大。因为创建分表会取 com 表的主键 Id 作为根表依据,Sharding-JDBC 的 algorithmExpression 参数(分片算法行表达式)。如果 com 表主键大于了 algorithmExpression 表达式的范围(例如{1…10000})会报错。
出现 SQL 执行效率变差是由于统计信息不准造成的,通过手动统计信息之后得到了正确的执行计划。创建联合索引可以更大的降低扫描数据量,进一步提升性能。在性能已经满足业务的情况下,联合索引会有额外的成本。
后续展望
2021 年是百融云创的 SaaS 产品使用 TiDB 的实践元年,在这一年中,百融云创多个技术团队:包含产品研发部、运维部、基础架构部和测试部门联合进行探索、调研到升级、迁移,让我们的产品和系统也进入了一个新的数据存储架构中,产品本身的稳定性和性能也带来了比较大的变化,技术团队在应对未来业务发展有了更强有力的工具,进一步积累了百融云创的技术范畴。我们相信在未来的几年中,以 PingCAP TiDB 为代表的分布式数据库将会在更多的公司和业务领域发挥更大的作用和价值。同时我们也在后续准备了一些新的计划:
多机房多活 TiDB 集群方案,为了保证公司的数据更加的安全和稳定,多机房多活 TiDB 集群势在必行,下一步我们也将研究这种架构,投入到我们的产品上。
TiFlash 的使用,满足公司基于列存储的一些业务场景。
百融云创技术团队将会持续根据公司业务发展的需求,在目前的基础上,引入适合自己的技术体系,让技术服务于产品和业务。后续我们也会继续更新我们新的实践经验,敬请期待!
百融云创研发技术平台
2022 年 1 月
版权声明: 本文为 InfoQ 作者【TiDB 社区干货传送门】的原创文章。
原文链接:【http://xie.infoq.cn/article/4a38fd0c2ec6b54709d07b5be】。文章转载请联系作者。
评论