Flink 2.0 状态存算分离改造实践

本文整理自阿里云智能 Flink 存储引擎团队兰兆千在 FFA 2023 核心技术(一)中 的分享,内容关于 Flink 2.0 状态存算分离改造实践的研究,主要分为以下四部分:

  1. Flink 大状态管理痛点

  2. 阿里云自研状态存储后端 Gemini 的存算分离实践

  3. 存算分离的进一步探索

  4. 批量化存算分离适用场景

一、Flink 大状态管理痛点

1.1 Flink 状态管理

状态管理是有状态流计算的核心。目前在 Flink 生产环境中使用的最多的状态后端是基于 RocksDB 的实现,它是一个以本地磁盘为主的状态管理,将状态文件存储于本地,同时在进行检查点的时候将文件周期性写入 DFS 。这是一种存算一体的架构,它足够简单,在小状态作业下能够保证稳定高效,可以满足绝大部分场景的需求。随着 Flink 的发展,业务场景日益复杂,大状态作业屡见不鲜,在存算一体的架构下涌现了很多与状态管理有关的现实问题。

1.2 大状态作业痛点

大状态作业下,基于 RocksDB 本地磁盘存算一体的状态管理主要会遇到以下四方面的问题:

  • 本地磁盘有可能会出现空间不足的情况,通常解决这类问题的方法就是扩容。在目前集群部署或是云原生部署的模式下,单独进行本地盘的扩容是不方便的,所以用户一般会增加并发度,也就是涉及到存储和计算绑在一起进行扩容,加剧了计算资源的浪费。
  • 作业正常状态访问时,本地磁盘 I/O 也会遇到一些瓶颈。这导致作业整体性能不足,同样需要进行扩并发操作。
  • 检查点的开销比较大。由于状态非常大,在检查点期间对于远端存储访问量呈现一个尖峰态势。
  • 在作业恢复的时候,需要从远端读取全量文件到本地,这个过程也十分缓慢。

上述前两点是影响用户成本的问题,而检查点的开销与恢复速度是 Flink 中影响易用性的关键问题。

1.3 存算分离的架构

对于以上问题,我们提出了存算分离的架构来解决。存算分离可以摆脱本地磁盘的限制,将远端存储(DFS)作为主存储,同时将空闲的本地磁盘作为一个 Cache 来进行使用。同时用户仍可以选择本地磁盘作为主存储,还用原来的模式来运行。这样做的显著的好处是,一方面由于磁盘空间和 I/O 性能不足的问题不再影响计算资源,另一方面是状态检查点与恢复在远端就可以直接完成,变得更加轻量级。从架构上完美解决了大状态作业面临的问题。

二.阿里云自研状态存储后端 Gemini 的存算分离实践

在进入存算分离架构探讨的最开始,我希望先从阿里云自研的企业级状态存储 Gemini 入手,探寻它在存算分离上的一些实践,主要分为以下三项:

2.1 多种文件系统分层管理

Gemini 能够把远端作为状态主存储的一部分。它首先将状态文件存储于本地磁盘,如果本地磁盘不足,则将文件移动到远端存储。本地磁盘中存留的是访问概率高的文件,远端存储的是不容易访问的文件。两部分共同构成了主存储,并在此基础上进行了冷热划分,保证了在给定资源条件下的高效服务。Gemini 的这种文件分层管理模式摆脱了本地磁盘空间的限制。理论上本地空间可以配置为零,以达到纯远端存储的效果。

2.2 状态懒加载

Gemini 能够支持远端文件存储,在作业恢复的场景之下,无需将数据从远端文件加载回本地就可以开启服务,使用户作业进入运行状态。这一功能称为状态懒加载。在实际恢复过程中,Gemini 仅需将元数据以及少量内存中的数据从远端加载回,就可以重建整个存储并启动。

虽然作业已经从远端文件启动了,但读取远端文件涉及到更长的 I/O 延迟,性能仍旧不理想,此时需要使用内存和本地磁盘进行加速。Gemini 会使用后台线程进行异步下载,将未下载的数据文件逐渐转移至本地磁盘。下载过程分为多种策略,比如按照 LSM-tree 层次的顺序,或者按照实际访问的顺序来下载。这些策略可以在不同场景进一步缩短从懒加载恢复到全速运行性能的时间。

