PyFlink简介
什么是PyFlink
PyFlink是一个可以让我们用Python语言编写Flink程序的库。它提供了与Flink Java/Scala API等价的Python API。
PyFlink的主要特征包括:
- 支持Flink的核心API和运行时,可以访问和利用Flink的所有功能
- 提供批处理(DataSet)和流处理(DataStream)两种编程抽象
- 基于pandas DataFrame API实现的批处理支持
- 基于asyncio的异步流式处理
- Python用户自定义函数(UDF)支持
- 机器学习库(TensorFlow/PyTorch/scikit-learn)的集成
PyFlink使得Python用户可以非常方便地开发Flink数据分析程序,不需要掌握Java或Scala即可构建复杂的流批任务。它降低了编程复杂度,提供更直观的编程体验。
总体而言,PyFlink充分发挥了Python的简洁和Flink的并行计算优势,为数据工程师和数据科学家提供了一个更易用、更富表现力的大数据分析框架。它使Python和Flink两者的优点得以完美结合。
PyFlink的特性及优势
PyFlink作为Flink的Python API,具有以下主要特性和优势:
- 完全兼容Flink:PyFlink支持Flink所有核心API和运行时特性,用户可以无缝使用PyFlink访问Flink的功能。
- 流批一体:同时支持流处理和批处理,提供统一的编程模型。
- 基于pandas的批处理:批处理支持基于用户熟悉的pandas DataFrame API。
- 异步流处理:基于asyncio实现的异步数据流处理。
- Python UDF:支持使用Python自定义函数,扩展计算能力。
- 机器学习集成:提供与TensorFlow、PyTorch等的深度集成。
- 部署灵活:支持在YARN、K8s、Standalone等环境部署。
- 性能优越:通过代码生成和优化,批处理性能可与Java版媲美。
- 简化编程:大大降低编程复杂度,提高工作效率。
- 开发调试方便:支持Jupyter Notebook,有助于交互式开发。
综上,PyFlink兼具Python的简洁优雅和Flink的性能优势,是构建流批一体分析应用的极佳选择。它可以大幅降低开发和使用门槛,提升数据分析效率。
PyFlink vs Flink Java/Scala API对比
这里我简要对比一下PyFlink和Flink的Java/Scala API:
- 编程语言:PyFlink使用Python;Flink Java API使用Java;Flink Scala API使用Scala。
- 编程范式:PyFlink支持面向对象和函数式编程;Java API是面向对象的;Scala API支持函数式编程。
- 代码风格:PyFlink代码简洁明了;Java API代码相对更繁琐;Scala API代码较为简洁。
- 批处理支持:PyFlink建立在Pandas之上,直接操作DataFrame;Java和Scala API通过DataSet API处理批数据。
- 流处理支持:所有三者都通过DataStream API进行流处理。
- 用户自定义函数:PyFlink通过Python函数实现UDF;Java和Scala通过继承接口实现UDF。
- 可维护性:PyFlink的动态类型特性增加了灵活性,较易维护扩展。
- 性能:PyFlink通过代码生成约等于Java性能;略优于Scala。
- 可移植性:Java和Scala API可直接移植到大数据系统如Spark;PyFlink仅可在Flink环境下运行。
- 学习曲线:PyFlink对Python用户更友好,降低了学习难度。
总体来说,PyFlink集Python和Flink优点于一体,是更易用和富有表现力的Flink API。
环境配置
安装PyFlink
使用conda安装PyFlink的步骤如下:
- 创建并激活一个conda环境:
conda create -n pyflink python=3.10
conda activate pyflink
- 配置conda以便搜索anaconda cloud上的PyFlink包:
conda config --add channels conda-forge
- 搜索可用的PyFlink版本:
conda search pyflink
- 选择需要的PyFlink版本并安装:
# 例如安装最新版本
conda install pyflink=1.17.1
- 验证是否安装成功:
import pyflink
print(pyflink.__version__)
这样通过conda就可以方便快捷地安装PyFlink环境,并管理多个版本。
conda也能自动处理PyFlink所依赖的其他包,如py4j、pandas等。非常适合用于数据科学、机器学习项目中。
创建PyFlink环境
这里是在Python中创建PyFlink执行环境的常见方式:
1. 创建批处理环境
from pyflink.dataset import ExecutionEnvironment
env = ExecutionEnvironment.get_execution_environment()
2. 创建流处理环境
from pyflink.datastream import StreamExecutionEnvironment
env = StreamExecutionEnvironment.get_execution_environment()
3. 设置执行模式
# 本地执行
env.set_runtime_mode(RuntimeExecutionMode.LOCAL)
# 集群执行
env.set_runtime_mode(RuntimeExecutionMode.CLUSTER)
4. 注册函数
env.register_function("my_udf", udf)
5. 配置参数
env.get_config().set_parallelism(16)
通过创建执行环境,可以启动PyFlink作业的运行,并进行必要的配置。
PyFlink基础概念
Execution Environment
在PyFlink中,Execution Environment(执行环境)是所有Flink程序的基础入口点。它负责建立程序的上下文环境并负责执行程序。
PyFlink中创建Execution Environment的常用方式有两种:
- 创建一个批处理执行环境:
env = ExecutionEnvironment.get_execution_environment()
env.set_runtime_mode(RuntimeExecutionMode.BATCH)
- 创建一个流处理执行环境:
# 默认是流执行环境
env = StreamExecutionEnvironment.get_execution_environment()
# 也可以直接指定
env.set_runtime_mode(RuntimeExecutionMode.STREAMING)
Execution Environment提供了下列主要功能:
- 并行执行:设置默认的并行度,以并行方式执行函数。
- 配置:管理执行参数,如重新启动策略、临时文件夹等。
- 连接外部系统:通过连接器或格式描述符与文件、 Kafka等外部系统交互。
- 指定运行环境:是在IDE直接运行还是提交到集群运行。
- 检查点配置:配置Checkpoint参数。
- 注册函数:在环境中注册用户自定义函数。
- 执行程序:最终执行批处理或流处理程序。
需要运行Flink Python程序时,第一步都是创建Execution Environment,这为后续编写实际的处理逻辑打下基础。
DataSet和DataStream
在PyFlink中,DataSet和DataStream是核心的编程抽象,用于构建批处理和流处理程序。
- DataSet
DataSet代表一个由各种数据组成的批量数据集合,用于构建批处理程序。常见操作包括:
- 从文件、数据库等源创建DataSet
- 对DataSet进行映射、过滤、聚合等转换操作
- 将DataSet写入文件、数据库等外部系统
- 通过connect()或join()将多个DataSet连接
- 对DataSet进行分组(groupBy)和聚合(aggregate)
- 对DataSet排序(sortPartition)
-
- 主要API:
from_elements()
创建DataSetmap()
flat_map()
映射转换filter()
过滤reduce()
aggregate()
聚合join()
连接两个DataSetco_group()
按分组连接sort_partition()
排序
使用DataSet可以方便地对批量数据进行ETL和分析。
- DataStream
DataStream表示一个包含连续数据的流,用于构建流处理程序。常见操作包括:
-
从socket、kafka等创建流数据源
-
对DataStream进行各种转换操作
-
定义时间语义,处理事件时间
-
对流应用状态进行检查点配置
-
将流输出到文件系统、kafka等外部系统
-
通过connect、join等操作连接流
-
对流进行窗口化操作
-
主要API:
from_elements()
创建DataStreammap()
flat_map()
映射转换filter()
过滤key_by()
分流window()
滚动/滑动窗口process()
sink_to()
输出sink
使用DataStream可以构建实时的流处理和分析应用。
DataSet和DataStream为PyFlink提供了统一的批流处理编程抽象和API。
函数和操作符
在PyFlink中,函数和操作符是编程的基本构建块,用于实现数据转换逻辑。
- 函数
PyFlink支持使用Python编写函数,常见类型包括:
- 源函数(SourceFunction):产生数据的函数
- 映射函数(MapFunction):一对一转换函数
- 扁平映射函数(FlatMapFunction):一对多转换函数
- 过滤函数(FilterFunction):过滤函数
- 减少函数(ReduceFunction):聚合函数
- 用户定义函数(UDF):自定义业务逻辑函数
所有这些函数可以方便地在PyFlink程序中被使用。
- 操作符
PyFlink提供了各种预定义的转换操作符,包括:
- map() / flat_map():一对一/一对多映射
- filter():过滤
- reduce() / aggregate():聚合
- join() / connect():连接
- union():合并
- window():窗口
通过组合使用这些操作符,可以实现批流程序的复杂数据处理需求。
函数和操作符为PyFlink提供了丰富的编程模型,可实现自定义和灵活的数据处理。它们是开发PyFlink程序的基础。
数据类型和序列化
在 PyFlink中,支持以下主要的数据类型和序列化:
数据类型
- 基本类型:String、Boolean、Numeric等
- 数组类型:ArrayType
- 行类型:RowType
- 复合类型:TupleType、MapType等
序列化
Flink需要对数据进行序列化和反序列化。PyFlink支持多种序列化方式:
- Pickle序列化:默认方式,可序列化大部分Python对象
- JSON序列化:通过json库进行JSON编码
- Avro序列化:需要注册Avro序列化schema
- Arrow序列化:支持跟Pandas交换Arrow格式数据
数据转换
PyFlink提供了方便的数据类型转换:
- to_array() 将集合转换为数组
- to_dict() 将Row转换为字典
- to_json() 序列化对象为JSON
灵活使用这些类型转换可以简化代码。
注意正确设置序列化,避免数据解析错误。 Pickle序列化对Python对象支持广,但不够高效。 Avro和Arrow支持的数据类型更受限制,但性能更好。
PyFlink编程模型
批处理编程
读取和写入数据
在PyFlink中,读取和写入批处理数据有以下常用方式:
读取数据
- 从集合创建DataSet:
ds = env.from_elements([1, 2, 3])
- 从文件读取:TextFile格式、Csv等
ds = env.read_text('/path/to/file')
- 从数据库读取:通过JDBC连接器
ds = env.from_jdbc(...)
写入数据
- 写入到文件系统:
ds.write_csv('/path/to/file')
- 输出到标准输出:
print(ds)
- 写入到数据库:通过JDBC连接器
ds.write_jdbc(...)
- 写入到数据汇表:通过connect_sink接口
ds.connect_sink(...)
通过这些机制,可以访问各种外部系统的数据源,也可以将处理结果输出到不同目标系统。
转换操作
在PyFlink中,常见的批处理DataSet转换操作包括:
- map / flat_map: 元素级变换
ds.map(lambda x: x * 2)
- filter: 过滤元素
ds.filter(lambda x: x > 5)
- reduce / aggregate: 数据聚合
ds.reduce(lambda x, y: x + y)
- join / co_group: 连接两个数据集
ds1.join(ds2).where(1).equal_to(2)
- union: 合并多个数据集
ds1.union(ds2)
- partitionBy / sortPartition: 分区和排序
ds.partition_by_hash('id').sort_partition('score', Order.DESCENDING)
- with_broadcast_set: 广播小数据集
ds.with_broadcast_set('dim_table', dim_ds)
通过组合这些转换操作,可以实现复杂的业务逻辑和数据处理流程。
分组和聚合
在PyFlink中,可以通过 groupby 和 aggregate 操作对批处理数据进行分组和聚合。
分组 GroupBy
按指定键对数据集进行分组:
grouped_ds = ds.group_by('key')
聚合 Aggregate
在分组上进行聚合运算:
grouped_ds.aggregate(AggregateFunction)
常用的聚合函数包括:
- Sum
- Min
- Max
- Count
- Average
- 以及自定义聚合函数
示例:
# 按学生分组,计算每组学生的平均分数
ds.group_by('student_id')
.aggregate(StudentAverage('score'))
groupBy 和 aggregate 提供了对批数据集进行聚合统计和得出指标的能力,是批处理的重要操作。
流处理编程
定义源
在 PyFlink中定义流处理程序的数据源,常见的方式包括:
- 从集合创建流:
stream = env.from_collection([1, 2, 3])
- 从文件读取流:
stream = env.read_text('file_path')
- 从kafka读取流:
stream = env.add_source(FlinkKafkaConsumer(topics='topic', deserialization_schema=DeserializationSchema(), properties={}))
- 自定义源函数:
class MySource(SourceFunction):
def run(self):
...
stream = env.add_source(MySource())
- 从数据流生成流:
stream1 = ...
stream2 = stream1.filter(...)
按数据源区分,PyFlink支持集合、文件、Kafka等外部流源,也可以自定义源函数实现自定义逻辑。
转换操作
在PyFlink中,常见的流处理DataStream转换操作包括:
- map / flat_map: 元素级变换操作
stream.map(lambda x: x * 2)
- filter: 过滤流中的元素
stream.filter(lambda x: x > 5)
- key_by: 将流进行分区
stream.key_by(lambda x: x[0])
- reduce: 在流上进行Reduce聚合操作
stream.key_by(...).reduce(lambda x, y: x + y)
- connect / union: 合并流
stream1.connect(stream2)
- window: 基于时间或计数的窗口聚合
stream.key_by(...).window(TumblingWindow)
- process: 自定义处理流的过程函数
stream.process(MyProcessFunction())
通过组合这些转换操作,可以在流上进行复杂的Events Driven 应用处理。
输出结果
在PyFlink中,常见的流处理结果输出方式包括:
- 打印输出:
stream.print()
- 写出到文件:
stream.write_text('/path/to/file')
- 写出到Kafka:
stream.add_sink(FlinkKafkaProducer('topic'))
- 写出到数据库:
stream.add_sink(JdbcSink())
- 自定义Sink:
class MySink(SinkFunction):
def invoke(self, value):
...
stream.add_sink(MySink())
- 写出到动态表:
stream.execute_insert('output_table')
- 写出到数据汇:
stream.add_sink(Converters.to_data_stream(collector))
PyFlink支持将流处理结果输出到外部存储、消息系统或通过自定义Sink函数输出到其他目标系统。
PyFlink Table API和SQL
创建表
在PyFlink中,可以通过Table API和SQL来创建表。
Table API
使用table_environment.from_elements
从数据集合创建表:
from pyflink.table import TableEnvironment
env = TableEnvironment.create(...)
table = env.from_elements([(1, 'John'), (2, 'Peter')])
或者从文件、数据库等外部系统读取数据创建表。
SQL
使用CREATE TABLE
语句创建表:
CREATE TABLE MyTable (
id BIGINT,
name STRING
) WITH (
'connector' = 'filesystem',
'path' = '/path/to/file'
);
注册表
无论是使用Table API还是SQL创建的表,都需要在环境中注册以便后续查询:
env.create_temporary_table('MyTable', table)
CREATE TEMPORARY TABLE MyTable ...
Table API和SQL为PyFlink提供了声明式的表处理和分析能力。
查询表
在PyFlink中,可以通过Table API和SQL来查询表中的数据。
Table API
通过table
对象查询:
result = table.select(table.id, table.name).filter(table.id > 1)
SQL
通过sql_query
方法查询:
result = env.sql_query("SELECT id, name FROM MyTable WHERE id > 1")
或者在SQL CLI环境中交互查询:
Flink SQL> SELECT * FROM MyTable
结果处理
查询结果可以进一步转换或输出:
result.to_pandas() # 转为 Pandas DataFrame
result.execute_insert('output_table') # 插入到表中
Table API提供了面向对象的查询方式,SQL提供了声明式的查询方式。结合两者可以实现复杂的表分析和处理。
关联和聚合
在PyFlink中,Table API和SQL都支持对表进行联结和聚合查询。
关联查询
Table API:
result = table1.join(table2).where(table1.id == table2.id)
SQL:
SELECT *
FROM table1
JOIN table2
ON table1.id = table2.id
支持INNER JOIN、LEFT JOIN、RIGHT JOIN等。
聚合查询
Table API:
result = table.group_by(table.key).select(table.key, table.value.avg)
SQL:
SELECT key, AVG(value)
FROM table
GROUP BY key
支持聚合函数 COUNT、SUM、AVG、MAX、MIN等。
Table API 和 SQL为PyFlink提供了统一的表处理查询能力,可以混合使用。
部署和运行
本地执行
使用PyFlink进行本地部署运行的基本步骤如下:
- 安装PyFlink
使用pip或conda安装PyFlink及其依赖:
pip install apache-flink
- 编写PyFlink程序
编写批处理或流处理程序代码,并设置执行环境。
- 启动本地Flink集群
启动standalone session集群作为运行时环境:
./bin/start-cluster.sh
- 提交PyFlink程序
设置执行环境为本地集群后提交程序:
env.set_runtime_mode(execution_mode.cluster)
env.execute("pyflink job")
- 监控执行
通过Flink UI监控作业运行情况:
http://localhost:8081
- 停止集群
作业结束后关闭本地standalone集群:
./bin/stop-cluster.sh
这提供了一个基本的PyFlink本地测试、调试、运行的工作流程。也可以部署到其他集群环境中进行生产运行。
提交到Standalone、YARN等集群
PyFlink程序可以部署到Standalone、YARN等不同的集群环境中进行生产运行。
Standalone集群
- 启动Flink Standalone集群
- 在执行环境中设置运行模式为集群:
env.set_runtime_mode(execution_mode.cluster)
- 设置JAR包位置:
env.add_jars("myprogram.jar")
- 提交PyFlink作业,会提交到Standalone集群
YARN集群
- 启动YARN集群
- 设置执行环境为YARN模式
- 设置JAR包位置
- 设置其他YARN配置(队列等)
- 提交PyFlink作业到YARN集群运行
Kubernetes Session集群
启动Flink K8s Session集群后,部署方式与Standalone类似。
PyFlink可以与主流集群资源管理器无缝集成,支持弹性的生产环境部署。
调试和测试
日志和监控
PyFlink提供了日志和指标监控的功能,可以帮助观察和诊断作业运行情况。
日志
PyFlink内置log4j日志框架,支持控制台打印和文件输出。
设置日志级别:
import logging
logging.info("test logging")
指标监控
PyFlink收集了丰富的指标,可以在Web UI中查看,也可以配置推送到Prometheus。
常用指标:
- startTime: 作业启动时间
- numRestarts: 故障重启次数
- numBytesInLocal: 本地输入数据量
- numBytesOutRemote:远程输出数据量
- numLateRecordsDropped:迟到数据丢弃数
Web UI
Flink Web UI显示了作业度量指标、检查点信息、异常等监控数据。
外部系统
还可以使用ELK栈进行日志分析,Prometheus等系统存储指标数据。
PyFlink的监控功能可以提供作业运行状态的全方位视图。
测试数据
PyFlink提供了一些内置的测试数据,可以用来调试和演示程序,主要包括:
集合数据
直接使用from_elements创建包含内置数据的表或流:
env.from_elements([(1, 'Hi'), (2, 'Hello')])
文件数据
从内置的文件读取测试数据:
- words.txt:常用英文单词
- numbers.txt:随机数
- logs.txt:模拟日志
例如:
env.read_text('words.txt')
生成数据
使用生成函数创建测试数据:
- generate_sequence(from, to)
- generate_random_bounded_string(len, chars)
例如:
ds = env.generate_sequence(1, 100000)
连接器数据
一些连接器提供了获取测试数据的方法:
- kafka_get_test_data_set
- es_get_test_data_set
等等
PyFlink提供了丰富的测试数据源,可以方便编写和调试程序。
模块化和组件测试
在PyFlink中可以通过以下方式进行模块化和组件测试:
- 单元测试
使用unittest等框架,基于PyFlink的组件接口编写单元测试用例,例如对自定义函数进行独立测试。
- 集成测试环境
- LocalEnvironment - 在本地设置一个模拟运行环境进行集成测试。
- RemoteEnvironment - 连接远程集群集成测试。
- 模拟数据源
使用TestSourceContext并行读取内存数据,测试流组件 chains。
- 测试Sink
使用TestSink验证和检查执行结果。
- 本地执行
在不启动整个集群的情况下,通过本地执行模式测试组件串联执行。
- 集群模拟
MiniClusterWithClient可以在本地启动一个最小化的集群用于测试。
- CI框架
结合GitHub Actions, Jenkins等实现自动化测试。
通过这些策略,可以对PyFlink程序进行全面的测试与验证,提高质量。
最佳实践
性能优化
这里我总结一下在Flink中的常见性能优化方案:
- 优化检查点配置
- 调大checkpoint间隔
- 使用INCREMENTAL模式
- 优化State Backend
- 调优Task参数
- 适当增大task slots提高并行度
- 优化slot与CPU核心绑定
- 数据倾斜优化
- rebalance
- rescale
- shuffle优化
- 提高网络传输效率
- 减少数据shuffle
- 开启backpressure
- 运行时通信优化
- 减少小消息通信
- 批量发送与接收
- 减少序列化开销
- 使用Avro、Protobuf等优化序列化
- 算法调优与代码生成
- 选择高效算法
- 开启代码生成
- 优化状态后端
- RocksDB参数优化
- 更换更快的状态后端
多方面综合调优可以显著提升Flink大规模生产环境下的执行效率。
常见问题及解决方案
Flink应用运行中常见的问题及解决方案包括:
- 数据倾斜:通过rebalance等方式缓解数据分布不均
- 任务堆积:增加task slots数,扩大资源规模
- 内存溢出:适当调低并行度,增加资源容量
- 检查点失败:简化状态结构,优化检查点配置
- 序列化错误:检查数据格式匹配情况,必要时自定义序列化器
- 网络IO问题:扩大网络带宽,优化数据传输
- 结果错误:检查业务逻辑,增加打印和assert校验
- 死锁:检查看是否存在竞争资源
- 性能下降:调优Task参数,提高并行度等
- 高重启次数:优化故障转移配置,减少转移
- 资源竞争:隔离不同job,指定slot资源比例
通过日志打印和指标监控,可以定位问题原因,并根据类型采取不同的优化手段进行故障排除和性能提升。
# PyFlink学习代码
https://github.com/huanglaoxie0503/flink-basic-introduction