头图

摘要:本文整理自抖音集团数据工程师陆魏老师和流式计算工程冯向宇老师,在Flink Forward Asia 2024 流式湖仓(二)专场的分享。内容分为以下三个部分:

1、背景及现状

2、Paimon湖仓实践

3、Paimon技术优化

01. 背景及现状

抖音生活服务是抖音集团重要的业务板块,主要通过抖音直播、短视频、团购以及本地同城等流量入口,引导用户进行线上下单支付,并进而引导用户线下消费履约,最终完成线上到线下的引流操作。近些年,抖音生活服务迎来了快速增长,伴随业务增长,生活服务实时数仓技术也迎来关键的技术转变。

1.1 生服实时数仓技术演变 

第一阶段——实时数仓阶段,早期采用 Flink + MQ + 在线存储,经 Flink 实时数仓多层处理后,将处理好的数据写到在线存储层,为下游应用提供数据服务。其优点是数据时效性高、秒级延迟,缺点是开发成本高、可扩展性差、管理难度大。第二阶段——近实时数仓阶段,引入 OLAP 引擎加快开发效率。实践中沉淀了 Doris 分钟级以及 Presto + Hive 小时级近实时方案,该架构的优点是开发灵活、交付快,开发体验近似离线开发体验,缺点是成本太高、保存时间有限、海量数据无法满足,且实时和离线的数据割裂明显。第三阶段——湖仓探索阶段,引入数据湖流批存储一体,提高开发效率,收敛众多技术栈,节约计算存储资源。优点是数据湖设计优良,能提高开发效率,流批存储一体,节省计算存储资源,缺点是目前数据湖尚处于初级阶段,其引擎本身、生态以及工具都还在完善之中。

1.2 当前数仓架构

下面这张图是当前生活服务数仓架构的简略图。可以看到,生活服务数仓目前主要还是 Lambda 架构。离线通过 Spark 以及 Hive 完成开发交付;实时数仓则主要依靠 Flink + Kafka 完成开发交付。公共维度层主要使用 Redis 来存储。

这套实时数仓架构目前支撑的实时应用包括直播实时大屏、商家 GMV 实时展示、实时榜单以及达人的数据中心等;近实时数仓主要通过使用 Doris、Presto+Hive 并结合内部数据开发平台的调度系统,完成分钟级和小时级的数据交付,这套架构目前支撑的数据应用主要有治理体验三率、缺货、销售运营分析以及用户增长等;数据湖仓目前使用 Flink+Paimon 完成开发交付,湖仓支撑的数据应用主要有内容治理评价、举报、营销标签圈选、审核平台、判责平台以及消费者服务体验等。

1.3 当前实时数仓问题及痛点

当前实时数仓存在的问题和痛点如下:

一是开发效率低,MQ存储有局限性,无法很好满足实时数仓诉求,需要新建诸多配套任务,比如为了探查和拓维需要很多 Dump2Hive 以及 Dump2Redis 任务。二是技术栈众多,引入了众多组件,学习成本非常高,管理与运维难度大。三是资源浪费,大量的 Dump2Hive 以及 Dump2Redis 操作带来巨大资源浪费。四是稳定性差,实时拓维强依赖于 Redis 存储,而 Redis 存储缺乏很好的物理隔离,常因稳定性问题导致大量任务失败,造成 SLA 破线。

02. Paimon湖仓实践

第二部分是 Paimon 湖仓实践,我们希望 Paimon 湖仓能够解决以往实时数仓的痛点问题。

2.1 治理体验 Paimon 维表实践

(1)当前流式点查拓维场景

当前我们的维度拓宽查询场景主要有两类,一类是时效 T+1 维度场景,另一类是实时维度场景。对于 T+1 维度场景,我们一般使用 Spark 天级将 Hive 维度数据导入到 Redis 里面,以此来提供探查。而对于实时维度探查,我们通常是新建一个 Flink 任务,消费对应的实时 Kafka 数据源,最终将数据导入到 Redis 里面来提供相应探查。
目前主要存在以下痛点:一是开发效率低,需要新增很多单独的 Dump2Redis 任务;二是资源浪费,Dump2Redis 任务带来了诸多额外的计算、内存、存储资源开销;三是探查强依赖于 Redis,Redis 的稳定性经常会给我们带来很多问题。