2.3 Gemini 延迟剪裁

在改并发的场景中,比如将两个并发的状态数据合并成一个并发时,目前 RocksDB 是把这两份数据都下载完成之后再做一个合并,涉及到将多余的数据剪裁掉,重建数据文件,其速度是比较慢的。社区针对这个过程进行了很多的针对性优化,但仍然避免不了数据文件的下载。Gemini 只需要把这两部分数据的元数据进行加载,并且把它们合成一个特殊的 LSM-tree 结构,就可以启动服务,这一过程称为延迟剪裁。

重建后 LSM-tree 的层数相比正常情况下会比较多。比如针对图中的例子,有两个 L0 层,两个 L1 层和两个 L2 层。由于 Flink 有 KeyGroup 数据划分的机制存在,层数变多并不会对读链路长度造成影响。由于并未对数据进行实际的裁剪,会存在一些多余的数据,这些数据会在之后的整理 (Compaction) 过程逐步清理掉。延迟剪裁的过程无需对数据本身进行下载和实际合并操作,它可以极大地缩短状态恢复的时间。

2.4 Gemini 恢复效果

有了异步剪裁状态+状态懒加载,对于 Gemini 来说,恢复时间即作业从 INITIALIZING 到 RUNNING 的状态可以变得非常之短,相比于本地状态存储的方案是一个极大的提升。

我们针对 Gemini 与 RocksDB 的改并发时间进行了评测。评测的指标为从作业启动一直到恢复原有性能的时间,这包含了 Gemini 异步下载文件的时间。从上述实验结果中可以看到 Gemini 相比于RocksDB 在缩容、扩容的场景下都有明显的提升。

三.存算分离的进一步探索

Gemini 做存算分离相关的优化部分解决了前述大作业场景的问题。本地空间不足的问题可以通过远端空间来解决。针对检查点开销大的问题,因为已经有一部分文件远端存储上了,无需再次上传,这部分的开销也得以减少。针对作业恢复慢的问题,状态懒加载+延迟剪裁功能,使得作业能够快速的恢复运行状态。

这里还有一个功能是对 Memtable 的快照。Gemini 在做检查点的时候,是将 Memtable 的原样上传到远端存储上,不会影响 Memtable flush 的过程,也不会影响内部的 Compaction。它的效果和通用增量快照的 changelog 的效果是类似的,都会缓解检查点时的 CPU 开销和 DFS I/O 量的尖峰。

3.1 Gemini 存算分离的问题

Gemini 在存算分离方面做了不错的实践,在阿里内部与云上客户的大状态作业场景下均取得了不错的效果。但它仍存在着一些问题:

第一个问题,Gemini 还是把本地磁盘作为主存的一部分,状态文件是优先写到本地磁盘的,这并非最彻底的一个存算分离。这样会导致检查点时上传文件数量还是会比较多,持续时间较长,做不到非常轻量级的检查点。

第二个问题,是所有存算分离方案都会遇到的一个问题,就是与本地方案的性能差距。目前的方案中 Gemini 已经利用了本地磁盘,但本地磁盘的利用效率并不是最高的。如果更多的请求可以落到内存或者本地磁盘,对应的远端 I/O 的请求数降低,作业整体性能会有提升。另外,异步 I/O 是很多存储系统都会采用的优化。它使用提高 I/O 并行度的方式来解决提高作业的吞吐,是值得尝试的下一步优化方向。

针对这几个问题我们进行了简单的探索,首先是做了一个非常彻底的存算分离,直接写入远端存储并且把本地磁盘直接作为 Cache 来使用,在此基础上实践了不同形式的 Cache。第二方面,我们实现了一个简单的异步 I/O PoC,验证其在存算分离场景上的性能提升。

3.2 直接写入远端与本地磁盘 Cache 的探索

3.2.1 原始方案:基于文件的本地磁盘 Cache

