端到端的实时计算:TiDB + Flink 最佳实际

作者简介

孙晓光,PingCAP Community Development 团队负责人,原知乎根底研发团队架构师,长期从事分布式系统相干研发工作,关注云原生技术。

本文来自孙晓光在 Apache Flink x TiDB Meetup · 北京站的演讲,次要分享了知乎在 TiDB x Flink 批流一体方面的局部工作,并以理论业务为例介绍如何充分利用两者的特点实现端对端实时计算的闭环交付

背景


上图是十分典型的实时数仓链路上的各个组件和数据,能够看到在很多中央 TiDB 和 Flink 都能够联合在一起去解决咱们的业务问题。比方 TiDB 的大本营是在线交易,所以 ODS 是能够利用 TiDB 的,后边的维表和利用数据存储等也都能够利用 TiDB。

实时业务场景

场景剖析


先看一下知乎这边一个理论的业务场景,知乎主站上的创作核心为创作者提供了内容交互数据的剖析能力。创作者能够在这看到本人创作的内容所取得的同意,评论,喜爱,珍藏的数据以及过来一段时间内这些数据的变动。

这些数据能够帮忙创作者更好地优化本人的创作。比方创作者对内容做了一些调整,而后发现交互数据开始产生显著的变动,创作者就能够基于这个信号对内容做相应的调整,去躲避不好的或者进一步发扬光大好的策略,因而对创作者具备十分大的价值。另外,这个数据越即时,创作者的策略调整就能越即时。比如说创作者刚追更了一篇答复,心愿立即就能够看到相干的数据变动,如果数据变动是正向的,下次就能够做更多相似的调整。或者形象进去过来好的调整都是什么,这样每次都能够基于之前的教训做出读者更喜爱的创作。

惋惜对创作者这么有价值的数据目前依然是不是实时的,大家能够在右上角看到数据更新的阐明。这是咱们在实时利用上还没有笼罩得足够好的一个证据,还是用传统的 T+1 的技术去实现的一个产品。

Flink 是咱们把相似创作核心这样的利用场景实时化必然的抉择,然而同大量应用 MySQL 的公司不同。知乎站上靠近 40% 的 MySQL 数据库曾经实现了到 TiDB 的迁徙,所以咱们必须将 TiDB 和 Flink 的实时计算能力做一个深刻的整合。在将来当 TiDB 成为咱们相对主力数据库的时刻,可能取得更好的综合收益。

接下来咱们探讨如何将内容交互数据的统计实时化,利用 TiDB 和 Flink 实现答复和文章这两种内容的喜爱、评论和同意数据的实时计算。

业务数据模型剖析


图中是对这些数据进行实时计算所须要关注的相干业务,这几个业务包含问答也就是右边的 QA,还有左边的专栏文章,以及评论、用户交互、视频答复。咱们心愿通过整合这些扩散在不同业务外面的数据,失去创作者中心里的用户交互的统计数据,而且咱们心愿它是实时的

首先咱们先放大一下问答业务,右边是 QA 业务里比拟根底的几个根本表,本质上咱们并不需要为计算交互信息理解到所有表所有的细节,只须要关注左边这几张表的局部字段就能够了。从这些表里咱们只须要晓得答复的 id,这个答复创作者的 member_id 还有被点赞的答复 id,就能够残缺地计算某一个人的某一个答复有多少点赞。

与此类似的是专栏文章,这边同样列出了一些根底表。如果要去做专栏文章的点赞这件事件的实时计算,咱们关注 article 和 article_vote 这两张表,利用 member_id、id 和 vote 字段能够非常容易的计算失去文章的点赞数。

除了在业务零碎内的点赞交互数据,其它类型的交互数据扩散在多个不同的业务零碎中。比方评论零碎的 comment_relation 表,视频答复的 vote 表,还有其它交互的 reaction 表。基于这些用户的行为数据,再加上内容数据就可能计算失去用户创作的残缺交互数据了。

从业务模型上能够失去交互数据计算的实质是把各种不同类型的内容和各种交互行为的数据作为源表,而后依照对这些数据以内容的 ID 分组进行聚合计算。比如说点赞就是一个 count 计算,因为表里一行数据就是一个点赞。如果说它是一个分值,那么这个数据的计算就是 sum。在拿到所有内容和所有交互聚合的后果后,再次同内容表做一个左连贯就能拿到最初的计算结果了。

