分享

Flink Table Store:流批一体存储




问题导读:

1、Flink Table Store 是 Flink 社区中 Table 的发展方向吗?
2、Flink Table Store 支持 Join 操作吗?
3、LSM 会有写放大和读放大的问题,Flink Table Store 会有这样的问题吗?如何避免?





01 业务需求
Flink 在 Table API  和 SQL 中提出了动态表的概念,跟 Hive 表在离线数仓中间层的作用类似,动态表是实时数据仓库的中间层。动态表中数据是随时间变化的,而静态表只能包含离线处理的数据。我们可以将实时计算看成对动态表上变更数据的处理,将离线计算看成对动态表某个时间快照的处理。如下图所示,我们可以像查询批处理表一样查询动态表,对动态表的查询将生成一个不会停止的连续查询,这个查询不断更新下游的结果表,以反映输入动态表上的更改,这构成了实时数仓的基本链路。本质上,动态表上的连续查询非常类似于定义物化视图的查询。

640.png

从架构上看,实时数仓架构常以 Kafka 作为中间层。然而 Kafka 作为中间层,其数据是不可查询的,只有最后落地的 ODS 层数据可查,这会严重限制数据分析师的能力,数据分析是面向所有表的,数据分析师对所有表数据的查询都有诉求;并且因为 Kafka 数据只能保存一段时间,在中间数据出现问题的场景下,对数据进行排查检验的成本是很大的。在某些场景下,以 Kafka 作为实时计算链路的中间层甚至需要付出比较高的成本。

为了解决 Kafka 不可查询的问题,业界的一种解决方案是构建实时离线一体化数据仓库:对于实时计算的一条流水线,再去创建离线链路的一条流水线,实时数据多往一条离线支路进行写入,离线的数据以天周期更新到下游,用以更新实时链路中的错误数据和分析查询。由于在技术上 Kafka 中需要重视时间字段和水位线等属性,而 Hive 则需要考虑分区问题,这种方案存在的问题就是两套系统的维护成本比较高;并且实时数仓和离线数仓的口径也难以保持天然的一致性,这就导致了两条链路不可避免的走向割裂。

640 (1).png

因此,我们对离线和实时计算在存储上的核心需求有以下几点,这也是对 Flink Table Store 设计的基本要求:

    ① 是一个流批统一的存储,提供一定的 OLAP 查询能力(基于列式存储),做到毫秒级别的实时流式读取,能够支持 Insert Overwrite。

    ② 是最为完善的 Flink Connector,支持 Flink SQL 的全部概念,支持任意 Flink Job 的输出,支持所有数据类型。

    ③ 是最好用的 Flink Connector,能够结合 Flink SQL 提供 DB 级别的体验,并且支持大规模更新。

640 (2).png

02 使用 Flink Table Store

Flink Table Store 结合 Flink SQL,致力于打造类似 Database 的使用体验。

1. 建表

在创建表时会创建实质的物理存储,而不只是逻辑映射,删除数据时也会删除物理存储。对于用户而言,在建表时不需要填写跟业务无关的连接信息,这些信息通过 session 级别的配置就可以设置完成。在实际存储上也有类似数据库主键的概念,在主键上可以预先构建好索引,因此基于主键的查询是非常快的。Flink Table Store 的建表语句如下图所示:

640 (3).png

2. 统一读

Flink Table Store 中的表有流读和批读两种模式,但是在使用上的体验是一致的,只需要通过修改“execution.runtime-mode”参数值来设置运行模式:

    ① batch,能够查询当前快照的数据信息。

    ② streaming,能够持续性地读取变更信息。

Flink Table Store 原生地支持 Kafka,在用户使用体验上,不需要了解 Kafka表是如何建立和写入的。

Flink Table Store 设置不同读取方式的方法如下图所示:

640 (4).png

在流式读取的场景下,能够达到 CDC 的读取效果。默认采取的是增量快照方案:不仅会将历史的数据全部读取,然后继续读取变更的数据,保证读取到的是全量数据。当然,也支持只读最新的变更数据,由用户通过参数决定读取方式。

640 (6).png

3. 统一写

考虑如下图所示的场景:

假设在 DWD 层存在一张实时写入的表,数据分析人员发现近期写入的数据出现了一些错误,通过把历史数据放入到实时数据去更新这些数据是不正确的,这不仅会导致整个消息队列的数据乱序,也不利于Flink去计算状态,因为状态有可能会过期。

640 (7).png
一个有效的解决手段是允许原来的实时写任务继续运行的,并新增一个离线的任务去重写这部分数据。对应到实际操作中,即是下图所示的 SQL 语句,通过修改“execution.runtime-mode”快速切换运行模式,手动修改过滤条件对出错的数据分区进行重写,通过 SQL 就能很方便的就能拉起一个离线任务。

640 (8).png

03 理解Flink Table Store

1. 分层设计

不同的数据对于实时性的紧迫程度是不同的:一部分数据对实时性要求非常高,不过这部分数据是很少的,我们称之为黄金数据;另一部分数据则对实时性的要求更低一些,我们称之为白银数据;剩下的大量数据虽然有一定的分析性价值,但是对实时性的要求并不高,我们称之为青铜数据。

我们在存储上的分层设计需要能够覆盖这三类需求,在使用时提供统一的调用接口,让用户能够根据自己的需求选择适合的方案。如下图所示,展示了一种存储分层方案:

640 (9).png