直接使用远端存储作为主存的改动我们不作详述,在这里主要探讨 Cache 的形态与优化问题。最简单的架构是基于文件的 Cache 。如果远端的文件被访问到,它会被加载到本地磁盘 Cache。与此同时内存 Cache 仍然存在,并且仍旧采用 BlockCache 的形式。这个形式是非常简单高效的架构。但是内存 BlockCache 和本地磁盘的文件 Cache 有很大的一个数据重复,这相当于浪费了很多空间。另一方面,由于文件的粒度相对较粗,对于同一个文件的不同 block ,其访问的概率并不一样,所以会有一些冷的 block 维持在磁盘中,降低了本地磁盘的命中率。针对这两个问题,我们设计了全新的本地磁盘 Cache 的形态,对上述问题进行优化。

3.2.2 优化方案:基于 Block 的本地磁盘 Cache

我们提出将本地磁盘与内存结合起来,组成一个以 block 为粒度的混合式 Cache。它使用一个整体的 LRU 进行统一的管理,不同 block 只有介质上的不同。内存上相对冷的 block 会异步地刷到本地磁盘上,磁盘的 block 是按照顺序以追加写的形式来写在底层文件中。如果由于 LRU 策略淘汰了某些磁盘的 block,必然会映射到某个文件上形成空洞。为了维持 Cache 空间有效性,我们采取了空间回收来进行优化。空间回收的过程是一个空间和 CPU 开销的权衡。

不同层的文件如 L0 file 、L1 file 以及 L2 file,它们的生命周期是不一样的。对于 L0 file 来讲,它的生命周期比较短一些,但是热度相对高。对于 L2 file 来讲,文件本身更容易存活,但是热度是相对低的。根据这些不同的特点,我们可以采取不同的策略来进行空间回收。来自不同层文件 block 会被 Cache 在不同的底层文件中。针对不同的底层文件可以执行不同的空间回收阈值与频率,这样可以保证最大的空间回收效率。

另外我们针对 block 淘汰策略也提出了优化方案。最原始的 LRU 是根据命中频率来进行管理的,某个 block 一段时间内不命中则会被淘汰。这种策略并没有考虑到在缓存某一个block 的空间开销。也就是说可能为了缓存某个 block,却有更多的 block 没有办法进行缓存。在这里引入了一个新的评判体系叫做缓存效率,用一段时间内命中次数除以 block 大小,来更好的评判每一个缓存的 block 是否应该被缓存。这种评判方式的缺点是开销会比较大。最基本的 LRU 针对于查询都是 O(1) 的,但缓存效率的评分需要实现一个优先队列,其运行效率会有较大下降。所以在这里的思路还是在保持 LRU 主体管理的情况下,针对 block 的缓存效率异常的情况进行特殊化处理。

目前发现有两部分异常,第一部分是内存中的 data block 。它的命中率是内存中相对低的,但是它的占比能达到 50%。目前对于它的策略就是进行压缩,其代价是每次访问涉及到解压,但这个开销要比进行一个 I/O 的开销要小得多。第二部分是磁盘中的 filter block 。虽然它有命中,但它的大小是比较大的,缓存效率并不高。在这里实现了一个倾向于把磁盘中的 filter block 优先踢出的策略,使得相对上层的数据可以缓存进来。在测试作业场景中,这两条特殊规则与 LRU 相结合,相比于没有这两条规则的时候,整体 TPS 会上升 22%,效果比较显著。

但直接写入远端使系统出现了远端文件冷读问题,即文件第一次生成后的读取仍然需要涉及到远端 I/O。为了解决这个问题,我们在这里也做了一个小的优化,在本地磁盘上提供一个上传远端的队列,并且让其中的文件多缓存一段时间。这个时间并不会很长,大概是二三十秒的一个级别,在此期间队列文件的远端 I/O 会变为本地 I/O。这样的做法能够让远端冷读的问题大大的缓解。

到目前为止我们有两种存算分离的 Cache 方案,第一种是基于文件的本地磁盘 Cache 方案,它的优点是非常简单和有效,在磁盘充足的场景下有与本地方案类似的性能,因为本地磁盘可以缓存所有文件。第二种是混合式 block cache 的优化,在本地磁盘不足的情况下是一个非常好的方案,因为它提升了 Cache 的命中率。但是它也带来了比较大的管理开销。如果我们想要有一个通用的方案来适配所有场景,应该怎么做呢?

