Flink原理介绍

目录

  1. Flink基础与核心设计
  2. 时间语义在Flink中的应用
  3. 状态管理与检查点机制
  4. Exactly-Once语义保障机制
  5. Flink配置调优策略
  6. 总结

1. Flink基础与核心设计

Flink是Apache软件基金会开源的流处理框架,核心定位是高效、可靠地处理大规模无界/有界数据流,以高性能、低延迟、灵活的窗口操作为核心优势,广泛应用于实时分析、金融风控、物联网等场景。

1.1 Flink简介及发展历程

  • 核心定位 :开源流处理框架,支持无界流(实时流)有界流(批处理) 统一处理,打破"流批分离"的传统模式。
  • 发展背景 :前身是德国柏林工业大学的Stratosphere项目,后捐献给Apache基金会并更名为Flink,经过多版本迭代成为大数据实时处理领域的核心工具。
  • 典型应用场景
    • 金融领域:实时交易监控、欺诈检测;
    • 物联网:传感器数据流实时分析(如智能设备状态监控);
    • 互联网:用户行为实时分析、实时推荐、日志实时处理。

1.2 架构设计概览

Flink采用主从(Master-Slave)架构 ,核心角色包括JobManager(主节点)、TaskManager(从节点),辅以ClientDispatcherResourceManager完成作业提交与资源调度,架构交互流程如下:

角色 核心职责
Client 负责将用户编写的Flink作业(JAR包)编译为数据流图(Dataflow Graph),提交给JobManager。
Dispatcher 接收Client提交的作业,启动JobManager实例,并将作业分配给对应的JobManager。
ResourceManager 管理集群资源(CPU、内存),为TaskManager注册资源,为JobManager分配任务所需的Slot(资源单元)。
JobManager 1. 协调作业执行:将Dataflow Graph优化为执行图(Execution Graph) ,拆分任务并分配给TaskManager; 2. 容错与恢复:触发检查点(Checkpoint),故障时协调任务恢复; 3. 高可用性:支持多实例部署,避免单点故障。
TaskManager 1. 执行具体任务:运行Dataflow中的算子子任务(Subtask),通过Slot隔离资源; 2. 数据通信:通过Network Manager实现跨TaskManager的数据传输; 3. 状态管理:存储本地任务的状态,参与检查点快照生成。
  • 水平扩展能力:可通过增加/减少TaskManager节点动态调整集群处理能力,Slot数量决定并行任务的最大数量。

1.3 核心组件与功能

Flink作业的执行流程依赖"数据源-转换-输出"三大核心组件,形成完整的数据处理链路:

组件 功能描述 典型实现/场景
Source(数据源) 从外部系统读取数据,将其转换为Flink可处理的数据流(DataStream/DataSet)。 - 消息队列:Kafka、RabbitMQ; - 文件系统:HDFS、本地文件; - 数据库:MySQL(CDC同步)、HBase。
Transformation(转换) 对输入数据流进行计算处理,如过滤、映射、聚合、关联等,生成新的数据流。 - 基础转换:map(映射)、filter(过滤)、flatMap(扁平映射); - 聚合操作:keyBy(按键分组)、sum(求和)、max(最大值); - 窗口操作:window(时间/数量窗口)、windowAll(全局窗口)。
Sink(接收器) 将处理后的数据流输出到外部系统,完成数据落地或进一步消费。 - 消息队列:Kafka(下游消费); - 数据库:MySQL、Hive、ClickHouse; - 文件系统:HDFS(批量存储); - 监控系统:Prometheus(指标上报)。

1.4 数据流模型与并行处理

Flink以无界数据流(Unbounded Stream) 为核心模型,同时支持有界数据流(Bounded Stream,即批处理),关键特性如下:

  1. 无界流模型:数据持续产生,无固定结束点,Flink通过"增量处理"实时消费数据,而非等待全部数据就绪。
  2. 并行处理机制
    • 将数据流按分区(Partition) 拆分,每个分区由一个TaskManager的Slot独立处理;
    • 分区规则可自定义(如按keyBy的键哈希分区、轮询分区),确保数据均匀分布,提升处理效率。
  3. 灵活的窗口操作
    • 支持时间窗口(如滚动窗口、滑动窗口、会话窗口):基于事件时间或处理时间聚合数据(如"每5分钟统计一次订单量");
    • 支持数量窗口:基于数据条数聚合(如"每100条数据计算一次平均值");
    • 解决无界流中"无法等待全部数据再聚合"的问题,实现实时统计分析。