(2)违规分域 Paimon 维表实践

下面主要以违规分域拓维场景为例,介绍一下我们 Paimon 维表的应用实践。

治理体验违规分主要是平台用来衡量商家及门店是否经营合规的一种综合量化手段,主要用于牵引商家及门店合规经营,维护平台秩序,最终增强用户的使用体验。我们的治理运营人员需要基于违规分数据进行精细化的运营和治理,最终提高平台的合规及健康度,这需要数仓人员进行相应的数据建设。违规分在我们的场景下主要有两类数据,一类是违规分域内的数据,比如扣分动作、违规分信息、违规标签以及违规原因等;另一类是公共维度信息,像商家、门店、行业、销售等。在进行违规分域内宽表建设时,需要做相应的拓宽操作。我们原有的实现方式如左下图所示,对于违规标签,T+1 时效通常就能满足需求,直接使用 Spark 将离线已加工好的维度数据写到 Redis,而违规分实时维度数据,做法如下:首先把违规分的业务库表数据接入到实时的 ODS 层,一般由 Kafka 来承载。为了提供点查,还需要新建一个 Flink Dump 任务,将 Kafka 承载的违规分 ODS 数据写入 Redis 来提供相应点查拓维。而且可以看到在原有的开发过程中,出于开发和运维的需要新增了很多 Dump2Hive 任务,整个场景显得颇为冗余。在使用 Flink 和 Paimon 完成相关实现时,整个流程变得非常简洁。首先,T+1 时效的违规标签 Hive 维度数据可以直接写入 Paimon 表,来提供维度拓宽点查。对于实时违规分维度数据,可以直接将业务库表的违规分数据接入到 Paimon ODS 表。该表既可以用于流读,也可以用于批查,还可以用于点查拓维,满足该场景的所有需求。

(3)业务收益

与原有方案相比,使用 Flink 和 Paimon 的方案可以带来诸多业务效益。

在开发与运维方面,Flink + Paimon 的几乎可以满足所有数据开发的需求,同时解决了之前的痛点,如探查和拓维问题。平均每个需求可以至少节省 0.5 个天/人的工作量。在资源节省方面,整体方案对比之下,CPU 使用量下降了 25%,内存使用量下降了 50%。在稳定性方面,通过收敛Redis存储表的数量,避免了不必要的数据写入,减少了对集群的负担,最终提高了任务的稳定性。

2.2 商品标签Paimon宽表的实践

在我们的场景中,宽表打宽一般有两种方式。第一种是很常见的多流 Join;第二种是多流有相同主键时,我们通常使用 Union All 加 Group By 这种方式来完成打宽操作。

这两种方式在数据量特别大的时候,都会带来一个问题,就是 Flink 任务的大状态。而 Flink 任务大状态给我们带来的痛点主要如下:一是资源消耗多,需要大量的 CPU、Memory;二是任务稳定性差,故障恢复非常慢;三是运维成本高,在新增或修改状态不兼容的时候,需要很多额外的处理动作。

下面主要从商品标签宽表建设场景来介绍 Paimon 宽表的应用实践,以此解决我们以往在宽表建设过程中的一些问题。商品标签宽表主要服务于下游标签数据服务。

宽表的建设旨在提高下游探查及分析的效率,降低使用门槛,这就需要数据人员提前在数仓内部完成宽表建设。我们这个场景下的商品标签宽表主要有两类数据源,一类是商品标签源,比如商品类目信息、可售时间、使用时间、核销时间等信息;另一类是 SKU 标签源,像库存信息、价格信息以及优惠信息。在原有的宽表实现方案中,是通过一个 Flink 任务去读取对应的商品状态标签源以及 SKU 状态标签源,然后在 Flink 任务里做 Union All + Group By 等操作来完成打宽。由于 Flink 任务一般计算状态保留时间不长,可能一天,甚至只有几小时(如 6 小时),所以在 Flink 内存里完成宽表打宽后,还需要相应地去查询对应的商品状态以及 SKU 状态标签的维表,以此完成长周期历史状态的兜底补全操作,最终完成该场景下的打宽,再将宽表数据进行下发。

