目前企业大多数的数据分析场景的解决方案底层都是围绕 Hadoop 大数据生态展开的,常见的如 HDFS + Hive + Spark + Presto + Kylin,在易果集团,咱们初期也是采起这种思路,可是随着业务规模的快速增加和需求的不断变化,一些实时或者准实时的需求变得愈来愈多,这类业务除了有实时的 OLTP 需求,还伴随着一些有必定复杂度的 OLAP 的需求,单纯地使用 Hadoop 已经没法知足需求。git
现有的准实时系统运行在 SQL Server 之上,经过开发人员编写和维护相应的存储过程来实现。因为数据量不大,SQL Server 可以知足需求,可是随着业务的发展,数据量随之增加,SQL Server 愈来愈不能知足需求,当数据量到达必定的阶段,性能便会出现拐点。这个时候,这套方案已彻底没法支撑业务,不得不从新设计新的方案。github
在评估初期,Greenplum、Kudu、TiDB 都进入了咱们的视野,对于新的实时系统,咱们有主要考虑点:算法
首先,系统既要知足 OLAP 还要知足 OLTP 的基本需求;sql
其次,新系统要尽可能下降业务的使用要求;数据库
最后,新系统最好可以与现有的 Hadoop 体系相结合。缓存
Greenplum 是一套基于 PostgreSQL 分析为主的 MPP 引擎,大多用在并发度不高的离线分析场景,但在 OLTP 方面,咱们的初步测试发现其对比 TiDB 的性能差不少。架构
再说说 Kudu。Kudu 是 CDH 2015年发布的一套介于 Hbase 和 HDFS 中间的一套存储系统,目前在国内主要是小米公司应用的较多,在测试中,咱们发现其在 OLTP 表现大体与 TiDB 至关,可是一些中等数据量下,其分析性能相比 TiDB 有必定差距。另外咱们的查询目前主要以 Presto 为主,Presto 对接 Kudu 和 PostgreSQL 都是须要考虑兼容性的问题,而 TiDB 兼容 MySQL 协议,在应用初期能够直接使用 Presto-MySQL 进行统一查询,下一步再考虑专门开发 Presto-TiDB。并发
另外,咱们但愿将来的实时系统和离线系统可以通用,一套代码在两个系统中都可以彻底兼容,目前 Tispark 和 SparkSQL 已经很大程度上实现了这点,这支持咱们在之后离线上的小时级任务能够直接切换到 TiDB上,在 TiDB 上实现实时业务的同时,若是有 T+1 的需求也可以直接指 HDFS 便可,不用二次开发,这是 Kudu 和 GP 暂时实现不了的。app
最后,TiSpark 是创建在 Spark 引擎之上,Spark 在机器学习领域里有诸如 Mllib 等诸多成熟的项目,对比 GP 和 Kudu,算法工程师们使用 TiSpark 去操做 TiDB 的门槛很是低,同时也会大大提高算法工程师们的效率。运维
通过综合的考虑,咱们最终决定使用 TiDB 做为新的实时系统。同时,目前 TiDB 的社区活跃度很是好,这也是咱们考虑的一个很重要的方面。
在这里介绍一下 TiDB 的相关特性:TiDB 是基于 Google Spanner/F1 论文启发开源的一套 NewSQL 数据库,它具有以下 NewSQL 核心特性:
SQL支持 (TiDB 是 MySQL 兼容的)
水平线性弹性扩展
分布式事务
数据强一致性保证
故障自恢复的高可用
同时,TiDB 还有一套丰富的生态工具,例如:快速部署的 TiDB-Ansible、无缝迁移 MySQL 的 Syncer、异构数据迁移工具 Wormhole、以及 TiDB-Binlog、Backup & Recovery 等。
因为咱们公司的架构是 .NET + SQL Server 架构,因此咱们没法像大多数公司同样去使用 MySQL Binlog 去作数据同步,固然也就没法使用 TiDB 官方提供的 Syncer 工具了。所以咱们采用了 Flume + Kafka 的架构,咱们本身开发了基于 Flume 的 SQL Server Source 去实时监控 SQL Server 数据变化,进行捕捉并写入 Kafka 中,同时,咱们使用 Spark Streaming 去读取 Kafka 中的数据并写入 TiDB,同时咱们将以前 SQL Server 的存储过程改形成定时调度的 MySQL 脚本。
在测试初期,咱们采用 TiDB 的版本为 RC4,在测试过程当中曾经在同时对一张表进行读写时,出现 Region is stale 的错误,在 GitHub 上提出 Issue 后,TiDB 官方很快在 Pre-GA 版本中进行了修复。在测试环境,咱们是手动经过二进制包的形式来部署 TiDB ,虽然比较简单,可是当 TiDB 发布 GA 版本以后,版本升级倒是一个比较大的问题,因为早期没有使用 TiDB-ansible 安装,官方制做的升级脚本没法使用,而手动进行滚动升级等操做很是麻烦。因为当时是测试环境,在听取了 TiDB 官方的建议以后,咱们从新利用 TiDB 官方提供的 TiDB-ansible 部署了 TiDB 的 GA 版本。只须要下载官方提供的包,修改相应的配置,就能完成安装和部署。官方也提供了升级脚本,可以在相邻的 TiDB 版本以前完成无缝滚动升级。同时 TiDB-ansible 默认会提供 Prometheus + Grafana 的监控安装,官方提供了很是丰富完善的 Grafana 模板,省去了运维不少监控配置的工做量,借着 TiDB 部署监控的契机,咱们也完成了诸如 Redis,RabbitMQ,Elasticsearch 等不少应用程序的监控由 Zabbix 往 Prometheus 的迁移。这里须要注意的是,若是是用官方提供的部署工具部署 Prometheus 和 Grafana,在执行官方的中止脚本时切记跳过相应的组件,以避免干扰其余程序的监控。
在10月中旬,随着新机器的采购到位,咱们正式将 TiDB 部署到生产环境进行测试,整个架构为 3 台机器,3TiKV+3PD+2TiDB 的架构。在生产环境中的大数据量场景下,遇到了一些新的问题。
首先遇到的问题是 OLTP 方面,Spark Streaming 程序设置的 5 秒一个窗口,当 5 秒以内不能处理完当前批次的数据,就会产生延迟,同时 Streaming 在这个批次结束后会立刻启动下一个批次,可是随着时间的积累,延迟的数据就会愈来愈多,最后甚至延迟了 8 小时之久;另外一方面,因为咱们使用的是机械硬盘,所以写入的效率十分不稳定,这也是形成写入延迟的一个很主要的因素。
出现问题以后咱们当即与 TiDB 官方取得联系,确认 TiDB 总体架构主要基于 SSD 存储性能之上进行设计的。咱们将 3 台机器的硬盘都换成了 SSD;与此同时,咱们的工程师也开发了相应的同步程序来替代 Spark Streaming,随着硬件的更新以及程序的替换,写入方面逐渐稳定,程序运行的方式也和 Streaming 程序相似,多程序同时指定一个 Kafka 的 Group ID,同时链接不一样机器的 TiDB 以达到写入效率最大化,同时也实现了 HA,保证了即便一个进程挂掉也不影响总体数据的写入。
在 OLTP 优化结束以后,随之而来的是分析方面的需求。因为咱们对 TiDB 的定位是实时数据仓库,这样就会像 Hadoop 同样存在不少 ETL 的流程,在 Hadoop 的流程中,以 T+1 为主的任务占据了绝大多数,而这些任务广泛在凌晨启动执行,所以只能用于对时间延迟比较大的场景,对实时性要求比较高的场景则不适合,而 TiDB 则能很好的知足实时或者准实时的需求,在咱们的业务场景下,不少任务以 5-10 分钟为执行周期,所以,必须确保任务的执行时长在间隔周期内完成。
咱们取了两个在 SQL Server 上跑的比较慢的重要脚本作了迁移,相比于 SQL Server/MySQL 迁移至 Hadoop,从 SQL Server 迁移至 TiDB 的改动很是小,SQL Server 的 Merge 操做在 TiDB 里也经过 replace into 可以完成,其他一些 SQL Server 的特性,也可以经过 TiDB 的多行事务得以实现,在这一方面,TiDB 的 GA 版本已经作的很是完善,高度兼容 MySQL,所以迁移的成本很是小,从而使咱们可以将大部分精力放在了调优方面。
在脚本迁移完毕以后,一些简单的脚本可以在秒级完成达到了咱们的预期。可是一些复杂的脚本的表如今初期并没表现出优点,一些脚本与 SQL Server 持平甚至更慢,其中最大的脚本 SQL 代码量一共 1000 多行,涉及将近 20 张中间表。在以前的 SQL Server 上,随着数据量慢慢增大,天天的执行时长逐渐由 1-2 分钟增加到 5-6 分钟甚至更久,在双11当天凌晨,随着单量的涌入和其余任务的干扰延迟到 20 分钟甚至以上。在迁移至 TiDB 初期,在半天的数据量下 TiDB 的执行时长大体为 15 分钟左右,与 SQL Server 大体相同,可是并不能知足咱们的预期。咱们参考了 TiDB 的相关文档对查询参数作了一些调优,几个重要参数为:tidb_distsql_scan_concurrency,tidb_index_serial_scan_concurrency,tidb_index_join_batch_size(TiDB 提供了很好的并行计算能力)。通过验证,调整参数后,一些 SQL 可以缩短一倍的执行时间,但这里依旧不能彻底知足咱们的需求。
随后,咱们把目光转向了 TiDB 的一个子项目 TiSpark,用官网的介绍来说 TiSpark 就是借助 Spark 平台,同时融合 TiKV 分布式集群的优点,和 TiDB 一块儿解决 HTAP 的需求。TiDB-ansible 中也带有 TiSpark 的配置,因为咱们已经拥有了 Spark 集群,因此直接在现有的 Spark 集群中集成了 TiSpark。虽然该项目开发不久,可是通过测试,收益很是明显。
TiSpark 的配置很是简单,只须要把 TiSprak 相关的 jar 包放入 Spark 集群中的 jars 文件夹中就能引入 TiSpark,同时官方也提供了 3 个脚本,其中两个是启动和中止 TiSpark 的 Thrift Server,另外一个是提供的 TiSpark 的 cli 客户端,这样咱们就能像使用 Hive 同样使用 TiSpark 去作查询。
在初步使用以后,咱们发现一些诸如 select count(*) from table 等 SQL 相比于 TiDB 有很是明显的提高,一些简单的 OLAP 的查询基本上都可以在 5 秒以内返回结果。通过初步测试,大体在 OLAP 的结论以下:一些简单的查询 SQL,在数据量百万级左右,TiDB 的执行效率可能会比 TiSpark 更好,在数据量增多以后 TiSpark 的执行效率会超过 TiDB,固然这也看 TiKV 的配置、表结构等。在 TiSpark 的使用过程当中,咱们发现 TiSpark 的查询结果在百万级时,执行时间都很是稳定,而 TiDB 的查询时间则会随着数据量的增加而增加(通过与 TiDB 官方沟通,这个状况主要是由于没有比较好的索引进行数据筛选)。针对咱们的订单表作测试,在数据量为近百万级时,TiDB 的执行时间为 2 秒左右,TiSpark 的执行时间为 7 秒;当数据量增加为近千万级时,TiDB 的执行时间大体为 12 秒(不考虑缓存),TiSpark 依旧为 7 秒,很是稳定。
所以,咱们决定将一些复杂的 ETL 脚本用 TiSpark 来实现,对上述的复杂脚本进行分析后,咱们发现,大多数脚本中间表不少,在 SQL Server 中是经过 SQL Server 内存表实现,而迁移至 TiDB,每张中间表都要删除和插入落地,这些开销大大增长了执行时长(据官方答复 TiDB 很快也会支持 View、内存表)。在有了 TiSpark 以后,咱们便利用 TiSpark 将中间表缓存为 Spark 的内存表,只须要将最后的数据落地回 TiDB,再执行 Merge 操做便可,这样省掉了不少中间数据的落地,大大节省了不少脚本执行的时间。
在查询速度解决以后,咱们发现脚本中会有不少针对中间表 update 和 delete 的语句。目前 TiSpark 暂时不支持 update 和 delete 的操做(和 TiSpark 做者沟通,后续会考虑支持这两个操做),咱们便尝试了两种方案,一部分执行相似于 Hive,采用 insert into 一张新表的方式来解决;另一部分,咱们引入了 Spark 中的 Snappydata 做为一部份内存表存储,在 Snappydata 中进行 update 和 delete,以达到想要的目的。由于都是 Spark 的项目,所以在融合两个项目的时候仍是比较轻松的。
最后,关于实时的调度工具,目前咱们是和离线调度一块儿进行调度,这也带来了一些问题,每次脚本都会初始化一些 Spark 参数等,这也至关耗时。在将来,咱们打算采用 Spark Streaming 做为调度工具,每次执行完成以后记录时间戳,Spark Streaming 只需监控时间戳变化便可,可以避免屡次初始化的耗时,经过 Spark 监控,咱们也可以清楚的看到任务的延迟和一些状态,这一部分将在将来进行测试。
在迁移过程当中,咱们获得了 TiDB 官方很好的支持,其中也包括 TiSpark 相关的技术负责人,一些 TiSpark 的 Corner Case 及使用问题,咱们都会在群里抛出,TiDB 的官方人员会很是及时的帮助咱们解决问题,在官方支持下,咱们迁移至 TiSpark 的过程很顺利,没有受到什么太大的技术阻碍。
在迁移完成以后,其中一条复杂的 SQL,一共 Join 了 12 张表(最大表数量亿级,部分表百万级),在平时小批量的状况下,执行时间会在 5 分钟左右,咱们也拿了双11全量的数据进行了测试,执行时间在 9 分钟以上,而采用了 TiSpark 的方式去执行,双11全量的数据也仅仅花了 1 分钟,性能提高了 9 倍。整个大脚本在 SQL Server 上运行双11的全量数据之前至少要消耗 30 分钟,利用 TiDB 去执行大体须要 20 分钟左右,利用 TiSpark 只须要 8 分钟左右,相对 SQL Server 性能提高 4 倍,也就是说,每一年数据量最高峰的处理能力达到了分钟级,很好的知足了咱们的需求。
最后,无论是用 TiDB 仍是用 TiSpark 都会有一部分中间表以及与原表进行 Merge 的操做,这里因为 TiDB 对事务进行的限制,咱们也采用以万条为单批次进行批量的插入和 Merge,既避免了超过事务的报错又符合 TiDB 的设计理念,可以达到最佳实践。
有了 TiSpark 这个项目,TiDB 与 Hadoop 的生态体系获得进一步的融合,在没有 TiSpark 以前,咱们的系统设计以下:
能够发现,实时数仓与 T+1 异步数仓是两个相对独立的系统,并无任何交集,咱们须要进行数据实时的同步,同时也会在夜晚作一次异步同步,无论是 Datax 仍是 Sqoop 读取关系型数据库的效率都远远达不到 TiSpark 的速度,而在有了 TiSpark 以后,咱们能够对 T+1 异步数仓进行整合,因而咱们的架构进化为以下:
这样就可以利用 TiSpark 将 TiDB 和 Hadoop 很好的串联起来,互为补充,TiDB 的功能也由单纯的实时数仓变成可以提供以下几个功能混合数据库:
实时数仓,上游 OLTP 的数据经过 TiDB 实时写入,下游 OLAP 的业务经过 TiDB / TiSpark 实时分析。
T+1 的抽取可以从 TiDB 中利用 TiSpark 进行抽取。
TiSpark 速度远远超过 Datax 和 Sqoop 读取关系型数据库的速度;
抽取工具也不用维护多个系统库,只须要维护一个 TiDB 便可,大大方便了业务的统一使用,还节省了屡次维护成本。
TiDB 自然分布式的设计也保证了系统的稳定、高可用。
上面这三点也是咱们从此去努力的方向,因而可知,TiSpark 不只对于 ETL 脚本起到了很重要的做用,在咱们从此的架构中也起到了举足轻重的做用,为咱们建立一个实时的统一的混合数据库提供了可能。
与此同时,咱们也获得 TiDB 官方人员的确认,TiDB 将于近期支持视图、分区表,并会持续加强 SQL 优化器,同时也会提供一款名为 TiDB Wormhole 的异构平台数据实时迁移工具来便捷的支持用户的多元化迁移需求。咱们也计划将更多的产品线逐步迁入 TiDB。
同时解决 OLAP 和 OLTP 是一件至关困难的事情,TiDB 和 TiSpark 虽然推出不久,可是已经知足不少应用场景,同时在易用性和技术支持上也很是值得称赞,相信 TiDB 必定可以在愈来愈多的企业中获得普遍应用。
做者简介:罗瑞星,曾就任于前程无忧,参加过 Elasticsearch 官方文档中文翻译工做,现就任于易果集团,担任资深大数据工程师,负责易果集团数据分析架构设计等工做。