传统解决方案


在开始讲 Flink 的计算之前,咱们能够先看看没有 Flink,同样的实时利用是什么样的开发模式。知乎外部有一套本人过来积攒的技术框架去做这样的事件驱动计算,如果用这样的技术做实时计算,开发的形式是上图这个样子。

业务工程师须要用本人相熟的语言和框架来开发两头红色的这些基于音讯零碎的 worker,对拿到的实时数据变动事件进行补数和聚合操作,再将计算失去的后果以事后约定好的格局发送到音讯零碎。最初用一个最终的 worker,将内容源表和多个上游 worker 的实时计算结果拼接在一起失去最终的计算结果并保留到上游。这样咱们就能够基于比拟传统的技术来实现实时利用。在这种开发方式下,业务工程师须要关注多个 worker 的实现,和不同零碎之间数据传递的格局。数据库和音讯零碎由平台团队来保护,对于工程师来说没有额定的学习老本,学习成本低和易于了解是这种传统开发方式的长处。

这种开发方式存在着一些问题。比方下面图里有 5 个 worker,worker 程序首先是一个音讯零碎的 consumer,它须要依据业务需要对接管到的实时数据进行聚合计算,并填充必要的维度数据。在保障这些计算逻辑的正确性之后,还要把这些计算的后果正确的发送到音讯零碎的上游 topic 中。不夸大地讲这样的一个程序至多须要 1000 行的工作量,5 个这样的 worker 不管从治理还是开发甚至是保护方面的老本都是十分高的。另外,这些业务团队自行开发的 worker 程序须要由开发者自行解决规模扩展性问题,还须要独立地预留资源应答突发流量造成全局的资源节约。难以在正当的老本下均衡弹性有余带来的零碎规模问题。

Flink 解决方案


作为比照,如果用 Flink 去开发整个利用的构造会变得非常简单。当咱们应用 SQL 来开发利用时,得益于更高的可维护性和可了解性,咱们可能在不损失可维护性的状况下将这个利用的全副逻辑放在一个 job 里对立保护。不管从业务团队的开发成本还是是保护老本角度看都是更优的抉择。

如上图所示,这是答复用户交互数据的实时计算逻辑用 Flink SQL 来开发,最初失去的 SQL 。利用 SQL 这种申明式的形式开发业务逻辑,非常容易地了解和验证它的正确性。

接下来看一下这种形式的劣势
首先,繁多 SQL 开发可维护性高,组件数少,保护成本低。

其次,Flink 对立解决零碎级问题,业务层无需关怀扩展性、高可用、性能优化和正确性的问题,极大地升高了解决这些问题的累赘。

最初,SQL 开发简直没有额定的学习老本。为什么说 “简直”,这个业务是典型的在线工程师的工作畛域,而在线工程师肯定很相熟 SQL。但他们日常工作中应用到的 SQL 范畴和大数据工程师应用的 SQL 范畴还存在着些许的不同。所以不能说 Flink SQL 没有学习老本,但这个老本非常低,学习曲线也十分平缓

任何事件都有两面性,基于 Flink 开发实时利用也须要解决上面的这些问题:
首先,SQL 的表达力不是有限的,肯定会有一些业务逻辑和业务场景很难拿目前的 Flink SQL 齐全笼罩。如果咱们用 28 法令来看这个问题,SQL 加上一些 UDF 就可能解决其中 80% 规范 Flink SQL 无奈笼罩的问题,最初还剩下无奈解决的 20% 问题有 DataStream API 进行兜底,确保整个业务问题可能在一个 Flink 技术栈上全副解决。

另外,Flink SQL 开发简略,但 Flink 零碎自身的复杂度并不低。这些复杂度对许多业务工程师来说是一个十分重的累赘,他们并不心愿了解 Flink 如何工作如何保护。他们更心愿在一个可自助操作的平台上编写 SQL 解决本人的畛域问题,防止关注运维 Flink 这样一个简单的问题。对此咱们须要以平台化的形式升高业务接入零碎的老本,利用技术手段和规模效应把单个业务的老本降到正当程度。

所以问题尽管存在,但都有适合的方法解决。

POC Demo

刚刚讲到的创作核心实时利用还处于 POC 过程,POC 应用知乎站上理论的表构造,大家能够从 POC Demo 感触业务工程师可能基于 Flink 实现什么,实现的成果,以及正确性是否有保障。

