TUG 周年庆精选:多点数字化新零售平台业财一体化

TUG 于 2019 年 6 月 23 日成立,值此一周年之际,TUG 管理委员会与各区域小组,将联合举办周年庆系列活动。今天我们回顾一下,在 8 月 15 日,由 TUG 西南组承办的,主题为《TiDB 在多点业财一体化下的探索与实践》的西南场 TUG 活动的精彩内容,以为为演讲实录。​

讲人 张兴晔 | 多点 Dmall 系统架构师,负责多点数字化零售平台的系统架构和开发。

首先感谢 PingCAP 的同学把 TUG 社群活动带到了多点,很荣幸能和大家一起分享 TiDB 在多点业财一体化下的探索与实践。

其实 PingCAP 和多点还很有缘分的,你们成都的办公室就在我们楼上。另外多点在2015年成立了分布式电商,而 PingCAP 也在 2015年成立了分布式数据库,所以让我有一种相见恨晚的感觉。

下面今天给大家的分享,分为五个小节:

  • 第一小节,零售平台业财一体化所遇到的挑战;
  • 第二小节,数据库存储方案的选型;
  • 第三小节,TiDB 业务场景及解决的痛点;
  • 第四小节,TiDB 在多点的上线规划;
  • 第五小节,我们在实践中遇到问题和解决方案。

先来看一下背景,刚才大家可能通过视频已经了解了多点,实际上是帮助零售企业进行数字化的转型升级,启动一站式的零售数字化解决方案。从之前的零售到现在的新零售,从之前的线上、线下到现在的线上线下一体化,从以前的信息化到现在的数字化,从原来的传统财务的ERP到现在的业财一体化,整个背景是这样的。

什么是业财一体化,我们先来看一下目前多点的业财一体化的整体架构。我们可以看到这个业务数据里面,销售类的对应财务系统的收入线,收银类对应的支付线,库存类对应成本线,结算类对应结算条线,所有这个业务条线的数据最终会汇聚到我们财务中台里面,通过我们的财务规则、凭证模型、数据校验,以及数据处理、数据存储,整个一套完整的规则体系,来生成我们对应的财务核算的数据。同时需要产出经营分析的相关的一些报表数据,以及进行业务单据和财务单据的跟踪追溯。可能我们传统的财务ERP里面,业务和财务数据是相互独立的,数据难以进行跟踪和追溯,并且只能做到财务核算,不能为这个业务赋能。

以财务中台为核心的业财一体化解决方案,它的本质其实是为业务赋能。不仅仅停留在财务核算的层面,更通过经营分析、跟踪追溯,为公司的决策提供数据支撑和数字化能力。同时,让我们的财务数据能够更好地理解每一笔业务数据,建立起会计总账与业务明细账之间的勾稽关系。

下面进入第一小节,零售平台业财一体化遇到的挑战。我们会遇到业务上、系统上、技术方案上三个层面上的挑战,首先我们看一下业务上,整个业财一体化对接的这个业务数据,包括多点的店务、订单系统、POS系统、进销存、采销系统、台账系统、售后系统等,大概有十几个系统,需要同时处理十多个业务系统的上百种业务类型的单据。面对如此多的业务单据,给我们业务上带来的有哪些挑战。

  • 第一,数据不及时。我们商家可能每个月月结的时候,才能把这个月的财务数据推送到管理层,一般次月的月初结账,所以说管理层要看到本月的财务数据,只能等到次月的上旬。效力差一点的商家可能要等到中旬,所以说数据不及时可能会错失我们最佳的决策时机。

  • 第二,我们发现财务数据与业务数据其实是割裂的。特别是在传统的财务ERP里面,这样会带来数据的不连贯。同时数据统计的口径也不一致,这会造成数据不准确。比如说财务上所讲的收入是不含税的,但是我们业务上所讲的收入是含税的,财务确认收入,用的是权责发生制,但是业务可能喜欢的是收付实现制。

  • 另外在数据的追溯上,传统财务ERP里面,我们财务报表里面的异常数据是很难进行追溯的。

给我们系统带来的挑战是数据量指数级的增长,系统吞吐量不足,数据延迟,性能不够快。

目前以多点的数据量为例,三个月来我们单个业务条线的数据量已经超过1T,结合财务数据的特点,需要至少保存三年。这个预估的量级是在50到100个T,考虑到业务的未来的快速发展,可能是能达到 PB 级别的数据规模。

面对如此庞大的数据量,按照已有的技术方案,我们可能需要针对 OLTP 业务,一方面通过数据库中间件来实现分库分表,比如说通过 Sharding-JDBC 或者 Mycat 这些数据中间件来帮我们实现分布式的方案。但这种方案不仅是严重侵入了业务系统,后期的维护成本也很高,业财一体化下面,我们几十个业务系统都需要单独去实现,而且各系统的分库分表规则也是千差万别,所以说后期会给我们带来成本维护上很大的消耗。