2. 时间语义在Flink中的应用

时间是流处理的核心维度,直接影响数据排序、窗口触发、状态更新的准确性,Flink提供明确的时间语义与配套机制解决乱序和延迟问题。

2.1 时间概念及重要性

  • 核心价值:流数据具有"时序性",时间语义决定了"何时触发计算""如何判断数据完整性",直接影响处理结果的准确性(如"统计当天订单量"需基于事件发生时间,而非处理时间)。
  • 时间分类:Flink支持两种核心时间语义,需根据业务场景选择:
时间类型 定义 优势 劣势 适用场景
事件时间(Event Time) 事件实际发生的时间,由事件自带的时间戳(如日志中的event_time字段)标识。 结果准确,不受系统处理速度、网络延迟影响。 需额外携带时间戳,需处理乱序数据。 金融风控(精确时间窗口统计)、用户行为分析(按事件发生顺序归因)。
处理时间(Processing Time) 事件被Flink系统接收并开始处理的时间,由TaskManager的系统时钟决定。 实现简单,无需时间戳,处理延迟低。 分布式环境中节点时钟可能不一致,结果不准确。 实时性要求极高、对时间精度要求低的场景(如实时日志打印、临时监控)。

2.2 Watermark(水印)机制

Watermark是Flink处理乱序事件的核心技术,本质是"数据流中的特殊标记",用于标识"某个时间点之前的所有事件已到达",触发窗口计算。

2.2.1 Watermark的核心作用
  • 解决乱序问题:允许事件延迟到达,通过Watermark定义"数据就绪的时间边界",避免因个别延迟事件导致窗口过早触发。
  • 触发窗口计算:当Watermark时间超过窗口的结束时间时,Flink认为窗口内所有事件已到达(或超过最大延迟),触发窗口聚合。
2.2.2 Watermark的生成与传递
  1. 生成方式
    • 数据源端生成:Source算子根据事件时间戳,结合"最大允许延迟时间"生成Watermark(如event_time - 5s,表示允许5秒延迟);
    • 示例:若事件时间戳为10:00:10,最大延迟5秒,则Watermark为10:00:05,表示10:00:05前的事件已全部到达。
  2. 传递机制
    • Watermark随数据流在算子间传递,下游算子接收Watermark后,更新本地Watermark,并继续向下传递;
    • 若算子有多个上游分区,取所有上游Watermark的最小值作为本地Watermark(确保所有分区的数据都已就绪)。

2.3 延迟数据处理策略

2.3.1 延迟数据的产生原因
  • 网络延迟:事件从产生端(如传感器、APP)传输到Flink的过程中因网络拥堵延迟;
  • 系统故障:数据源(如Kafka)临时下线,恢复后批量补发历史事件;
  • 数据积压:TaskManager负载过高,导致事件处理排队延迟。
2.3.2 核心处理策略

Flink提供3种主流延迟数据处理方式,可根据业务需求组合使用:

  1. 设置最大延迟时间
    • 在Watermark生成时指定allowedLateness(如5秒),窗口触发后仍接收5秒内的延迟事件,更新窗口结果;
    • 超过最大延迟的事件将被丢弃或路由到侧输出流。
  2. 侧输出流(Side Output)
    • 将超过最大延迟的事件路由到独立的"侧输出流",而非直接丢弃;
    • 可对侧输出流单独处理(如存储到异常表、人工排查),避免数据丢失。
  3. 状态更新
    • 对于有状态计算(如累计计数),延迟事件到达后更新历史状态(需确保状态保留时间足够长);
    • 示例:用户当天的登录次数统计,延迟的登录事件仍需更新当天的累计次数。
2.3.3 延迟数据的影响与应对措施
  • 主要影响:导致窗口结果临时不准确(如窗口触发时少算延迟事件)、状态不一致(如累计值未更新)。
  • 应对措施
    • 合理设置最大延迟时间(基于历史延迟数据统计,如99%的事件延迟在3秒内,则设为5秒);
    • 延长状态保留时间(与最大延迟时间匹配),确保延迟事件可更新历史状态;
    • 监控延迟事件比例,若比例过高(如超过10%),需优化网络或扩容TaskManager。