而当使用 Flink+Paimon 方案时,只需使用 Flink 任务去读取消费对应的商品状态标签源以及 SKU 状态的标签源,然后在 Flink 任务里几乎不用做过多操作,只需做 Union All,将 Union All 之后的数据写入到 Paimon 结果宽表里面去,在写入时利用 Paimon LSM Merge 能力就可以完成宽表的制作。并且这时不需要去点查对应的维表来做长周期历史数据兜底补全了,因为 Paimon 宽表本身就能存放长周期历史数据,无需点查维表。

可以看到,使用 Paimon 宽表方案时,获得的业务收益主要有如下:

首先是开发运维提效,Paimon 方案可大幅减少开发运维的任务数量,任务简洁,显著提高交付效率,相比原方案能节省不少人力投入。其次是节省资源,相比原方案节省了 2/3 的 CPU 资源,使用 Paimon 打宽很节约 CPU,实践中很显著。同时还节省了 Redis 存储资源,Paimon 结果宽表可长周期存储,无需点查历史维度进行补全兜底。最后是稳定性提升,Flink 任务状态转移到 Paimon 后变为无状态任务,Flink 任务更易维护,提高了稳定性,也大大提高了故障恢复速度。

2.3 Paimon changelog 在圈选标签变更检测的实践

首先简单介绍一下生服圈选应用。生服圈选主要是依靠标签平台以及画像平台来完成标签的生产以及标签的圈选,进而将圈选的标签提供给下游业务系统,满足它们基于标签开展的线上业务活动。

考虑这样一个场景,业务人员在画像平台基于标签进行一次圈选活动,圈选任务会生成当前时刻的圈选结果,然后数据管理平台会保存这个圈选结果,并封装成对应的标签数据服务,提供给下游业务系统使用。假设圈选结果生成之后,相应的标签状态发生了变更,数据发生变化,那么之前生产的圈选结果就可能会给下游业务系统造成一些不可预期的业务影响,由于标签场景很容易出现一些资损事故,所以在这个场景下,需要实时增量地去更新之前的圈选结果。

(1)营销圈选商户标签变更检测实践

下面主要以我们营销商户标签变更为例,介绍一下检测实践。商户实体标签一般有两类,第一类是基础标签,像商户状态是否生效、商户是否有效、商户类别、商户类型等,这类标签一般来源于业务库表;第二类标签是复合标签,比如是否是高价值客户、认领企业的数量、销售人员列表等,这类标签一般是业务人员基于一定的业务逻辑进行二次加工而形成的。假设我们的基础标签状态发生了变化,在以前的实践方案中是这样做的:根据这个标签对应的顶层业务库表的 Binlog 消息变更事件来感知它的变更,通过监听变更事件获取相关信息,然后进行层层处理,最终在检测层新建一个对应标签检测任务,把该标签和现有的商户标签宽表里面的标签进行对比,如果检测到变更,就会进行变更事件的有效下发。可以看到,原有的实践方案存在以下问题,一是强依赖于业务库表的变更消息事件;二是整个链路流程处理起来很长,操作较繁琐;三是每做一个标签检测,都要新建一个检测任务,如果有二三十个标签,就得新建二三十个任务,十分麻烦。 而当使用 Paimon 检测方案时,就非常简洁了。我们只需读取现有的商品标签宽表,不需要顶层业务库表,然后新建一张 Paimon 标签检测表,将对应需要检测的标签字段写入到 Paimon 检测表里面去,接着下一个任务去读取该 Paimon 检测表的 Audit Log,直接就能获取到对应的标签变更事件,从而进行有效下发。逻辑很简单,可以理解为就是在读 Binlog,只不过 Paimon 里叫做 Audit Log。

(2)业务收益 

使用 Paimon Audit Log 检测方案与原有方案对比,获得的业务收益主要有如下:

一是开发运维提效,不需要从顶层业务库表去监听变更,链路更短,检测逻辑简单直白,就是读 Audit Log。二是适用性广,不依赖业务库表的变更消息事件,复合标签甚至任意字段都能做检测。三是节省资源,一个实体的所有标签可维护在一个检测任务里,无需新建很多标签检测任务,只需一张 Paimon 标签检测表,比如商户的所有字段都能放在其对应的标签检测表中。

2.4 总结与规划

(1)总结

Paimon 目前在生活服务业务大概落地了 100+ 任务,主要集中在治理体验、营销圈选以及服务商等方向。

