AbutionGraph+Flink带您构建流批一体的增强流式知识图谱数据仓库

2022-04-18 00:00:00 数据 数据库 写入 计算 实时


AbutionGraph是众多国产数据库中新兴的一员,且是一款GraphHOLAP的实时知识图谱数据仓库,将在本文中介绍Abution如何结合Flink(无缝对接Flink-1.12.1)构建增强的流批一体实时数仓。

 

Abution 结合 Flink Spark 的差异

AbutionGraph初实现了跟Spark的无缝对接,使得我们可以很方便的将图数据与Spark的数据结构GraphX、GraphFrames、DataFrame、RDD互转,格式的转化已经覆盖了Spark的所有格式。对于流式处理,Spark和Flink一样,都具有流和批处理能力,不同的是,Spark的流是微批流,我们需要的延迟越低,额外的开销越大,甚至很难做到亚秒及的延迟响应。而Flink是真实的事件流,允许我们以更好的性能和更快的速度对接入的数据进行处理,对数据源端的实时处理支持比较友好。对于批处理和图计算,图挖掘算法(机器学习模型)绝大多数都是离线计算与分析。Spark的功能要更加的完善,AbutionGraph是图数据库,而Spark具有知名的图计算框架GraphX,以及第三方图计算框架GraphFrames,这在Flink中是还不具有或是实验性的功能。与Spark的无缝衔接,不仅可以满足存储与大规模计算的分离,也可以让我们的Spark工程师很方便的操作Abution图数据,中间的数据格式转换都交给Abution,让用户只需关注业务的实现。使用Spark的另外一点优势,我们可以在GraphX上开发很多图计算算法(如:社区划分、网络分层、分类和聚类等等),Abution已具有一个13大类70种图挖掘算法的平台,并且扩展了Spark的云服务,实现模型一键上传更新与调用,这部分内容较多,将在另一篇文章中介绍。

简而言之,Spark与Flink各具优势,还没有谁可以一统流批计算的天下。对于我们今天要介绍的,是Flink的强项,实时数据流处理,虽然结合Spark也可以实现,但是没有Flink友好,多一种方式就是为Abution的用户提供多一种选择,所以我们选择了拥抱大数据时代的趋势。Flink优势如:高容错的计算状态管理、支持事件时间(Event Time)的概念、同时支持高吞吐,低延迟,高性能的分布式流式数据处理框架,这三方面优势结合AbutionGraph的多维、时序和动态的特有功能,使得我们可以构建一个更加强大的实时流式动态的数据仓库

 

**传统 Flink **+ Druid/ES/HBase 构建的实时数仓

采用Kafka+Flink+Druid/ES/Hbase构建实时数仓可能是过去一年看到非常主流的实时数仓建设方案了,端到端的实时流处理,一端是采集到的原始数据(Kafka),另一端是报表/标签/接口这些对数据的呈现与应用(Druid/ES/Hbase),连接两端的是中间实时流(Flink)。更优化的端到端,源表是Kafka,目标表也是Kafka,统一经过Kafka后再导入到HBase。下图来自Flink推文:

1)首先说说中间过程的实时流处理Flink,阶段(ETL)是从各种数据源读取数据到Flink统一处理;第二阶段(流式汇总)是整个数仓体系的核心,负责对留存于Flink中的所有数据执行汇总,并定时输出到数据库端持久化。那么问题来了,流式汇总的数据量可能很大,如以天为粒度,那么就需要足够强大的服务器资源(内存和CPU)来存储计算这一天的所有数据,否则数据不全还需要从数据库中再拉取补全来合并实时流再计算,新拉取的数据是继续保存在内存呢?还是每次需要都重新拉取?若经常或每秒就需要拉取很多次可能会同时降低数据库和Flink的性能。