3.2.3 混合方案:自适应变化

将上述两种方案结合,我们设计了一个自适应变化的的混合方案。在磁盘充足的情况下使用的是基于文件的 Cache 方案,在磁盘不足的情况下,会把本地磁盘自动的和内存结合在一起组成混合式 block cache 方案。两种方案的结合会让它们两个的优点结合在一起,在所有的场景下都能够最大化的满足性能效率的需求。

3.2.4 混合方案:评测

我们针对上述提出的混合方案使用测试作业进行评测。可以看到在 TPS 上,新方案相比于文件为粒度的原始缓存方案有 80% 的提升。同时它也伴随着一些 CPU 的开销,用 CPU 效率(TPS/CPU)作为评判标准,新方案也有 40% 的提升。Cache 命中率的提升是 TPS 提升的一个主要来源。

3.3 异步 I/O 的探索

3.3.1 同步单条处理模式

第二项探索是对 Flink 进行的异步 I/O 改造与测试。如图展示了目前 Flink 的单线程处理模型,在 Task 线程上面,所有的数据是按顺序来进行处理的。对于每一条数据处理,会分为算子(operator)的 CPU 开销,状态(State)访问的 CPU 开销,以及状态访问所需的 I/O 时间,其中 I/O 是最大的一块开销。由于存算分离需要访问远端存储,其 I/O 延迟会比本地方案大,最终会导致整体 TPS 有明显下降。

3.3.2 批量处理+内部异步模式

我们对这一模式进行更改,使得 State 操作可以同时进行。在 Task 线程的角度来讲,State 被并行化之后整体的时间被缩小,所以 TPS 会有一个提升。同时,Task 线程需要预先攒批,这和 micro-batch 做的事情是非常类似的,同理也可以借用预聚合的功能,降低 state 访问的数目,TPS 得以进一步提升。

3.3.3 算子异步+批量处理模式

更进一步,在加上状态访问异步的基础之上,可以继续探索从算子的角度上进行异步化的过程。这意味着状态访问已经开始了异步执行后,让 Task 线程得以继续进行其他数据的 CPU 操作。但这样做有一个问题:状态访问 I/O 一般都是时间比较长的,虽然在 Task 线程闲的时候可以做一些其他的数据的处理工作,但是最终会一个速率不匹配的问题,瓶颈最终还会落到状态访问上,会退化到没有做此优化的情况。

经过权衡,我们认为仅采用攒批,再加上批内的状态访问使用异步 I/O 这种方式,是一个比较平衡的方案。

3.3.4 存算分离+批量化:评测

我们做了一个简单的支持批量异步的接口的状态后端,并在社区 Microbenchmark 上面做了一个简单的测试,目前仅涉及到 value get 的场景。从对比结果上可以看到,批量执行加上异步 I/O 是对存算分离场景有很大的提升。

四.批量化异步 I/O 存算分离适用场景

上述探索的批量化执行的存算分离状态访问有独特的应用场景。对于大状态作业来讲,存算分离在功能上解决了最开始所述的几个问题,在性能上,用批量接口的方式来弥补它的低的问题。

4.1 性能分析

此种方案的性能来源是 State 访问在批次内并行化,减少了状态访问的时间,提升了计算节点的 CPU 利用率。这种方案对于大状态作业性能提升是很有用的。

4.2 定性性能分析

在小状态作业的场景下,状态访问可以做到非常的快,将状态访问从 Task 线程抽离出来的提升量很小,且引入了线程之间交互的开销。所以在小状态的场景,这种批量异步状态访问的方案或许还不如原始本地状态管理方案。

随着状态大小逐渐增大,状态 I/O 开销逐渐增大并成为了瓶颈,异步 I/O 的执行当于摊薄了每个 I/O 所耗的时间。这导致了图中红色线的下降是较慢的,而本地状态管理(蓝色线)降低会比较快。在达到某个状态大小后,异步 I/O 的方案性能会显著的好。这种方案需要消耗 I/O 带宽,如果状态访问已经达到了 I/O 上限,异步 I/O 不能减少 I/O 的总时间,故此时它的斜率跟本地状态管理差不多。