我们获得了显著的业务收益,像开发运维提效、节省资源以及提高稳定性等等。同时也遇到了一些问题,例如,Paimon 维表目前支持的量级不够大,在实践中可能四五十 GB 就是上限了,这使得 Paimon 维表目前应用范围有限,有待后续进一步提升(目前已经支持 bulket shuffle join,支撑量级得到很大的提升,几百 G 甚至 TB 级别维表也可支持);其次是 Paimon 和字节内部的 Olap 引擎(比如 Doris 等)的结合还比较初级,很多任务的加工计算逻辑是在 Flink 任务里完成的,并没有充分发挥 Olap 引擎优势;最后是 Paimon 产品化以及生态建设还有待提升,比如 Paimon 维表监控以及 Paimon ODS 层数据集成,内部目前还缺少能很好满足诉求的相关产品。

(2)展望

Paimon维表的更大范围应用:在流量、直播、短视频等领域应用Paimon维表,减轻Redis集群的压力,这些数据领域的流量非常高,可能达到上百万的QPS,可减轻Redis集群压力,提高稳定性。推广Paimon维表在治理体验、服务商等领域的覆盖率达到50%。

Paimon Olap引擎的应用:在治理体验等方向使用Paimon结合Doris或者Presto,实现更高效、更灵活快速的开发交付。

Paimon离线以及流批一体的应用:在流量调控平台落地Paimon存储计算一体的能力,并利用Paimon时光旅行的特性来给离线ODS层数据降存储,目前在国际化电商方向已做了一些POC尝试,测试结果显示能给离线的ODS层节省大约90%的存储,收益显著,很值得期待。

03. Paimon 技术优化

下面下面说说我们在内部对 Paimon 做的技术优化方向。

一个是在 Paimon 的核心能力方面,做了例如如 Patial Update、Changelog 使用以及维表相关的优化;二是在生态集成方面,做了跟 Presto 相关的查询优化以及 Spark 相关动态参数使用的优化。

3.1 Patial Update原理

先介 Partial Update 是 Paimon 主键表的一种合并策略,跟其他的合并策略 Deduplicate 或者 Aggregation 等机制相比,它在数据写入、排序、合并时的流程基本类似。最开始有一行数据,比如 PK3 写入到 Paimon 之后,会先放到 Paimon 主键表的一个核心数据结构 SortBuffer 中,在刚开始写入时,Buffer 中的数据并不是完全有序的状态,相同主键的数据其实没有完全聚合在一起。而当需要对 Buffer 当中的数据进行遍历的时候,会利用快速排序算法,将相同主键的数据进行排序放在一起。完成排序过程后,再对 Buffer 里的数据进行遍历,就能够很方便地把相同主键的不同数据进行合并。在合并过程中,可以使用不同的合并方式,比如 Duplicate 可能就是取最后一条,Aggregation 就是做一些聚合计算,而对于 Partial Update 来说,它会把多条数据的不同列整合到一起,产生一条合并好的数据,写入到 Parquet 或者 Orc 文件中,最终上传到远端 bucket 对应的 LSM Tree 的目录下。在下游读取的时候,LSM Tree 当中主键相同的多条数据记录会进行合并,最终生成准确的数据下发给下游。
对于 Partial-Update 表的下游流式消费者,最大变化在于其 Changelog 日志文件的生成方式。因为在进行多流列拼接时,任意一个流都没办法生成完全准确的 Changelog 变更日志。所以对于 Partial Update 表,在产生对应的 Changelog 变更日志时,必须要把 L0 层的数据往高层去做 Lookup 点查,让新数据和老数据进行对比,才能生成完整的 Changelog 变更日志。

3.2 Patial Update优化 

对于 Partial Update 我们内部在使用时也碰到了一些问题。第一个场景是当业务有多个流进行拼接时,会给每个流定义一个单独的 Sequence Group。而社区原生能力中,对每个 Sequence Group 只支持单个排序字段。但在业务的很多实际使用场景里,是存在多排序字段情况的。最开始也有一些变通方法,比如自定义一个 UDF 把多个排序字段合并成一个,再去使用 Partial Update 里 Sequence Group 的能力。不过这样总归是增加了业务负担,而且迁移成本比较高。所以我们也跟社区提了一个优化建议,通过 Paimon 内置的 User Defined Comparator,让 Sequence Group 支持多排序键的使用。在具备这个能力后,右边就是我们的一个使用示例,可以对 c、d、g_2、g_3 这四个字段定义一个 Sequence Group。在更新时,会整体比较这几个排序字段,只有整体比较满足条件了,才会完成数据的更新,这样就实现了开发提效。