另一方面,我们需要考虑我们的 OLAP 业务怎么去实现,首先我们需要把这个数据同步到我们的 ES 和 ClickHouse 里,来满足我们列表和 AD-Hoc 的业务查询,同时还要将数据同步到大数据平台,完成复杂的报表分析,而且这个报表的数据只能做到T+1,所以它的目前是满足不了我们的业务需求的。

目前其实很多系统可能都是通过这个技术方案这么去做,从技术实现难度来说,它已经是一个比较成熟的方案。初期可能我们可以这么去玩,但是随着数据量的不断地增长,这个方案可能到后期的维护成本会越来越高。

所以我们说按照传统的技术架构,系统的复杂度堪比黄桷湾立交桥,就哪怕你是老司机,也可能走错路口。我们如何才能摆脱存储的介质、容量的限制,不用担心系统的复杂度,而专注于业务逻辑的实现,我们该选择什么样的数据存储方案呢? 答案无外乎就三种:传统关系型数据库、单机容量有限;而 NoSQL 数据库满足不了我们对于事物和数据强一致性的要求;NewSQL 作为新型分布式关系型数据库,可能是目前我们比较好的选型方案。

说到选型和转型,大概从8年前开始,很多公司都在从 Scale-up 到 Scale-out ,以及去 IOE 的背景下,经历了从 Oracle、SQLServer,到 MySQL 的转型,同时也从这个传统的 SQL 向 NoSQL 的转型,很多互联网应用目前可能都依赖于 MongoDB、Redis、Cassandra 去做一些相应的业务实现。但是现在我们可能正在面临一个新的转型是从 SQL/NoSQL 到 NewSQL ,也就是分布式关系型数据库。

说到分布式关系型数据库,其实在技术领域,去中心化、分布式架构已经是大势所趋,我们可以看到:

  • 通过 Dubbo、Spring Cloud、Service Mesh 实现了服务的分布式。
  • 通过 Redis Cluster、Codis 解决了缓存的分布式问题。
  • Kubernetes 构建了操作系统的分布式,那谁来完成数据库的分布式呢?

毫无疑问就是NewSQL,目前市面上用 NewSQL 的产品,大概就是 Google 的 Spanner,作为全球级的分布式数据库,可以说也是 NewSQL 的开山鼻祖。包括亚马逊的 Aurora,以及基于 Google Spanner 做的开源的实现的 CockroachDB, 它是使用的 PostgreSQL 协议,TiDB 是兼容了 MySQL 5.7 的协议,以及MySQL相关的一些生态,并且它还提供了实时的HTAP,就是OP+TP混合的业务支撑,以及这个金融级高可用的特性。所以说最终我们选择了 TiDB 作为我们业财一体化数据的存储方案。

说到 TiDB,它是分布式的关系型数据库,其实去中心化的分布式已经渗透到了各行各业。

举个例子,就是分布式在足球领域的应用,喜欢足球的同学应该比较了解,这个叫 Tiki-Taka 的体系,可以看到下面分别是乌拉圭、国际米兰、利物浦和巴萨四支球队的巅峰阵容的分布模型。提到巴萨,其实我作为一个巴萨的球迷,今天早上起床就看到各种朋友圈在刷屏,内心是很崩溃的,今天巴萨 2:8 输给了拜仁慕尼黑。其实拜仁慕尼黑,也是 Tiki-Taka 体系的受益者,因为我们知道包括现在拜仁或者曼城都是在巴萨的基础上,把Tiki-Taka体系发扬光大。说到 Tiki-Taka 战术体系,它其实是去中心化在足球领域的应用,它来源于荷兰的全攻全守,在巴萨得以发扬光大,把分布式,还有三角网络模型的数据理论引入进来,像 2010 年的西班牙,2014 年的德国,包括今天 8:2 血洗巴萨的拜仁慕尼黑,其实都是 Tiki-Taka 理念的受益者。因为足球它是团队运动,普通的球队可能靠的是明星,一流的球队它靠的是系统,最厉害的系统其实是根本不需要有核心的,可能是发展成这样分布式的这种模型。

这个是个题外话,其实就是觉得分布式实际上在很多领域已经有很多的应用。拿多点来说,多点其实 2015年创建的时候就是定位做一个分布式电商。在我们的业务里面,也有很多针对这个分布式的应用场景。比如说原来我们的仓储集中管理的模式,是对效率的很大的瓶颈,后来我们切换到这种到门店的模式,其实这就是分布式在业务场景的很典型的应用。