3. 状态管理与检查点机制

流处理中"状态"是记录中间计算结果的核心(如累计计数、会话信息),Flink提供完善的状态管理与检查点机制,确保故障后状态可恢复,数据处理不中断。

3.1 状态管理基本概念及分类

3.1.1 状态的定义
  • 状态是Flink算子在处理数据流过程中需要"记住"的中间数据(如"用户A的累计登录次数""窗口内的订单总金额"),用于后续计算或故障恢复。
3.1.2 状态分类

根据状态与"数据键(Key)"的关联关系,Flink将状态分为两类:

状态类型 定义 支持的状态类型 适用场景
键控状态(Keyed State) 与特定"键(Key)"绑定的状态,仅在keyBy后的算子中使用(如summax); 每个Key对应独立的状态实例,不同Key的状态相互隔离。 - ValueState:存储单个值(如用户余额); - ListState:存储列表(如用户最近10次操作); - MapState:存储键值对(如用户的订单ID与金额映射); - ReducingState/AggregatingState:存储聚合结果。 按Key分组的聚合计算(如"按用户ID统计登录次数""按商品ID统计销量")。
操作符状态(Operator State) 与算子实例(Subtask)绑定的状态,不依赖Key,每个算子实例对应一个状态实例; 算子并行度调整时,状态会在新实例间重新分配。 - ListState:最常用,如Kafka Source的"已消费偏移量"; - UnionListState:合并所有实例的状态(如广播状态)。 非Key关联的全局状态(如Kafka消费者的偏移量管理、广播配置信息)。

3.2 检查点(Checkpoint)机制

检查点是Flink实现容错的核心技术,通过"定期保存全量状态快照",确保故障后从最近的快照恢复,避免数据重复处理或丢失。

3.2.1 检查点的核心原理
  • 定义:按配置的时间间隔(如1分钟)对所有算子的状态、数据流中的Watermark进行快照,存储到持久化介质(如HDFS);
  • 目标:故障(如TaskManager宕机)后,Flink从最近的检查点加载所有算子的状态,恢复到故障前的一致状态,重新处理故障后的数据流。
3.2.2 检查点的工作流程
  1. 触发检查点
    • JobManager的CheckpointCoordinator按配置的间隔(如checkpoint.interval=60000ms)触发检查点,向所有Source算子发送"检查点屏障(Checkpoint Barrier)"。
  2. 同步/异步快照
    • 算子接收屏障后,对当前状态生成快照:
      • 同步快照:暂停数据处理,生成快照后继续处理(简单但影响吞吐量);
      • 异步快照:后台生成快照,不暂停数据处理(推荐,Flink默认方式)。
  3. 状态存储
    • 将快照数据写入状态后端(如HDFS、RocksDB),算子向CheckpointCoordinator汇报快照成功。
  4. 确认与恢复
    • 所有算子快照成功后,CheckpointCoordinator标记该检查点为"完成";
    • 故障时,JobManager通知所有算子从最近的完成检查点加载状态,恢复处理。

3.3 状态后端存储选择及配置

状态后端决定状态的存储位置和方式,Flink提供3种状态后端,需根据业务规模选择:

状态后端类型 存储位置 优势 劣势 适用场景
MemoryStateBackend 存储在TaskManager的JVM堆内存中,检查点快照存储在JobManager内存中。 读写速度最快,无I/O开销。 状态规模有限(受堆内存限制),JobManager内存不足时易OOM,不支持大状态。 小规模测试、非关键性任务(如临时数据过滤)。
FsStateBackend 状态存储在TaskManager堆内存中,检查点快照存储在分布式文件系统(如HDFS)。 支持中等规模状态,检查点持久化安全。 状态过大时TaskManager堆内存易OOM,不支持超大规模状态。 中等数据量的生产任务(如日处理千万级事件)。
RocksDBStateBackend 状态存储在本地RocksDB(嵌入式KV数据库)中,检查点快照存储在分布式文件系统。 支持超大规模状态(TB级),本地存储不占用JVM堆内存。 读写需序列化/反序列化,性能略低于内存后端。 大规模生产任务(如日处理亿级事件、大窗口聚合)。
3.3.1 关键配置参数
配置项 说明 示例值
state.backend 指定状态后端类型(memory/filesystem/rocksdb rocksdb
state.checkpoints.dir 检查点快照的存储路径(分布式文件系统路径) hdfs:///flink/checkpoints/
checkpoint.interval 检查点触发间隔(毫秒) 60000(1分钟)
state.ttl.config 状态保留时间(确保覆盖最大延迟时间,避免延迟事件无法更新状态) 86400000(24小时)
state.backend.rocksdb.localdir RocksDB本地数据存储路径(需配置在高性能磁盘如SSD) /data/flink/rocksdb/

3.4 检查点性能优化建议

  1. 使用异步快照
    • 配置state.backend.async=true(Fs/RocksDB后端默认开启),避免快照生成阻塞数据处理,提升吞吐量。
  2. 优化状态数据结构
    • 减少状态数据大小(如用Long存储计数而非String,序列化后体积更小);
    • 避免存储冗余数据(如仅存储必要字段,而非完整事件)。
  3. 选择高性能存储
    • 检查点快照存储路径选择高性能分布式文件系统(如HDFS的SSD节点),减少快照写入延迟;
    • RocksDB本地存储使用SSD,提升状态读写速度。
  4. 合理调整检查点间隔
    • 间隔过短(如10秒):快照频率过高,占用I/O和CPU资源;
    • 间隔过长(如10分钟):故障后重新处理的数据量过大,恢复时间长;
    • 推荐:基于业务容忍的恢复时间设置(如恢复时间不超过5分钟,则间隔设为1-2分钟)。

4. Exactly-Once语义保障机制

"Exactly-Once"是流处理的最高一致性级别,指"每条数据仅被处理一次,无论故障发生与否",Flink通过"事务+快照"结合的方式实现该语义。

4.1 核心前提:幂等性与事务性

4.1.1 幂等性(Idempotence)
  • 定义:操作一次与多次执行的效果完全相同,无副作用(如"将用户余额设为100"是幂等的,"给用户余额加100"是非幂等的);
  • 作用:即使数据重复处理(如故障恢复后重发),也不会导致结果错误。
4.1.2 事务性(Transactionality)
  • 定义:操作要么"完全成功"(所有步骤完成),要么"完全失败"(回滚到初始状态),无中间状态;
  • 作用:确保分布式环境中,多算子、多节点的操作一致性(如"更新状态+输出结果"需同时成功或同时失败)。

4.2 Exactly-Once的实现方法

Flink通过两种核心机制实现Exactly-Once,可根据外部系统特性选择:

4.2.1 两阶段提交协议(Two-Phase Commit, 2PC)

适用于支持事务的外部系统(如Kafka、Hive、ClickHouse),核心流程如下:

  1. 预提交阶段(Prepare)
    • Flink算子处理数据后,将结果写入外部系统的"预提交区"(如Kafka的临时分区、Hive的临时表),不对外可见;
    • 所有算子完成预提交后,向CheckpointCoordinator汇报"预提交成功"。
  2. 提交阶段(Commit)
    • CheckpointCoordinator确认所有算子预提交成功后,向所有算子发送"提交指令";
    • 算子将预提交区的结果"转正"(如Kafka临时分区数据合并到正式分区、Hive临时表重命名为正式表),对外可见;
    • 若任一算子预提交失败,CheckpointCoordinator发送"回滚指令",算子删除预提交区数据,避免脏数据。
4.2.2 利用外部系统的幂等性

适用于不支持事务但支持幂等写入的系统(如MySQL、Redis):

  • 依赖外部系统的幂等特性,即使Flink重复写入数据,系统也会自动去重;
  • 示例:
    • MySQL:通过主键约束(如订单ID),重复插入时触发主键冲突,忽略重复数据;
    • Redis:通过SET命令(如SET user:1:login_count 5),重复执行时覆盖旧值,结果一致。

4.3 分布式快照算法:Chandy-Lamport

Flink检查点的底层实现基于Chandy-Lamport算法,确保分布式环境中"所有算子的状态+数据流位置"生成全局一致的快照:

  1. 快照触发CheckpointCoordinator向所有Source算子发送"检查点屏障",屏障随数据流向下游传递;
  2. 局部快照
    • 算子接收屏障后,暂停数据处理,对当前状态生成局部快照;
    • 快照包含"算子状态"和"屏障在数据流中的位置"(如Kafka的消费偏移量);
  3. 全局一致性
    • 算子生成局部快照后,将屏障继续传递给下游算子,下游算子重复局部快照流程;
    • 所有算子完成局部快照后,全局快照生成成功,确保"状态与数据流位置"一一对应。
4.3.1 异步快照优化
  • 为避免快照阻塞数据处理,Flink在Chandy-Lamport算法基础上优化为"异步快照":
    • 算子接收屏障后,继续处理数据,同时后台线程生成状态快照;
    • 快照生成期间的新数据暂存到"缓冲区",快照完成后再处理缓冲区数据,确保快照一致性与处理吞吐量平衡。

4.4 失败恢复与一致性保证

  • 恢复流程:TaskManager宕机后,JobManager从最近的检查点加载所有算子的状态,重置数据流位置(如Kafka消费偏移量回滚到检查点时的位置),重新处理检查点后的数据流;
  • 一致性保证
    • 检查点确保"状态与数据流位置"一致,恢复后重处理的数据不会重复计算(因检查点前的数据已处理并记录状态);
    • 结合两阶段提交或外部幂等性,最终实现"每条数据仅被处理一次"的Exactly-Once语义。

5. Flink配置调优策略

合理的配置调优可显著提升Flink作业的吞吐量、降低延迟,避免资源浪费或故障,核心从"集群资源""作业并行度""内存""网络"四个维度优化。

5.1 集群资源配置建议

  1. 分离计算与存储资源

    • 计算资源(TaskManager):部署在CPU密集型节点,确保算子处理速度;
    • 存储资源(状态后端、检查点):部署在I/O密集型节点(如SSD),减少快照写入和状态读写延迟;
    • 避免计算与存储资源争用(如TaskManager与HDFS DataNode混部导致磁盘I/O瓶颈)。
  2. 合理规划集群规模

    • 根据数据量估算TaskManager数量:如日处理10亿条数据,单TaskManager每秒处理1000条,则需10^9 / (86400 * 1000) ≈ 116个TaskManager;
    • 每个TaskManager的Slot数量建议与CPU核心数匹配(如4核CPU设4个Slot),避免CPU上下文切换频繁。
  3. 选择高性能存储

    • 状态后端:大规模作业用RocksDB+SSD,中小规模用FsStateBackend+HDFS(SSD节点);
    • 检查点:存储到HDFS或对象存储(如S3),确保高可用且I/O性能稳定。

5.2 作业并行度设置技巧

并行度(Parallelism)决定作业的最大并发处理能力,需根据"数据量""算子类型""资源规模"动态调整:

  1. 按数据量调整
    • 大数据量(如每秒10万条):提高并行度(如32),分散任务负载;
    • 小数据量(如每秒100条):降低并行度(如4),避免资源浪费。
  2. 避免数据倾斜
    • 并行度设置需与keyBy的键分布匹配,若某Key的数据量过大(如占比超过30%),需拆分Key或调整并行度(如并行度为质数,减少哈希碰撞);
    • 示例:用户ID哈希分区,并行度设为16,避免某Slot处理过多用户的数据。
  3. 动态调整并行度
    • Flink支持运行时动态调整并行度(通过Flink UIREST API),无需重启作业;
    • 高峰期(如电商大促)临时提高并行度,低谷期降低并行度,节省资源。

5.3 内存管理与垃圾回收(GC)优化

Flink作业的内存溢出(OOM)和GC频繁是常见问题,需从"内存分配""GC配置""对象管理"三方面优化:

  1. 合理分配堆内存
    • 区分"Flink内存"与"JVM内存":Total Process Memory = Flink Memory + JVM Metaspace + JVM Overhead
    • Flink内存分配:
      • 状态密集型作业(如大窗口聚合):增加state.backend.memory(RocksDB后端);
      • 计算密集型作业(如复杂聚合):增加taskmanager.memory.process.size(JVM堆内存)。
  2. 选择高效的垃圾回收器
    • JDK 8及以上推荐使用G1 GC (低延迟、高吞吐量),配置:

      复制代码
      taskmanager.env.java.opts: "-XX:+UseG1GC -XX:MaxGCPauseMillis=100"
    • 避免使用CMS GC(并发标记清除,碎片化严重)或Serial GC(单线程GC,吞吐量低)。

  3. 优化对象分配与回收
    • 减少频繁创建临时对象(如在map算子中避免每次创建新HashMap);
    • 使用对象池复用频繁创建的对象(如序列化器、临时缓冲区),降低GC压力。

5.4 网络通信与序列化性能提升

网络通信是分布式作业的常见瓶颈(如跨TaskManager的数据传输),需从"缓冲区""序列化""压缩"三方面优化:

  1. 优化网络缓冲区
    • 调整网络缓冲区大小:taskmanager.network.memory.fraction=0.3(分配30%的TaskManager内存给网络缓冲区);
    • 增加缓冲区数量:taskmanager.network.memory.buffers-per-channel=2,避免数据传输时缓冲区不足导致阻塞。
  2. 选择合适的序列化方式
    • 默认Java序列化效率低,推荐使用Kryo序列化(支持自定义序列化器,性能比Java高5-10倍);

    • 对复杂数据类型(如自定义POJO),注册Kryo序列化器:

      java 复制代码
      env.getConfig().registerKryoSerializer(User.class, UserKryoSerializer.class);
    • 简单数据类型(如String、Long)可使用Avro序列化(Schema化,兼容性好)。

  3. 压缩数据传输
    • 开启网络数据压缩:taskmanager.network.compression.enable=true
    • 选择高效压缩算法(如Snappy,压缩比适中,速度快):taskmanager.network.compression.codec=snappy
    • 减少跨TaskManager的数据传输(如通过rebalance调整分区策略,避免数据倾斜导致的大量跨节点传输)。

6. 总结

6.1 Flink核心优势

  1. 实时数据处理能力:原生支持无界流处理,可实时消费并处理大规模数据流(如日志、传感器数据),满足实时业务需求。
  2. 高吞吐量与低延迟
    • 通过数据并行、流水线执行、异步快照等机制,实现每秒百万级事件处理(Throughput);
    • 基于事件时间和Watermark,延迟可控制在亚秒级(Latency),优于Spark Streaming(秒级延迟)。
  3. 灵活的窗口操作:支持时间窗口、数量窗口、会话窗口等多种窗口类型,可自定义窗口触发逻辑,满足复杂实时分析场景(如实时报表、监控告警)。
  4. 强大的容错与状态管理
    • 检查点机制确保故障后状态可恢复,结合Exactly-Once语义,数据处理一致性高;
    • 支持Keyed State和Operator State,可灵活管理中间计算结果,适配有状态计算场景(如累计统计、会话保持)。

6.2 典型应用场景

场景 应用描述 Flink优势体现
实时日志分析 处理系统/应用的实时日志,监控异常(如ERROR日志告警)、分析用户行为(如页面访问路径)。 高吞吐量处理日志流,低延迟触发告警。
物联网(IoT)数据流处理 接收传感器(如温度、湿度、位置传感器)的实时数据,实时监控设备状态、预测故障。 支持无界流处理,可处理海量传感器数据。
金融科技风控 实时监测交易数据(如信用卡消费、转账),识别欺诈行为(如异地登录、大额异常转账)。 Exactly-Once语义确保交易数据不重复处理,低延迟满足风控实时性要求。
电商实时推荐系统 处理用户实时行为(如浏览、加购、下单),结合机器学习模型生成个性化推荐。 灵活的窗口操作统计用户近期行为,低延迟更新推荐结果。
相关推荐
Hello.Reader3 小时前
Flink 项目配置从 0 到可部署
大数据·flink
eve杭6 小时前
解锁数据主权与极致性能:AI本地部署的全面指南
大数据·人工智能·5g·ai
数字时代全景窗6 小时前
商业航天与数字经济(一):从4G、5G得与失,看6G时代商业航天如何成为新经济引擎?
大数据·人工智能·5g
励志成为糕手8 小时前
Spark Shuffle:分布式计算的数据重分布艺术
大数据·分布式·spark·性能调优·数据倾斜
K_i1349 小时前
GitOps实战:Helm一键部署ArgoCD
大数据·elasticsearch·搜索引擎
1892280486110 小时前
NX482NX486美光固态闪存NX507NX508
大数据·网络·数据库·人工智能·性能优化
数据与人工智能律师12 小时前
数字人民币钱包抉择:匿名自由与实名安全的法律风险评估
大数据·人工智能·python·云计算·区块链
DashingGuy13 小时前
Spark的Broadcast Join以及其它的Join策略
大数据·spark
Hello.Reader16 小时前
用 Maven 配置 Flink 从初始化到可部署的完整实践
java·flink·maven