然后我们在 Partial Update 这块做的第二个优化,其实是 Sink 节点的复用。左边是优化前 Partial Update 的一个典型 SQL 语句情况,会对多个流进行 Select 操作,Select 之后把这些数据汇总到一起,再写到下游的一张宽表当中。由于下游的宽表可能是有上百列、上千列的宽表,而上游的每张源表并没有下游的全部字段,所以在写的过程中需要做大量的字段补齐操作。当源表很多时,SQL 语句就会变得特别长,难以维护。并且当下游宽表有任何字段新增等变更时,源表的 SQL 语句中也都需要去新增相应的字段,这样迭代效率也会非常低。 而理想中优化后的 SQL 语句应该像右边这样写,就是可以对每一张源表直接进行 Select 操作,Select 之后直接写入到下游的宽表当中,不需要去做前置的 Union all 操作。不过这个 Union all 操作我们是在 SQL 的 Plan 层去做了检测。当发现多个 SQL 语句写入的目标表是一致的时候,会在 Plan 层去做合并,也就是将 Sink 节点合并到一起。 这个优化可能有不同的实现方法,我们内部是对 Paimon 的 Dynamic Sink 新增了一些 equals&hashcode 方法,这样就能比较方便地去做比较。 后来跟社区的老师沟通,了解到阿里内部还有一个更好的优化,就是不需要 Connector 去做改造,通过一些其他的属性,也可以判断它们的 Sink 节点是相等的。 这种优化的好处就是多个 Connector 不需要去做适配,就可以直接使用 Sink 节点复用的能力。 当实现这种 Sink 节点复用之后,即便 SQL 语句像右边这样多个流之间维护各自的写入字段,最终的 Plan 也能像中间那样,合并成一个流后写入。 这种开发提效是很明显的,每个流可以维护自己的写入字段,不管是 SQL 语句的维护,还是后续迭代中遇到下游宽表新增字段的情况,可能只需要对上游的某一张源表的 SQL 语句进行修改就行。

3.3 changelog 优化

下面是我们在 Changelog 变更日志生成这块所做的一些优化。 Paimon 的 Changelog 变更日志生成主要分成两种情况,第一种是如果上游的数据源是一个 CDC 的数据源,那本身写入的数据当中就已经有变更日志了,这时只需要在写入的时候做一个双写就可以了。当上游是普通流量数据,不带完整变更日志的时候,就需要通过 Lookup ChangeLog Producer 或者其它 Compaction 的形式去产生对应的变更日志。 这种变更日志的生产过程中,通常会涉及到 Full Raw Compare,要将 LSM Tree 中低层的数据跟高层的数据进行对比,然后在对比过程中生成新的变更日志。 不过对于一些流量场景,并非每条数据的变更都需要产生增量日志,我们贡献了一个优化,就是当全列数据没有发生变更时,可以避免产生变更日志的功能,这样能避免产生无效的增量日志,进而优化下游的计算效率。 在后续使用中我们又发现,对于一些流量场景,可能真实有效的字段变化不大,但会有一些附加字段,比如时间戳(TS)字段、序列字段等,真实数据本身没太大变化,但这些时间属性或递增的字段一直在变,每次变化都会产生新的增量日志,而下游其实不一定需要感知到这些变化。

所以我们又向社区推了一个新的优化,就是在做对比的时候,可以忽略掉指定字段的变更。 比如下面这个使用例子,F1-4 字段是我们真实需要检测变更的字段,它变更并不频繁,真正变更频繁的可能是 TS 字段或者 Seq 字段,只要对这些字段配置忽略检测,在真正生成变更日志时,就不会因这些字段的变化而生成新的变更日志了,这对下游无效计算的优化效果是非常明显的。

我们在变更日志这块我们做的另一个优化是支持了 Binlog Table。我的同事在前面分享中提到过,在标签变更检测的场景下,像左边这条链路,会读取 MQ 里的数据,然后写入到 Paimon 的一张主键表,在写入主键表的过程中,通过 Lookup ChangeLog Producer 产生对应的 +U 和 -U 事件。 产生这些变更日志后,会有下游 Flink 作业去流读取这张表的 Changelog,拿到对应的变更数据日志及变更标签。 拿到之后,由于下游需要在一条数据中包含完整的变更,包括更新前(Update Before)和更新后(Update After)的数据。 所以之前会增加一张 Partial Update 的主键拼接表,把同行数据的更新前和更新后分别写入到不同字段中,再最终提供给下游使用。