后面看到的局部只包含了在线业务的技术栈范畴,也就是说源数据在 TiDB 上,通过 Flink 解决后的计算结果也存储在 TiDB,端到端的解决实时计算问题。如果须要在计算中引入离线产生的数据怎么办?比方咱们想要在计算结果中蕴含每个内容的实时 PV,咱们能够把大数据系统中的 PV history 的表和 PV 实时流进行一个 union 操作,再依照内容 ID sum 在一起,就能够失去实时的内容维度 PV 数据。传统形式的实现可能要写 1-2 个 worker,当初只须要在 Flink job 中加几行 SQL 代码就能够实现。

可能的疑难


如果不相熟 Flink 不相熟大数据的同学当初可能会有一些疑难,接下来咱们 一一 看下这几大类疑难。

第一个就是计算到底怎么做的,在 TP 零碎外面都是客户端申请触发计算,Flink 的计算是如何触发的呢?

答案是在事件触发时进行计算,每产生一个 event 就会触发一次计算。对数据库里任何一行的变动都触发一次计算,触发的颗粒度可能太细导致老本过高。所以 Flink 里边有 mini batch 的优化,能够攒一批变动事件以批的模式驱动计算。如果是对于时间段内数据的计算,还能够用 window 机制,应用 Watermark/Trigger 来触发计算并取得后果。如果计算的过程中须要保护状态,那么 Flink runtime 会负责管理状态数据。

第二个问题是 window 在哪里

并不是所有业务都必须要用 window,当计算和触发逻辑跟时间段没有关系的时候,就不须要应用 window。比方这里的 demo 场景计算逻辑由数据变更触发状态永恒无效,整个逻辑中不须要应用 window。

如果须要用 window 的时候怎么解决早退的事件?这里有 discard 和 retract 两个次要的策略解决早退事件,当遇到早退事件时开发者能够抉择扔掉早退的数据,也能够用 retract 机制去解决。除此以外咱们还能够用自定义的逻辑来解决早退事件。总之 window 的作用是帮助用户以预置的窗口策略,将落在某一时间段内的数据攒在一起触发计算,在有超出窗口的提早数据达到时,依照利用冀望的形式进行解决。

第三是开发上手难度如何?

Streaming SQL 在规范 SQL 的根底上建设,它的学习过程是渐进性的、平缓的。再配合上易扩大的 UDF 能力,可能解决大多数单纯应用 Flink SQL 无奈解决的问题,多数只适宜用编写代码形式解决的问题依然有 Flink 的 DataStream API 能够解决。

最初 TiDB 和 Flink 如何保障计算结果的正确性

TiDB 是一个默认快照隔离级别的数据库,咱们可能间接拿到某个工夫点的静止全局快照状态。在 SI 隔离级别下保障整个数据流的正确性非常容易。咱们只须要拿到一个工夫戳,并读取这个工夫戳时刻全副数据的静止快照,解决完快照数据后对接上 CDC 里所有工夫戳之后产生的 CDC event。在 Flink 的角度这就是一个流批一体的动静表,Flink 本身的机制可能保障流入到零碎中事件计算结果的正确性。

TiDB x Flink 批流一体

上面来理解在做 POC 过程中,咱们在 TiDB 和 Flink 整合方面发展了哪些工作,以及这些整合工作带来的能力处于什么样的状态。

TiDB as MySQL


作为一款和 MySQL 兼容的分布式数据库,即使咱们不做 TiDB 到 Flink 的原生整合,咱们依然可能以图示的形式把 TiDB 当作一个大号的 MySQL 和 Flink 配合在一起应用。这个架构下所有批工作流量都须要先过 LB ,而后再通过 TiDB 最初依据读取的数据范畴拜访相应的 TiKV 节点。而流工作流量是利用 TiCDC 从 TiKV 抓取数据变更事件,经由音讯零碎交付给 Flink 进行解决。

这种非原生对接的应用形式尽管能工作,然而在许多场景下无奈充分利用 TiDB 架构的特点做更极致的老本优化和价值放大。比方在流量稳定大的利用场景,因为所有的流量要在整个门路上,从 LB 到 TiDB 到 TiKV 的每一层走一遍。而流量会对每一个过程产生全量的冲击,为了保障在峰值流量冲击下的业务体现,咱们不得不依照峰值流量去准备所有的资源,造成了极大的资源节约。