如果状态很小的时候就达到 I/O 上限,并行化执行并不会产生效果,上图所示的便是这个场景。

总结一下,批量并异步执行状态访问在满足以下条件时会有优势:

  • 大状态作业场景且状态访问是作业的瓶颈
  • I/O 并没有达到瓶颈(未打满)
  • 业务对于攒批的延迟(亚秒到秒级别)可以接受

绝大部分存算分离场景下,由于 I/O 性能是存储集群提供,可以支撑比较大的 I/O 量且可以灵活伸缩,一般不会过早达到 I/O 瓶颈状态,异步 I/O 可以很好的优化存算分离场景。

五. 结语

以上介绍了我们在存算分离方面做的一些探索。这些工作我们希望借着 Flink 2.0 的机会贡献给社区,一方面是支持纯远端的存算分离方案+混合式缓存的存储后端,另一方面是希望能够引入异步化 I/O 保证存算分离模式下的高性能数据处理。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/675038.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

利用路由懒加载和CDN分发策略对极客园项目进行性能优化

文章目录 前言1.配置路由懒加载2.项目资源打包3.包体积可视化分析4.cdn配置 总结 前言 极客园项目的完成之后,我们需要对项目进行打包以及性能优化,优化用户体验以及加快响应时间,本文只列举了路由懒加载和cdn分发的策略 1.配置路由懒加载 …

政安晨:快速学会~机器学习的Pandas数据技能(二)(索引、选择与赋值)

小伙伴们,今天这篇文章里讲到的操作,专业的数据科学家每天都会执行这个动作数十次。你当然也可以做到! 概述 选择pandas DataFrame或Series中的特定值进行操作是几乎任何数据操作中的一个隐含步骤,因此在使用Python处理数据时&am…

怎么给《Cyberpunk 2077》制作功能性MOD

Cyberpunk的官方mod支持【REDmod】:https://www.cyberpunk.net/zh-cn/modding-support。官网有三个视频教程,其中第二集演示了脚本的替换,比较合程序员的胃口。 REDmod 是《赛博朋克 2077》的免费 DLC(须购买游戏之后才能下载&am…

【Java 数据结构】String进阶