为优化这种使用体验,我们贡献了一种新的系统表,也就是 Binlog Table。它新增了一种读取器叫 PackChangelogReader,在读取任意一个主键表的变更时,能把读取到的 +U 和 -U 事件装配到一起。 这里会涉及到系统表 Schema 的设计,比如对于 Binlog Table,它的 Schema 可能会由原来的 DataTable Schema 进行转换,之前 Column0 在 Data Table 里是字符串类型,在 Binlog Table 里就会变成一个字符串数组,数组第一位就是更新前的值,第二位就是更新后的值。 通过这种方式,能把左边 Lookup ChangeLog Producer 加 Partial Update 的链路优化成右边基于 Binlog 系统表的链路。 只需一次写入让其产生变更事件,下游去读取它的 Binlog 系统表,就能在一条数据里同时读到这条数据的更新前和更新后的值,再通过 Flink SQL 处理,就能把最终使用的数据交付给下游,这样就在原有的基础上进一步实现了开发提效。

 3.4 lookup join优化

下面说说我们在 Paimon 维表使用上的一些优化。传统维表可能只支持用独立的存储服务(比如 Redis)去做点查,而且不支持流读和批读,所以需要额外分别往流处理链路和批处理链路里补两份数据。而 Paimon 维表能做到一表三用,可极大提升开发效率。不过我们也发现,Paimon 维表在实际使用中确实还有很多可完善的地方。第一个就是在 Paimon 维表的刷新方面,因为 Paimon 维表很依赖把远端的维表缓存到本地,不管是 FullCache 模式还是 PatialCache 模式。对于缓存的数据,当进行刷新时,如果是同步刷新,首先会带来 CPU 方面的消耗,另外在这个过程中,可能会导致数据流出现反压甚至断流的情况,用户体验不太友好。而有了异步刷新的能力后,我们就可以将维表在远端产生的新的快照变更,异步的同步到进行 Lookup Join 任务的 Flink TaskManager 本地。这里对于 FullCache 模式的维表和 PatialCache 模式的维表又有所不同。 对于 FullCache 模式的维表,我们希望可以异步读取它在远端产生的新的快照变更数据,在完成行过滤、列裁剪等一系列操作后,把相关数据写入到本地的 RocksDB 中。该操作异步化以后就可以减少对 Lookup 点查操作的影响了。而对于 PatialCache 模式的维表,我们正在提的一个优化是异步地将新的快照对应的元数据文件同步到本地来,不需要在刷新的时候进行同步的元数据更新操作。不过,对于 PatialCache 模式的 Lookup File 构建流程,目前还没有特别好的异步刷新策略,我们会持续进行探索。

再看维表的第二个问题,也就是数据分布的问题。现阶段 Paimon 维表其实不支持特别大的数据量。这主要是因为当进行维表查询时,任意一个执行维表查询操作的 Flink 算子,其输入流可能会包含所有 Bucket 的数据。这时候本地的 Lookup File 缓存命中率以及内存当中配置缓存的命中率就会非常低。因为一个算子需要查询远端所有 bucket 的数据,命中率就很难做到很高,而且在数据量非常大的时候,会给 I/O 以及本地磁盘带来很大的压力。 在这方面,社区有一个新的优化,就是让 Flink 的维表算子去感知到远端维表的数据分布。在具体实现上,Flink 作业会在维表算子前面加一个 bucket shuffle 策略,这样能让对应 buckte 的输入流只流入到对应的 Lookup 算子当中。比如,假设 Flink 和 Lookup 算子是 10 个,远端的维表是 20 个 bucket 分区,那就可以保证在每一个 Flink 的 Lookup 算子只会去点查对应的两个 Bucket 分区的数据。这个时候,本地的 Lookup File 以及内存中配置的缓存命中率都能得到极大提高。在这个功能完成之后,我们认为可以让 Paimon 维表的使用产生质变,支持的维表数据量能提升非常多的量级。