2)再说说末端的存储,经过Flink产出的数据一般是计算好了的,经过Kafka把计算好的结果持久化到Hbase/MySQL,供上层应用和下游服务查询结果直接使用。比如:在Flink中实时计算今日每个银行卡号的总交易次数和总交易额(银行每日活跃账户数不定),之后写入到Hbase/Druid。那么问题来了,这T+1天的延迟如何解决?虽然Druid支持聚合查询,Hbase可以查询明细,那么数据重新关联计算是不是需要分别从Druid和Hbase中抽取对应的数据到Flink实时流再计算一遍新的结果?然后再将结果更新到Hbase/Druid,供下游应用使用。这些延迟虽然没有T+1天大,但也很难是秒响应吧。

3)后说说整体架构体系,涉及的技术很多,每种业务分类都有各自的数据库,报表分析(Druid/MySql)、用户画像(ES)、接口服务(Redis/Hbase),如果各自业务独立,这样的架构没有问题,若各个业务交叉,如用户画像肯定离不开用户行为的分析(一般是报表指标),也就是ES与Druid的Join,那么查询计算的业务实现就会变得很复杂。况且,所有的业务库都是使用的一个数据源,都是从一个Kafka的原始表来,很多业务库的拆分导致Flink端变得很复杂,需要管理各种数据库的各种表数据ETL,数据的状态监控与更新或许会让人头疼。企业需要一个很大的研发团队来维持它的更新与运维,也需要有能力为继如此规模的计算资源,我相信有很多中小型企业是没办法做到的。

 

**Flink **+ AbutionGraph 构建的简洁轻量实时数仓

从架构上可以看到,AbutionGraph合并了输出存储端,这是因为Abution中同时具有多种数据库的建模方式,可以在一个数据库中实现所有存储模型,实时汇总表(聚合模型-代替Druid的报表分析,且是多维度的)、历史明细表(静态模型-代替Hbase/MySQL,存储历史明细)、知识图谱表(图模型,天生的用户画像模型,代替ES的搜索-关联实时汇总表与历史明细表)。

现在,我们有了一个很简洁的系统架构来实现之前的业务,这是一个真正一步到位的端到端实时数据流处理,一端是采集到的原始数据(Kafka),另一端是报表/标签/接口这些对数据的呈现与应用(AbutionGraph),中间只有一个Flink平台,用来实现简单的数据ETL。细心的你可能发现了,另一个Flink平台(流式汇总)没有了,因为对接了Abution,Abution是一款具有预计算技术的数据仓库,所以这部分功能可以在Abution中自动完成,省去了中间与另一个Flink+Kafka的集群对接工作,计算成本得以降低,时效性也会大幅度提升。对于末端的应用,AbutionGraph是图数据库,用户画像的更友好支持已不需要再介绍;Abution的OLAP特性可以很好的支撑报表分析和即席查询应用,可以保障大多数的查询都能在1秒内返回,这对比使用Flink的“流式汇总”步骤优势在于,可以查询和管理全量数据,而不是内存中的少量数据;Abution具有图数据中台,具有所有的数据库查询接口,也可以很方便的扩展业务接口服务,同时也是一个无需管理维护的分布式key-value内存数据库,可以应用到集群的剩余内存提供分布式内存和计算服务。

可以看到,使用Flink+AbutionGraph构建的实时数仓,不仅功能一个没落下,也更简洁轻量,可以让更多的中小型企业低成本使用上实时数仓在自己的项目场景中。

 

AbutionGraph Flink 的联系

AbutionGraph开始接入的Flink是1.7,在Flink-1.9做了全面的稳定性测试和优化,显然,AbutionGraph和Flink都很年轻,都处于高速发展期,因其都属于流式技术,且都属于Hadoop生态中的一员,Flink是实时的流式计算引擎,而AbutionGraph则是实时的数据仓库,天生的上下游互补关系,使得Abution团队一直在跟进Flink的进展和实现Flink的无缝对接,来提升AbutionGraph的技术竞争力。