在最底层,需要存储所有的数据,业界通常的方案是采用 DFS 进行存储。而在次一层,则是 File Store,这是一个准实时的数据湖。在数据湖上,则是 Table Store,提供实时的流水线,主要存储白银数据,而在最上层就是 Table Store Service,通过 Cache 保存数据,存储的是黄金数据。我们预期的流程是通过 Flink 写入数据,在查询端构建良好的生态,可以通过 Flink、Spark 查询。

2. 业界已有实现

业界已经有了两种比较成熟的实现方案,分别是 Copy on Write 和 Merge on Read。第一种实现方案是 Copy on Write,在已经有一堆历史数据文件的基础上,将更新数据与历史数据进行 Join,合并出新的数据文件。

这种方式存在的问题在于历史文件比较多,Join 代价比较大,常用的优化手段是加上文件定位操作,快速找到需要被更新的数据文件。

640 (10).png

文件定位的方法有以下几种:

    ① Key Min Max。在数据文件有序的情况下,通过 Min/Max 能快速过滤文件。

    ② BloomFilter。存在假阳性的问题,在大数据量场景下性能仍然会比较差。

    ③ Key-Value Index。依赖 KV 存储服务,但是 KV 存储的点查询性能不太好。

在合并文件操作中常用的方法有:

    ① Sort Merge Join。对 base 文件和 delta 文件进行排序,然后进行合并。这种方式的优点是比较稳定,缺点是排序的代价比较大。

    ② Hash Join。将 delta 文件的数据加载到内存,将 base 文件与内存中的数据进行对比。这种方式的优点是性能更好,缺点是在 delta 文件比较大的极端情况性能反而低于 Sort Merge Join。



第二种实现方案是 Delta Merge on Read,这种方案在定位到需要更新的文件时不需要立即进行合并,而是新建一份数据文件,在读取时再进行合并,然而这本质上只是延迟了复制操作,对于数据定位合并上的效率是没有太多改变的。

3. Flink Table Store方案

Apache Hudi 满足了列存储和分布式存储的要求,但之所以不选择 Apache Hudi,是因为基于底层数据结构的考虑,Hudi 在设计上采用了 Index 的方式定位文件。对于 Bloom Filter Index,大数据场景下存在的假阳性问题会导致查询效率低下;而使用 Flink State Index 又会出现状态过期、点查性能不足以及不能对外暴露信息等问题。

640 (13).png

Flink Table Store 选择使用 LSM Tree(Log Structured Merge Tree)作为底层存储,LSM 是大数据规模场景下的核心数据结构,通过 Sort-Merge 的方式达到了很好的更新性能,并且基于有序的数据,LSM 的数据查询(点查、范围查询)的性能也是比较良好的。考虑到 LSM 是基于单机存储的设计,我们可以加上分桶的策略实现分布式设计。

640 (14).png

在 Flink Table Store 中,会在 Sink 端维护一个 Memory Table,用作数据合并,数据会写入到 File Store 和 Log Store 当中,File Store 中保存的就是经过桶分区的 LSM 树存储结构,Log Store 则是保存了 LSM 中的 Write Ahead Log 信息。对于批读,只需要去读取 File Store;而对于流读,则需要混合的读取,先读取 File Store 中的全量数据,再通过 Log Store 读取变更的数据。

640 (15).png

基于 LSM 结构,Flink Table Store 能够提供的业务价值包括:

    ① 提供了可以被查询的数据中间层,并且仍然具备流式处理的能力。

    ② 在保证数据更新效率的基础上,还能提供较好的数据过滤能力。

640 (16).png

04 项目和规划

在 Flink Forward Asia 2021 上,社区已经确定要给 Flink 增加一个重要的存储组件,这之后 Flink Table Store 的规划有以下几个重要节点:

    ① 在 V0.1 版本里实现基本的日志存储功能,这一阶段还不具备服务化能力。

    ② 在 V0.2 版本里希望能够做到聚合 Hive、支持表结构演变和更完整的 LSM 数据结构实现等,达到生产状态,但还不具备服务能力。

    ③ 在 V0.3 版本里预计能够具备服务化能力,能够达到真实时的流批一体存储,为流批一体数仓使用。

    ④ 在 V0.4 版本里期望推出比较完善的版本,为流计算里的维表关联所使用。

640 (17).png

Flink Table Store 的 Github、文档社区信息、邮件列表汇总如下:

项目开源地址:
https://github.com/apache/flink-table-store

项目文档:
https://nightlies.apache.org/flink/flink-table-store-docs-master/

讨论邮件列表:dev@flink.apache.org

用户列表:user@flink.apache.org

用户中文列表:user-zh@flink.apache.org

05 Q&A环节

Q1:Flink Table Store 是 Flink 社区中 Table 的发展方向吗?

A1:仅有计算而没有存储会存在许多问题,Flink 社区之后一定会发展自己的存储,之后也将会以 Flink Table Store 作为在存储上的着力点,为流批一体提供更好的保障。

Q2:Flink Table Store 支持 Join 操作吗?

A2:Flink Table Store 不支持维表 Join,这需要点查能力的支撑,而对于 OLAP查询中的多表 Join,Flink Table Store 支持基于 Snapshot 的查询,和传统的数据湖提供的能力是相同的。

Q3:LSM 会有写放大和读放大的问题,Flink Table Store 会有这样的问题吗?如何避免?

A3:LSM 一定会有写放大和读放大的问题,但是我们会提供配置参数进行调节,在默认情况下,我们更重视会对读取的优化,同时保障足够的吞吐量。
最新经典文章,欢迎关注公众号



---------------------

作者:大数据研习社
来源:weixin
原文:Flink Table Store:流批一体存储



没找到任何评论,期待你打破沉寂

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

关闭

推荐上一条 /2 下一条