最后一个优化是我们内部同事正在向社区贡献的内容。在维表使用过程中,发现维表从远端的存储文件拉到本地,然后重写为 Lookup File 时,会产生比较大的 CPU 开销。所以我们提出了一个方案,让 Paimon 在远端直接支持这种 Sorted Format 的行存。对于这种格式的文件,从远端拿到本地后,进行点查时,就无需再重写一遍了,这样能很好地优化维表数据刷新时 CPU 消耗的问题。

3.5 生态集成优化 

(1)Presto 查询优化

Paimon Presto 社区已经提供了一个基础版本,在这个版本中,支持一些分区条件的过滤下推。但在实际使用时我们发现一个问题,当对下推的分区字段包了一层计算函数时,就没办法下推到 Paimon 的 Reader 里面去做有效的分区过滤了。这时通常需要读取全部的分区数据,然后在内存中一条一条地过滤,计算效率非常低。后来我们内部做了一个优化,就是当发现对分区条件外面包了一层函数时,在 Plan 阶段就可以做函数展开,同时在 Presto 的解析阶段获取全部分区数量,获取之后在解析阶段完成分区裁剪,然后将裁剪后的分区直接推到 Paimon 的 Reader 当中,这样即便分区字段外面包了一层函数计算,也能达到很好的过滤下推效果。这是过滤前后的对比,之前可能需要查询 10 秒的耗时,支持分区裁剪后能做到毫秒级。

(3)Spark 使用优化

我们在 Spark 使用上也做了一些优化。社区的原生版本在读取 Paimon 时,对 Spark SQL 语句支持一些全局的参数,也就是可以通过 Set 方法指定一些运行时的参数,比如缓存大小或者 Time Travel 的参数等。但在 Spark SQL 中需要读取多张表时,参数的使用不是很灵活,因为这个参数会对 SQL 语句中读写的所有表生效。后来我们也向社区提了一个优化建议,支持表级别的参数。也就是当读取不同表时,可以通过 Set 的方式,针对每个表的读写只提供不同的参数,这样就能优化 Spark 读写 Paimon 的用户体验,同时我们也和 Flink 的动态参数进行了对齐。

3.6 未来规划

以上就是我们已经在做的相关技术优化情况。未来规划方面,我们会持续优化大流量场景的稳定性问题。因为内部有很多流量超千万级别 QPS 的场景,且底层依赖的是规模很大的 HDFS 公共大集群。超大的混部 HDFS 集群虽然成本低,但运行环境恶劣,存在不少慢节点问题。在上层流量高、底层存储不稳定的状况下,优化中间层的性能与稳定性对我们而言是个很大挑战,另外还包括对 Unaligned Checkpoint 的支持,要确保对于 bucket 表的 Hash 连接,单个节点故障不会影响全局数据推进,这也是我们需要优化的地方。其次在生态集成上,我们会持续优化与 Spark、Doris 以及内部的 ClickHouse 的集成,支持更多的外表查询。在秒级延时方面,很多内部业务方对此也有需求,社区虽支持 Log System 的使用,也就是在数据写入时对外挂的 MQ 和 HDFS 进行双写。但很多秒级延时场景对稳定性和实效性要求很高,双写的时效性与稳定性不一定能满足我们需求,所以未来可能会推出一种 MQ on Paimon 的架构,将 MQ 的一些 Topic 自动映射为 Paimon 表的元数据,通过内置的 Dump 任务,直接把 MQ 主题的数据导入到 Paimon 表内。这样用户既能享受到 Flink 加 MQ 的秒级延时,又能享受到 Paimon 流批一体的优化特性。 最后,我们会持续推进 Paimon 在离线场景的使用,包括利用 Paimon 主键表的 Time Travel 特性来替换 ODS 层的分区全量表,达到降本提效的目标;还会利用 Branch 特性去优化离线的数据订正开发流程。

以上就是这次分享的全部内容。


更多内容


活动推荐

阿里云基于 Apache Flink 构建的企业级产品-实时计算 Flink 版现开启活动:
新用户复制点击下方链接或者扫描二维码即可0元免费试用 Flink + Paimon
实时计算 Flink 版(3000CU*小时,3 个月内)
了解活动详情:https://free.aliyun.com/?utm_content=g_1000395379&productCode=sc


ApacheFlink
949 声望1.1k 粉丝