再说回到咱们这个系统,我们为什么会选择通过 TiDB 这种分布式的存储方案来解决我们业务上的一些问题,那就是咱们业务场景上以及我们遇到的一些痛点。

第一,财务数据要求强一致性的ACID事务。

第二,OLTP 的业务是占我们主要的部分,大概在60%~70%。

第三,OLAP 是我们的辅助,30%~40%的比例,这个业务对实时性要求比较高。

第四,真实的线上业务场景的请求是 TP+AP 是混合的,它在很多场景下分的并不是这么清,我们很难去用一个很明确的方式,把这两种业务完全给分开。所以说我们面临的业务场景就是怎么样能够把这个 TP 和 AP 的业务能够更有机的整合在一起。

我们发现 TiDB 4.0 在6月份正式推出,它提供了默认的悲观事务模式,这样对业务来说,可能我们不需要那么关注一致性数据冲突之类的问题。同时它支持大事务,最大的事务限制从100MB 提升到了10GB ,同时支持乐观事务和悲观事务两种模式。

最重要的一点是 TiDB 4.0 提供了 TiFlash 的列存副本,这个满足我们 RealTime-OLAP 的业务。针对 TiFlash,我们也进行了线上的性能验证,大家可以看到这是一个1.5亿行大表的聚合查询,通过执行计划,整个 SQL 走到 TiFlash 之后,能够在8秒多就得到响应。而相同的数据规模,在 MySQL 里面已经没办法去玩了,然后通过 Hive,可能也需要分钟级别,但是 TiFlash 的这个性能,让我们眼前一亮。

接下来借用 PingCAP 官网上面的架构图,介绍一下多点业财一体化 TiDB 的部署结构,我们目前在线上也是使用的这个 TiDB、TiKV、PD 三副本,然后再加上 TiFlash 的列存节点,同时我们也用到从 MySQL 同步到 TiDB 的 DM 的数据同步主键,包括从 TiDB 再往 MySQL 反向同步的,通过 TiDB Binlog,目前我们线上用的是 Drainer 主键。后面等新的版本发布,可能会切换到 TiCDC 的数据同步主键上。

说完线上的部署结构,下面再介绍一下 TiDB 在多点的上线规划。我们本着大胆尝试,小心求证,然后步步为营去进行分段迁移,整个上线规划,我们分成三个阶段:

第一阶段,MySQL 还是当成我们的主库,TiDB 作为 MySQL 的一个从库。

第二阶段是做一个切换,把 TiDB 当成主库,MySQL 作为从库。经过前两个阶段的充分验证之后,我们可以跳入到第三个阶段,所有的数据都存放到 TiDB 上,通过 TiDB 来提供我们所有的 TP 和 AP 的业务。具体看第一阶段,我们针对一个业务条线,将 TiDB 作为 MySQL 的从库,通过 TiDB 的 DM 组建进行数据同步,承接了我们财务池、凭证池的明细和列表的查询。第一阶段是进行数据和功能的验证,这一块还是比较顺利,我们大概花了一个多月的时间。

然后说一下我们用到的 TiDB 的 Data Migration 的同步主键,后面我们会讲 DM 同步所踩到的各种坑。经过第一个阶段的数据验证,目前我们线上是处于第二阶段的状态,针对另外一条业务条线,我们是把 TiDB 当成主库,然后通过 TiDB 提供的 TiKV 和 TiFlash 两种存储方式,把 TiKV 上的数据通过 TiCDC 主键,同步到 MySQL 备库,然后再将 MySQL 的数据通过我们多点的 DataHub 主键进行归档,到 TokuDB 里面。同时我们利用 TiDB 4.0 的 TiFlash 的列存,为我们的财务报表中心来提供 OLAP 业务。目前我们在线上已经验证了,用两个月的时间发现了各种各样的使用上的问题,也有一些 4.0 版本的 Bug,跟 PingCAP 这边的同学经常进行一些沟通,然后也非常感谢 PingCAP 的同学给我们提供的一些帮助。

在第二阶段,我们经过充分验证之后,后面的计划我们可能会把 MySQL 这一块备库拿掉,然后整个业务完全运行在 TiDB 集群上。

最后一个小节,就是我们实践中遇到的问题还有解决方案。第一个是写入热点的问题,通过社区里面,我了解到可能很多公司在使用 TiDB 的时候,都会踩到这个坑,这可能是一个常见的误区,因为我们毕竟是在MySQL上,有一些设计的方式如果是转到 TiDB 上,它的使用方式还是有很大的差异。比如说我们这个表结构的主键设计,按照 MySQL,我们可能很自然的把它设计成自增 ID 的方式,但是等我们切换到 TiDB 之后,它就会带来写入热点的问题。因为你知道 TiKV 里面,它是以 Region 作为最小存储单元,每个 Region 的大小是96MB,如果频繁的批量的一批数据进来,都是一个自增 ID 的话,它会集中写入到一个 Region 里面,这样就会给整个 TiKV 集群造成写入热点的问题。