还有大数据场景常常遇到的数据歪斜问题。在没有业务知识的前提下,面对业务各种各样的表结构设计和业务数据分布特色,咱们很难以对立的形式自动化地解决所有的数据歪斜问题。实际上在目前版本的 Flink JDBC connector 上,如果表主键不是整数类型且不存在分区表,那么 Flink 的 source 就只能以 1 个并行度去解决全副数据。这在面对 TiDB 上海量存储业务数据的场景是十分艰难的。

最初,咱们无奈间接利用为 MySQL 设计的 flink-cdc-connector 我的项目为 TiDB 提供流和批一体的 connector。那么在许多须要这个能力的利用场景中,业务方就须要本人去关注批和流数据对立解决的问题。

TiDB 适配

为了解决在 Flink 中应用非原生 TiDB 反对遇到的这些缺点,咱们充分利用了 TiDB 架构的特点,为 TiDB 开发了原生的 Flink Connector,更好地服务于 Flink 的宽泛计算场景。

首先是针对大流量冲击场景的资源优化。在 TiDB 中有零碎表能够得悉整个集群所有 TiDB 服务器的地址和端口。咱们实现了一个十分薄的代理到原生 MySQL JDBC driver 的 JDBC driver,利用零碎表中的集群拓扑信息间接在客户端实现了负载平衡。通过直连 TiDB-server 的形式咱们实现了负载均衡器的流量绕行,只有首次和后续定期更新集群信息的小数据量申请会通过负载均衡器,真正的大流量数据读写申请都通过到 TiDB 的间接连贯来承载。

接下来是防止 TiDB-server 的流量冲击。在对 TiDB 上的数据进行读取操作时,咱们可能让客户端从 PD 上获取到须要读取数据范畴内的所有 region 信息。通过间接连贯 region 背地 TiKV 节点的形式,咱们可能将所有读的流量绕行 TiDB,极大地升高 TiDB 层负载,节约硬件资源老本。在实现 TiDB 绕行计划时,咱们实现了同 TiDB 统一的 predicate 下推和 projection 下推能力,TiDB connector 对 TiKV 产生的压力同真正的 TiDB 十分靠近,不会对 TiKV 产生额定的累赘。

下一个是利用 placement rules 让一批物理隔离的 TiKV 节点只承载 follower 角色的数据正本,再配合 follower read 能力咱们可能在没有付出额定服务器老本的状况下将实时计算的大流量负载,同在线的业务负载物理隔离开。让大家可能释怀的在一个 TiDB 集群上同时撑持在线业务和大数据业务。

接下来是业务无关的数据平衡能力。如后面所讲,在没有业务层畛域常识和数据分布信息的状况下,JDBC 形式只能对整数主键的数据进行近似平衡的拆分,而对于非整数其它类型主键的非分区表就只能序列化的解决所有的数据。在 TiDB 这种海量数据存储的状况下,不论是单并发还是不平衡都会导致工作执行效率低的问题。而后面介绍 TiDB 绕行的时候大家也看到了,TiDB connector 的工作拆分粒度是 region 级别。而 region 尺寸是由 TiKV 依照一个最优的尺寸去主动放弃的,所以对于任意一个表构造,咱们都可能做到工作单元的均衡性,在无任何业务知识的状况下完全避免数据歪斜问题

接下来是 TiDB connector 原生实现的批流一体能力。它的原理是利用 TiDB 的快照隔离级别拿到一个数据的全局快照,在解决完这个快照数据后,再接入所有 commit 版本号大于快照版本号的 CDC 事件。通过这个内嵌的流批一体能力,在数据处理工作失去极大简化的同时,还能确保整个实时计算流水线的相对正确性。

最初为了进一步优化 TiDB 大流量写能力带来的 CDC 流量冲击。咱们还对 TiCDC 的数据编解码格局做了二进制编码优化。大家常常在 TiCDC 中应用的 canel json 和 open protocol 都是 JSON 的格局,然而这些以 JSON 为物理格局的协定都偏向于尺寸更大和编解码 CPU 耗费过大的问题。而全新设计的 binary protocol 充分利用了 CDC 数据的一些特点,在典型场景下可能将数据尺寸压缩到 open protocol 的 42%,同时晋升 encode 速度到靠近原来的 6 倍,decode 速度到靠近原来的 10 倍。