字符串常量池 1. 创建对象的思考2. 字符串常量池(StringTable)3. 再谈String对象创建 1. 创建对象的思考 下面两种创建String对象的方式相同吗? public static void main(String[] args) {String s1 "hello";String s2 "hello";String s3 …

前端ajax技术

ajax可以实现局部刷新,也叫做无刷新,无刷新指的是整个页面不刷新,只是局部刷新,ajax可以自己发送http请求,不用通过浏览器的地址栏,所以页面整体不会刷新,ajax获取到后台数据,更新页…

计算机毕业设计 | SSM超市进销存管理系统(附源码)

1,绪论 1.1 开发背景 世界上第一个购物中心诞生于美国纽约,外国人迈克尔库伦开设了第一家合作商店,为了更好地吸引大量客流量,迈克尔库伦精心设计了低价策略,通过大量进货把商品价格压低,通过商店一次性集…

Nginx中logs的nginx.pid文件引发的问题

Nginx中logs的nginx.pid文件引发的问题 Q1:nginx: [error] CreateFile() "D:\software\nginx-1.22.1/logs/nginx.pid" failed (2: The system cannot find the file specified)Q2:nginx: [error] invalid PID number "" in "D:…

掌握虚拟化与网络配置之道:深入浅出VMware及远程管理技巧

目录 虚拟机介绍 虚拟机的关键字 服务器架构的发展 为什么用虚拟机VMware 虚拟机和阿里云的区别 功能角度 价格因素 应用场景 优势方面 找到windows的服务管理 配置VMware 关于VMware安装的几个服务 vmware如何修改各种网络配置 关于NAT的详细信息(了解) NAT(网…

Ribbon全方位解析:构建弹性的Java微服务

第1章 引言 大家好,我是小黑,咱们今天聊聊Ribbon,这货是个客户端负载均衡工具,用在Spring Cloud里面能让咱们的服务调用更加灵活和健壮。负载均衡,听起来挺高大上的,其实就是把外界的请求平摊到多个服务器上,避免某个服务器压力太大,其他的却在那儿闲着。 Ribbon的牛…

Netty连接通道中的Channel参数模型

ChannelOption(Channel中的连接参数) ChannelOption.SOBACKLOG ChannelOption.SO_BACKLOG对应的是tcp/ip协议listen函数中的backlog参数,服务端处理客户端连接请求是顺序处理的,所以同一时间只能处理一个客户端连接,多个客户端来的时候&…

传输层协议 ——— TCP协议

TCP协议 TCP协议谈谈可靠性为什么网络中会存在不可靠?TCP协议格式TCP如何将报头与有效载荷进行分离?序号与确认序号 确认应答机制(ACK)超时重传机制连接管理机制三次握手四次挥手 流量控制滑动窗口拥塞控制延迟应答捎带应答面向字…

使用ESP-01/ESP-01S接入Homekit远程控制电器

一、准备材料 ESP-01/ESP-01s 芯片 、 继电器模块 、 烧录器 二、下载固件和烧录软件 固件地址https://github.com/RavenSystem/esp-homekit-devices 烧录软件下载地址:https://drive.google.com/file/d/1_M4EzolaJWpYXts_FwUIqH8pZWqy-fye/view 三、烧录固件 …

【原创】Qt库open62541 MinGW编译

一、前言 为了统一公司的驱动层开发,准备采用OpcUA的方式转发底层数据,而服务器有Windows Server,也有CentOS,因此想用Qt开发一个基于MinGW的OpcUA Server,这样就能跨平台部署。这里记录一下,希望对你也有用…

神经网络 | 常见的激活函数

Hi,大家好,我是半亩花海。本文主要介绍神经网络中必要的激活函数的定义、分类、作用以及常见的激活函数的功能。 目录 一、激活函数定义 二、激活函数分类 三、常见的几种激活函数 1. Sigmoid 函数 (1)公式 (2&a…

问题:创业者在组建创业团队时,在个人特征和动机方面更应该注重创业者的( ) #知识分享#微信#媒体

问题:创业者在组建创业团队时,在个人特征和动机方面更应该注重创业者的( ) 参考答案如图所示

hook函数——useState

useState useState是React中的一个Hook函数,用于在函数组件中添加状态。基本使用语法如下: const [state, setState] useState(initialState) state:表示当前状态的值setState:更新状态的函数initialState:初始状态…

为什么是0.1uF电容?

旁路电容是电子设计中常用的电容器之一,主要用于过滤电源噪声和稳定电源电压。在实际应用中,0.1uF电容器是最常用的旁路电容值之一,那么为什么常用旁路电容是0.1uF而不是其他值?这个值又是怎么来的呢?本文将深入探讨这…

基于微信小程序的校园二手交易平台

博主介绍:✌程序员徐师兄、7年大厂程序员经历。全网粉丝12w、csdn博客专家、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ 🍅文末获取源码联系🍅 👇🏻 精彩专栏推荐订阅👇…

UDP 用户数据报协议

目录 1 UDP 1.1 UDP 的主要特点 1.1.1 UDP 是面向报文的 1.1.2 UDP 通信和端口号的关系 1.2 UDP 的首部格式 1.2.1 UDP 基于端口的分用 1.3 UDP抓包 1 UDP UDP 只在 IP 的数据报服务之上增加了一些功能: 1.复用和分用 2.差错检测 1.1 UDP 的主要特点 1.无连…

PSM-Net根据Stereo图像生成depth图像

一、新建文件夹 在KITTI数据集下新建depth_0目录 二、激活anaconda环境 conda activate pt14py37三、修改submission.py文件 3.1 KITTI数据集路径 parser.add_argument(--datapath, default/home/njust/KITTI_DataSet/00/, helpselect model)3.2 深度图像输出路径 save…