具体的解决方案,我们用了 TiDB 4.0 的一个新特性,就是把自增主键转化成AUTO-RANDOM,一个随机自增的主键,它把数据打散到多个节点。这个问题应该是比较普遍的,因为我们设计基于分布式的数据库应用式,通常不应该把表的主键设计成自增主键,尽可能地用比如说 UID 或者其他的方式,去把这个数据进行哈希的散列分布,这样即使在有突发写入压力的时候,系统也能够去很好地去进行扩展。

我们看一下主键 ID 在我们这边切换成 Random 之后,前后的性能对比,还是比较明显。我们可以看到,原来在使用自增主键作为 ID 的时候,这个吞吐量是上不去的,然后性能也是比较差的,但切换之后,这个性能大概有4到5倍的提升,然后吞吐量也得到了很好的提升。

这个是 PingCAP 东旭老师的分享,我把它截取过来,自增主键和 UUID 在我们 TiDB dashboard 上面的一个可视化的监控,这个可以比较直观的看到,如果是用自增主键的这个方式,它是呈线性连续递增的趋势。如果是用 UUID 或者其他一些散列的 ID 作为主键的话,它的分布会比较均匀,不会造成写入热点的问题。

我们再看一下第二个问题,其实我们刚上 TiDB 的时候,我们发现单条 Insert 的性能,耗时大概是在10到20毫秒左右,它的事务 Commit 大概是在20到30毫秒左右,这个性能其实是比 MySQL 要差的,因为它毕竟是要做一些分布式的数据同步,还有 TSL 的这些数据,都会带来更多的耗时。但通过我们做批量 Insert,我们发现下面20条 Insert 语句,实际上它的性能耗时跟单条 Insert 是在一个量级的。所以我们得出一个结论,就是能批量的话尽量批量,使用批量插入,可以实现更好的写入性能,从TiDB服务器的角度来看,批量插入不仅可以减少客户端与TiDB服务器之间的RPC延迟,还可以减少 SQL 解析的时间。在 TiKV 内部,批量插入通过多个记录合并到一个 Raft 日志,减少了 Raft 信息的总数量。根据我们测试的经验,建议将批量的SQL保持在20~100行之间,这样做的好处是能减少事务的提交次数,对我们系统整体的性能和吞吐量都是有比较明显的提升。

第三个问题是同步链路的主键冲突的问题。这个就是刚才我们说到,我们第一阶段是通过DM 去做 MySQL 到 TiDB 的数据同步。在 MySQL 里面,我们是做了分表,每个分表采用的是自增ID,但这样通过 DM 同步到 TiDB 的时候,就会出现分表主键的冲突。现在我们有几种解决方案:

第一个是Column mapping,但是官方强烈不推荐。

第二个是去掉自增主键的主键属性,这个对于我们的业务来说,也是不能接受的。

第三个是使用联合主键。

第四个,我们采用的方式是通过 MySQL sequence 引擎来生成一个全局唯一的主键,这样就能保证我们一百个分表能够同步到 TiDB 的时候,主键冲突问题得以解决。

第四个问题是 Drainer 工具的一些相关限制,现在我们还是基于 Drainer 去把 TiDB Binlog 的数据同步到 MySQL,目前它还有一些限制,比如说 TiDB 同步到 MySQL 的时候,无法进行分表,它同步到 kafka 的时候,也只能使用一个 partition。而后期我们会等 TiCDC 这个方案成熟之后迁移到 TiCDC 的主键上。

最后还有一个问题,是咱们大 SQL 查询,导致了 TiDB 节点的一个 OOM。从监控上我们可以看到,它主要是 TiKVRPC 的延迟比较严重,而它主要的问题是 TiDB 节点,OOM 重启之后,导致这个应用的 tp99 飙高,然后会出现大量的 9002 TiKV sever timeout 异常,目前我们的解决方案是重启 TiDB 节点,然后业务就能自动恢复正常。TiDB 的 4.0.2 版本,其实已经修复了 TiKVRPC 的问题,后续我们也会持续关注这个版本的升级,通过 TiDB 的版本升级彻底解决这个问题。

多点使用 TiDB 的时间也不算长,大概也就是三个月左右的时间,目前我们在线上仍然是处于验证的阶段,但是我们也跑了很多真实的业务数据,后续会根据我们使用情况进行整体的切换。

最后总结一句话,我们希望借助 TiDB 这样的分布式存储方案来简化我们的系统架构,业务可以更复杂,但是我们系统要更简单。谢谢大家!

全程直播录像https://www.bilibili.com/video/BV1EA411n7wC/