以上就是咱们在 TiDB 和 Flink 原生整合方面所做的工作,这些工作很好地解决了利用 TiDB 和 Flink 实现端到端实时计算时所遇到的一些问题。

在 TiDB connector 的帮忙下,TiDB 和 Flink 配合的形式变成图上的这个样子。读的流量绕行负载平衡和 tidb-server,间接申请 TiKV 的 follower 节点上。写的流量目前是借助 JDBC 实现,但在客户端负载平衡能力的帮忙下,咱们依然可能绕行负载均衡器,升高负载均衡器的老本。

以后 Flink 曾经在知乎领有许多落地的利用场景了。咱们基于 Flink 建设了数据集成平台,并利用 TiDB connector 提供了 TiDB to Hive 和 Hive to TiDB 的能力,解决了 ODS 层数据同步以及离线计算的数据在线提供服务的同步问题。在数据集成平台之外还有许其余的实时利用,比方商业团队的点击数据处理程序。再比方搜寻里的时效性剖析,还有要害指标的实时数仓。最初还有一些业务利用 Flink 将实时行为数据落到 TiDB 供在线查问。

瞻望

除了以上提到的这些停顿,咱们还有许多能够改善的方面,为 TiDB 和 Flink 的用户发明更多的价值。接下来就让咱们看下将来还有哪些能够持续开掘价值的方向。

TiDB x Flink 外围能力加强


首先是全局事务反对。目前基于 JDBC 实现的 Flink sink 存在同 JDBC connector 一样的局限,无奈实现分布式的全局事务。此外应用 JDBC 连贯 TiDB 的同时也带来了 TiDB 最大事务尺寸的限度,无奈反对超大事务的写入。当咱们遇到有全局可见性要求或相似银行跑批工作的需要时,目前的 TiDB connector 依然无奈提供现实的能力。咱们心愿接下来实现原生的写入能力,间接以分布式的形式向 TiKV 上进行两步提交,从而实现全局大事务写入能力。全局事务不仅仅能带来事务隔离和大事务的收益,咱们还能够通过将所有大流量的申请绕行 TiDB 的形式,彻底开释 tidb-server 的压力,彻底杜绝没必要的资源节约。

还有一个改良方向是原生 lookup table 的反对,目前这一块儿也是基于 JDBC connector 实现的。尽管维表查问的吞吐通常不会特地大,但 bypass TiDB 依然可能取得 latency 上的额定收益。而这个晋升可能为流计算零碎计算吞吐的晋升和防止事件积压起到十分要害的侧面作用。

最初还有一个尚未明确收益的改良方向是基于 TiKV 的 state backend,可能会解决一些场景下 checkpoint 慢的问题

更多利用场景


在领有了 TiDB 原生反对具备了许多新的能力之后,咱们能够畅想将来 TiDB x Flink 可能撑持更多的利用场景。

比方以后的数据集成平台只反对批模式的数据抽取工作,在 TiDB 流批一体能力的帮忙下,咱们可能配合 Hudi 或 Iceberg 以非常低的老本实现所有 ODS 层数据的实时化。如果所有 ODS 层数据具备了实时的能力,数仓同学在思考实时数仓的建设门路时就没有太多的前置依赖了。配合常见的实时埋点数据和实时 ODS 数据,齐全依照业务价值的高下去安顿数仓的实时化建设。

在实时数仓之外,随着技术的成熟还会有更多的实时利用场景诞生。比方咱们可能以极低的老本从站上现有内容产出实时的内容池。再比方搜索引擎的实时索引更新,当然还有 demo 的内容交互数据实时统计等等。置信在知乎的 Flink SQL 平台建设实现后,肯定会产生越来越多基于 TiDB x Flink 端到端的技术体系笼罩的利用场景。

最初如果大家对 TiDB x Flink 的生态整合或者 TiDB 在整个大数据生态的能力,能够在 GitHub 上关注 TiBigData 我的项目。首先欢送大家在理论场景中尝试应用这个我的项目,如果在应用中遇到问题或有意见建议能够随时给我的项目提 issue。最初也心愿有更多的开发者参加到这个我的项目的开发,咱们一起让它为 TiDB 在大数据畛域提供成熟欠缺的一站式解决方案。