在功能使用上,Abution使用Flink的多数据源接入,可以轻松的对接Kafka、RocketMQ、Socket、HDFS、MySQL等等外部数据源,实现企业数据生态的完美结合,也很轻易的迁移老系统,与新系统整合。因为AbutionGraph本身就是一款实时数据仓库产品了,所以Abution和Flink具有一定的功能重叠,首先体现在数据的聚合上,Abution具有数据预聚合功能,并且粒度自由定义,这与Flink的groupBy实现类似。

不同的是,Flink是内存计算,管理的数据量受限于集群内存资源,只能管理一部分实时数据;而Abution是数据库,内存计算只会涉及到少量数据的合并更新,并且是持久化的,可以管理历史的+实时的全部数据。Abution不用关心数据的聚合情况,它是构建graph时的schema中定义的,只需要告知Abution在属性字段上应用的聚合函数即可,如Max、Min、Last、TopN、DistincCount等等,当数据从Flink端(Kafka/Mq等)流入Abution,Abution即时间完成聚合更新,计算资源的占用稳定,抖动很小;

另外,与上一点不同类似,在ETL的数据转换上,Abution是直接接入Kafka/Mq数据流的,可能并不需要在Flink上编写任何代码我们就可以完成数据的写入,然后计算交给Abution自动完成。Flink的优点之一是具有灵活丰富的数据处理函数,这些丰富的处理函数无疑需要熟练Flink的工程师才可以操作,有一定的上手门槛。而Abution的用户未必具有的Flink知识,甚至于是Flink小白,这样同时也增加了Abution的使用门槛。在大多数情况下,Abution实现业务场景并不需要复杂的操作,仅仅是做简单的数据清洗入库即可,这么简单的行为就不必让开发者专门去写几行复杂的Flink代码了,且不方便维护和管理,Abution简化了这些步骤,所有的ETL数据转换都可以使用一个普通的Java/Scala函数来代替,这个数据处理函数您可以仅仅使用简单的编程语义就可以完成将您的外部数据转化为Abution图数据并写入,执行代码时,Abution会将这部分操作并行到Flink下执行,这样一来,您的开发工作不必关心Flink的事情,甚至于一点Flink知识都没有就可以使用到Flink的引擎,这将大大的减少学习和开发成本。另外的好处是,您可以不用修改任何Abution的代码就可以脱离Flink,实现代码重用。

而对于Flink的工程师,您就可以随意的发挥了,没有强绑定的使用规则,一切以优化的业务实现为主,您还可以使用Flink的计算能力提前聚合一些图数据,减轻Abution的IO,提高写入和查询效率,特别是在物联网传感器监控场景(包括电网能源场景),由于数据更新太快(一秒内更新几次),那么您就可以采用这种内存的计算方式,实时监控后再落地给Abution做后续的操作,AbutionGrapha的图数据可以脱离数据库实现聚合,或者是在Flink中实现聚合,可以有效减少数据写入量,相关功能的功能都已备好,可以方便的使用。

 

大数据实时数据流式处理技术 Flink 进展

在2020的尾声,Apache Flink 1.12.0 版本正式发布。这版本极大地提高了 Flink 的可用性,并且简化(且统一)了 Flink 的整个 API 栈。其中一些比较重要的修改包括:

  • 在 DataStream API 上添加了高效的批执行模式的支持。这是批处理和流处理实现真正统一的运行时的一个重要里程碑。
  • 扩展了 Kafka SQL connector,使其可以在 upsert 模式下工作,并且支持在 SQL DDL 中处理 connector 的 metadata。现在,时态表 Join 可以完全用 SQL 来表示,不再依赖于 Table API 了。
  • PyFlink 中添加了对于 DataStream API 的支持,将 PyFlink 扩展到了更复杂的场景,比如需要对状态或者定时器 timer 进行细粒度控制的场景。除此之外,现在原生支持将 PyFlink 作业部署到 Kubernetes上。

一些功能的改变:

  • Flink-1.12版本不再支持Kafka010,AbutionGraph也在2.6版本中删除了对Kafka010和Kafka011的支持,因为它们已经过于老旧,官方的介绍是在新版的Kafka中具有向后兼容的特性;

目前,AbutionGraph中使用的是2021年的新版本,且可以向低版本兼容(因为我们没有复杂的依赖),亦可向之后的Flink版本兼容。

 

AbutionGraph Flink 的优化

Flink是一个流式框架,与Spark的微批次不同,它的数据是源源不断的一条一条的,若将Flink中的数据写入Abution,则每条数据都会提交一次写入操作,并将该条数据写到数据库中,且Flink所提供的扩展接口中也是一条一条的数据迭代,这么做显然非常低效,也远远不可能到达Abution的IO瓶颈。

鉴于此,我们对Flink端的输出进行了改造,把单条的输出更改为批量的流式输出,加大Abution的吞吐量(Abution具有很好的并行性,在服务器资源允许的情况下,能承受的住大规模的实时写入而不会出现运行故障,假设输出端是MySQL,那肯定很难驾驭得了Flink)。要想Abution-Flink-Connection的写入不是一条一条的,就需要一个容器来存储一条一条的数据,使其组装成流批。所以,在AbutionGraph中加入了流缓冲,默认缓冲区大小为100w条图数据(理论上可以使Abution每次提交时,同一时间入库100w条图数据),若超过此值将阻塞等待Abution写入程序空闲,好处是可以避免数据丢失和OOM,改变缓冲区大小的参数是abution.flink.config.max-queue-size,您可以在写入开始前修改它为集群资源的性能佳值。

我们将在线流改成了在线流批,那么就有疑问了,假设数据量没达到100w,是不是永远都不会将缓冲中的数据写入Abution。答案肯定是否定的,Abution会不定时的刷新缓存空间(取决于正在写入的状态),当数据量很小,甚至只有一条,Abution也可以立即添加此数据,而不是阻塞并等待满一批再写入。

经过Flink数据流的流批改造后,AbutionGraph从Flink端接入的写入性能得到了几个数量级的提升,从此再也不害怕突然的数据暴增带来的IO节点崩溃。(与图无关)

 

使用 Flink 写入 AbutionGraph 的性能

我们在一台16G4H(不够AbutionGraph每个进程分一个核,存在多个数据库进程共同使用一个核的情况)的低配阿里云服务器上进行写入测试,即可以达到23w/s实体与关系的峰值写入,并且各个组件都运行良好。

AbutionGraph还具有很好的线性扩展能力,增加节点和资源就可以实现水平扩容,增加客户端或者提高并行度就可以提高读写性能,更多的读写性能基准测试报告可以查看AbutionGraph的官网资料。

 

总结和展望

在本文中,我们介绍了如何使用Flink为AbutionGraph增强流批一体的实时写入能力,同时也为构建更强大的在线流应用打下了坚实基础,是您降本增效的选择之一。

流和批是数据集成的两种应用形态,在我们的场景中,从Flink流入Abution的数据可能不是是源头,从Flink之前的Kafka接入才是源头,在Kafka端,我们也可以针对业务需要做一些控制,比如分布式事务,因为AbutionGraph是OLAP数据库,天生对事务支持不友好,好在如今的分布式事务解决方案已经很成熟,且也有了很多种方式可选,使得我们可以不必在OLAP里做事务,因为很多场景只是分析,并不需要数据回滚,那么OLAP场景的性能可以始终维持在一个很高的水平,等到我们真正需要到数据回滚等功能了,也可以很方便的通过第三方工具集成进来,如Kafka的CDC(捕获数据变化)平台Debezium就可以轻易的实现这件事,并且是在数据的源头就做了这件事,灵活性和可控制性也更强,而不需要在Abution数据库中做拉低吞吐量。此外,Flink也正在致力于摆脱Kafka的功能约束,在内部实现CDC功能,相信在不久的将来我们可以更加便捷高效的在Flink中就实现这类需求。

 

相关文章