PyFlink
pyflink教程一、apache-flink的安装如果是Windows,需要注意这一点才能安装成功!二、Table API教程完整代码展示详解:2.1 python table api的基本流程:1. 导入2. 创建 TableEnvironment3. 创建 source 表4. 创建 sink 表5. 查询 source 表,同时执行计算6. 将计算结果写入给 sink 表2.2 创建表的通用方法:(包括创建source和sink表) from_xxx:以from开头的都是创建表,后面的单词是创建表的来源2.2.1 从自己有限的几个列表数据中添加数据2.2.2 通过DDL(数据定义语言)语句2.2.3 通过Catalog(元数据管理中心)创建2.3查询2.3.1 Table Api 查询2.3.1.1Table API 支持 行操作的 API, 包括 Map , FlatMap , Aggregate Operation 和 FlatAggregate 2.3.2 SQL查询2.3.3Table API 和 SQL 的混合使用2.3.3.1 在 SQL 中使用 Table 对象2.3.3.2 在 Table API 中使用 SQL 表:2.4 删除表2.5 输出结果2.5.1打印到客户端(本地)2.5.2将结果写入到一张 Sink 表中2.5.3将结果写入到多张 Sink 表中2.6 Explain 表2.6.1 Table.explain()2.6.2 StatementSet.explain()2.7 Table Api中的常识性问题:2.7.1 Table Api中的常识性问题:2.7.2 Table Api中sql语句的常识性问题:代码的详细分析:
一、apache-flink的安装
Java 8 or 11 推荐使用Java8,因为没有
Python 3.6, 3.7 or 3.8
相关命令,如:java -version
返回的结果举例:
java version "11.0.14" 2022-01-18 LTS
Java(TM) SE Runtime Environment 18.9 (build 11.0.14+8-LTS-263)
Java HotSpot(TM) 64-Bit Server VM 18.9 (build 11.0.14+8-LTS-263, mixed mode)
在这里我也被埋了深坑!如果版本不对,到oracle官方下载SE development kit相关版本后,添加环境变量,如果实在版本搞不过来了,在环境变量中添加JAVA_HOME,然后在环境变量的路径中添加
%JAVA_HOME%/bin
如果版本实在太多了,可以把path中的这一条往上移到最顶端,
如果是Linux,可以直接安装,没有太多坑!
如果是Windows,需要注意这一点才能安装成功!
安装visual studio 2019C++生成工具,勾选图中右侧的即可
时间较长,需耐心等待!
上述工作完成后,创建新的目录,尽量使用虚拟环境,然后安装apache-flink,就能成功了!
python -m pip install apache-flink==1.14.3
二、Table API教程
完整代码展示详解:
import argparse
import logging
import sys
from pyflink.common import Row
from pyflink.table import (EnvironmentSettings, TableEnvironment, TableDescriptor, Schema,
DataTypes, FormatDescriptor)
from pyflink.table.expressions import lit, col
from pyflink.table.udf import udtf
word_count_data = ["To be, or not to be,--that is the question:--",
"Whether 'tis nobler in the mind to suffer",
"The slings and arrows of outrageous fortune",
"Or to take arms against a sea of troubles,",
"And by opposing end them?--To die,--to sleep,--",
"No more; and by a sleep to say we end",
"The heartache, and the thousand natural shocks",
"That flesh is heir to,--'tis a consummation",
"Devoutly to be wish'd. To die,--to sleep;--",
"To sleep! perchance to dream:--ay, there's the rub;",
"For in that sleep of death what dreams may come,",
"When we have shuffled off this mortal coil,",
"Must give us pause: there's the respect",
"That makes calamity of so long life;",
"For who would bear the whips and scorns of time,",
"The oppressor's wrong, the proud man's contumely,",
"The pangs of despis'd love, the law's delay,",
"The insolence of office, and the spurns",
"That patient merit of the unworthy takes,",
"When he himself might his quietus make",
"With a bare bodkin? who would these fardels bear,",
"To grunt and sweat under a weary life,",
"But that the dread of something after death,--",
"The undiscover'd country, from whose bourn",
"No traveller returns,--puzzles the will,",
"And makes us rather bear those ills we have",
"Than fly to others that we know not of?",
"Thus conscience does make cowards of us all;",
"And thus the native hue of resolution",
"Is sicklied o'er with the pale cast of thought;",
"And enterprises of great pith and moment,",
"With this regard, their currents turn awry,",
"And lose the name of action.--Soft you now!",
"The fair Ophelia!--Nymph, in thy orisons",
"Be all my sins remember'd."]
def word_count(input_path, output_path):
# 创建表环境
t_env = TableEnvironment.create(EnvironmentSettings.in_streaming_mode())
# 表环境相关配置
# 这里的set_string("parallelism.default", "1")代表设置并行度为1
t_env.get_config().get_configuration().set_string("parallelism.default", "1")
# define the source 创建临时表
if input_path is not None:
t_env.create_temporary_table( # 创建临时表
'source', # 填写表名
TableDescriptor.for_connector('filesystem') # 这里for_connector表示和哪张表有关联,'filesystem'是关联的表名
.schema(Schema.new_builder() # schema通常是表结构
.column('word', DataTypes.STRING()) # column创建字段名称和类型
.build())
.option('path', input_path) # 设定保存的路径
.format('csv') # 创建表的格式
.build())
tab = t_env.from_path('source') # 从默认的数据库和目录中读取表
else:
print("Executing word_count example with default input data set.")
print("Use --input to specify file input.")
tab = t_env.from_elements(map(lambda i: (i,), word_count_data),
DataTypes.ROW([DataTypes.FIELD('line', DataTypes.STRING())])) # lambda空格后面的是形参,冒号后面的是根据参数做出的运算,本例中是根据word_count_data,生成元祖,最后一个参数是每行的字段为line,类型是字符串
# define the sink 定义输出内容
if output_path is not None:
t_env.create_temporary_table(
'sink',
TableDescriptor.for_connector('filesystem')
.schema(Schema.new_builder()
.column('word', DataTypes.STRING())
.column('count', DataTypes.BIGINT())
.build())
.option('path', output_path)
.format(FormatDescriptor.for_format('canal-json')
.build())
.build())
else:
print("Printing result to stdout. Use --output to specify output path.")
t_env.create_temporary_table(
'sink',
TableDescriptor.for_connector('print')
.schema(Schema.new_builder()
.column('word', DataTypes.STRING())
.column('count', DataTypes.BIGINT())
.build())
.build())
@udtf(result_types=[DataTypes.STRING()]) # 定义输出返回的结果类型,user-defined table function创建用户定义的表函数
def split(line: Row):
for s in line[0].split():
yield Row(s)
# compute word count
tab.flat_map(split) \ # flat_map是根据用户定义的函数split进行转换
.alias('word') \ # 起别名
.group_by(col('word')) \ # 分组,让相同的挨在一起
.select(col('word'), lit(1).count) \ # 本句是对字段word执行操作,lit忘记什么意思了!!!
.execute_insert('sink') \ # 插入到sink表中
.wait() # 一般什么情况才需要使用.wait()?操作比较慢,耗时,需要你等的时候
if __name__ == '__main__':
logging.basicConfig(stream=sys.stdout, level=logging.INFO, format="%(message)s")
parser = argparse.ArgumentParser() # 这里使用了用户命令行
parser.add_argument( # 添加参数
'--input',
dest='input', # 被添加到 parse_args() 所返回对象上的属性名
required=False, # 此命令行选项是否可省略
help='Input file to process.')
parser.add_argument(
'--output',
dest='output',
required=False,
help='Output file to write results to.')
argv = sys.argv[1:]
known_args, _ = parser.parse_known_args(argv)
word_count(known_args.input, known_args.output)
2.1 python table api的基本流程:
1. 导入
from pyflink.table import EnvironmentSettings, TableEnvironment
2. 创建 TableEnvironment
env_settings = EnvironmentSettings.in_streaming_mode() # 设置组件在流模式下工作
table_env = TableEnvironment.create(env_settings)
# or create a batch TableEnvironment 或者设置组件在批模式下工作
env_settings = EnvironmentSettings.in_batch_mode()
table_env = TableEnvironment.create(env_settings)
或者,用户可以从现有的 StreamExecutionEnvironment 创建 StreamTableEnvironment,以与 DataStream API 进行互操作。
from pyflink.datastream import StreamExecutionEnvironment
from pyflink.table import StreamTableEnvironment
# create a streaming TableEnvironment from a StreamExecutionEnvironment
env = StreamExecutionEnvironment.get_execution_environment()
table_env = StreamTableEnvironment.create(env)
3. 创建 source 表
table_env.execute_sql("""
CREATE TABLE datagen (
id INT,
data STRING
) WITH (
'connector' = 'datagen',
'fields.id.kind' = 'sequence', # 设置id的类型为序列
'fields.id.start' = '1', # 设置id的从'1'到'10'
'fields.id.end' = '10'
)
""")
4. 创建 sink 表
table_env.execute_sql("""
CREATE TABLE print (
id INT,
data STRING
) WITH (
'connector' = 'print'
)
""")
5. 查询 source 表,同时执行计算
# 通过 Table API 创建一张表:
source_table = table_env.from_path("datagen")
# 或者通过 SQL 查询语句创建一张表:
source_table = table_env.sql_query("SELECT * FROM datagen")
result_table = source_table.select(source_table.id + 1, source_table.data)
6. 将计算结果写入给 sink 表
# 将 Table API 结果表数据写入 sink 表:
result_table.execute_insert("print").wait()
# 或者通过 SQL 查询语句来写入 sink 表:
table_env.execute_sql("INSERT INTO print SELECT * FROM datagen").wait()
2.2 创建表的通用方法:(包括创建source和sink表) from_xxx:以from开头的都是创建表,后面的单词是创建表的来源
2.2.1 从自己有限的几个列表数据中添加数据
# 不定义表名
table = table_env.from_elements([(1, 'Hi'), (2, 'Hello')])
# 定义表名,第二个参数为字段列表
table = table_env.from_elements([(1, 'Hi'), (2, 'Hello')], ['id', 'data'])
2.2.2 通过DDL(数据定义语言)语句
table_env.execute_sql("""
CREATE TABLE random_source ( # 表名 random_source
id BIGINT, # 创建字段id,且该字段的数据类型是大数值型
data TINYINT
) WITH (
'connector' = 'datagen', # 关联某张表
'fields.id.kind'='sequence',
'fields.id.start'='1',
'fields.id.end'='3',
'fields.data.kind'='sequence',
'fields.data.start'='4',
'fields.data.end'='6'
)
""")
table = table_env.from_path("random_source")
再展示一个例子:
my_source_ddl = """
create table source ( # 表名 source
word STRING # 创建字段word,且该字段的数据类型是STRING
) with (
'connector' = 'filesystem', # 关联某张表
)
""".format(input_path)
my_sink_ddl = """
create table sink (
word STRING,
`count` BIGINT
) with (
'connector' = 'filesystem',
'format' = 'canal-json',
'path' = '{}'
)
""".format(output_path)
t_env.execute_sql(my_source_ddl)
t_env.execute_sql(my_sink_ddl)
2.2.3 通过Catalog(元数据管理中心)创建
# 准备 catalog
# 将 Table API 表注册到 catalog 中
table = table_env.from_elements([(1, 'Hi'), (2, 'Hello')], ['id', 'data'])
table_env.create_temporary_view('source_table', table)
# 从 catalog 中获取 Table API 表
new_table = table_env.from_path('source_table')
2.3查询
2.3.1 Table Api 查询
# 创建表orders, 后续对orders表进行一些复杂的操作
orders = table_env.from_elements([('Jack', 'FRANCE', 10), ('Rose', 'ENGLAND', 30), ('Jack', 'FRANCE', 20)],
['name', 'country', 'revenue'])
# 计算所有来自法国客户的收入
revenue = orders \
.select(orders.name, orders.country, orders.revenue) \
.where(orders.country == 'FRANCE') \
.group_by(orders.name) \
.select(orders.name, orders.revenue.sum.alias('rev_sum'))
2.3.1.1Table API 支持
orders = table_env.from_elements([('Jack', 'FRANCE', 10), ('Rose', 'ENGLAND', 30), ('Jack', 'FRANCE', 20)],
['name', 'country', 'revenue'])
map_function = udf(lambda x: pd.concat([x.name, x.revenue * 10], axis=1),
result_type=DataTypes.ROW(
[DataTypes.FIELD("name", DataTypes.STRING()),
DataTypes.FIELD("revenue", DataTypes.BIGINT())]),
func_type="pandas")
orders.map(map_function).alias('name', 'revenue').to_pandas()
2.3.2 SQL查询
table_env.execute_sql("""
CREATE TABLE random_source (
id BIGINT,
data TINYINT
) WITH (
'connector' = 'datagen',
'fields.id.kind'='sequence',
'fields.id.start'='1',
'fields.id.end'='8',
'fields.data.kind'='sequence',
'fields.data.start'='4',
'fields.data.end'='11'
)
""")
table_env.execute_sql("""
CREATE TABLE print_sink (
id BIGINT,
data_sum TINYINT
) WITH (
'connector' = 'print'
)
""")
table_env.execute_sql("""
INSERT INTO print_sink
SELECT id, sum(data) as data_sum FROM
(SELECT id / 2 as id, data FROM random_source)
WHERE id > 1
GROUP BY id
""").wait()
2.3.3Table API 和 SQL 的混合使用
2.3.3.1 在 SQL 中使用 Table 对象
# 创建一张 sink 表来接收结果数据
table_env.execute_sql("""
CREATE TABLE table_sink (
id BIGINT,
data VARCHAR
) WITH (
'connector' = 'print'
)
""")
# 将 Table API 表转换成 SQL 中的视图
table = table_env.from_elements([(1, 'Hi'), (2, 'Hello')], ['id', 'data'])
table_env.create_temporary_view('table_api_table', table)
# 将 Table API 表的数据写入结果表
table_env.execute_sql("INSERT INTO table_sink SELECT * FROM table_api_table").wait()
2.3.3.2 在 Table API 中使用 SQL 表:
# 创建一张 SQL source 表
table_env.execute_sql("""
CREATE TABLE sql_source (
id BIGINT,
data TINYINT
) WITH (
'connector' = 'datagen',
'fields.id.kind'='sequence',
'fields.id.start'='1',
'fields.id.end'='4',
'fields.data.kind'='sequence',
'fields.data.start'='4',
'fields.data.end'='7'
)
""")
# 将 SQL 表转换成 Table API 表
table = table_env.from_path("sql_source")
# 或者通过 SQL 查询语句创建表
table = table_env.sql_query("SELECT * FROM sql_source")
2.4 删除表
drop_temporary_view(view_path) 删除指定路径下已注册的临时表。
drop_temporary_table(table_path) 删除指定路径下已注册的临时表。 你可以使用这个接口来删除临时 source 表和临时 sink 表。
2.5 输出结果
2.5.1打印到客户端(本地)
# 准备 source 表
source = table_env.from_elements([(1, "Hi", "Hello"), (2, "Hello", "Hello")], ["a", "b", "c"])
# 得到 TableResult
res = table_env.execute_sql("select a + 1, b, c from %s" % source)
# 遍历结果
with res.collect() as results: # 这里使用了TableResult.collect() 方法
for result in results:
print(result)
2.5.2将结果写入到一张 Sink 表中
table_env.execute_sql("""
CREATE TABLE sink_table (
id BIGINT,
data VARCHAR
) WITH (
'connector' = 'print'
)
""")
table = table_env.from_elements([(1, 'Hi'), (2, 'Hello')], ['id', 'data'])
table.execute_insert("sink_table").wait()
# 或者用sql的方式完成,二选一
table_env.create_temporary_view("table_source", table)
table_env.execute_sql("INSERT INTO sink_table SELECT * FROM table_source").wait()
2.5.3将结果写入到多张 Sink 表中
# 准备 source 表和 sink 表
table = table_env.from_elements([(1, 'Hi'), (2, 'Hello')], ['id', 'data'])
table_env.create_temporary_view("simple_source", table)
table_env.execute_sql("""
CREATE TABLE first_sink_table (
id BIGINT,
data VARCHAR
) WITH (
'connector' = 'print'
)
""")
table_env.execute_sql("""
CREATE TABLE second_sink_table (
id BIGINT,
data VARCHAR
) WITH (
'connector' = 'print'
)
""")
# 创建 statement set
statement_set = table_env.create_statement_set()
# 将 "table" 的数据写入 "first_sink_table"
statement_set.add_insert("first_sink_table", table)
# 通过一条 sql 插入语句将数据从 "simple_source" 写入到 "second_sink_table"
statement_set.add_insert_sql("INSERT INTO second_sink_table SELECT * FROM simple_source")
# 执行 statement set
statement_set.execute().wait()
2.6 Explain 表
Table API 提供了一种机制来查看逻辑查询计划和优化后的查询计划,通过 Table.explain() 或者 StatementSet.explain() 方法来完成。
Table.explain() 可以返回一个 Table 的执行计划。
StatementSet.explain() 则可以返回含有多个 sink 的作业的执行计划。包括以下信息:
关系查询的抽象语法树,即未经优化的逻辑查询计划,
优化后的逻辑查询计划,
物理执行计划。
2.6.1 Table.explain()
# 使用流模式 TableEnvironment
from pyflink.table import EnvironmentSettings, TableEnvironment
env_settings = EnvironmentSettings.in_streaming_mode()
table_env = TableEnvironment.create(env_settings)
table1 = table_env.from_elements([(1, 'Hi'), (2, 'Hello')], ['id', 'data'])
table2 = table_env.from_elements([(1, 'Hi'), (2, 'Hello')], ['id', 'data'])
table = table1 \
.where(table1.data.like('H%')) \
.union_all(table2)
print(table.explain())
2.6.2 StatementSet.explain()
# 使用流模式 TableEnvironment
from pyflink.table import EnvironmentSettings, TableEnvironment
env_settings = EnvironmentSettings.in_streaming_mode()
table_env = TableEnvironment.create(environment_settings=env_settings)
table1 = table_env.from_elements([(1, 'Hi'), (2, 'Hello')], ['id', 'data'])
table2 = table_env.from_elements([(1, 'Hi'), (2, 'Hello')], ['id', 'data'])
table_env.execute_sql("""
CREATE TABLE print_sink_table (
id BIGINT,
data VARCHAR
) WITH (
'connector' = 'print'
)
""")
table_env.execute_sql("""
CREATE TABLE black_hole_sink_table (
id BIGINT,
data VARCHAR
) WITH (
'connector' = 'blackhole'
)
""")
statement_set = table_env.create_statement_set()
statement_set.add_insert("print_sink_table", table1.where(table1.data.like('H%')))
statement_set.add_insert("black_hole_sink_table", table2)
print(statement_set.explain())
2.7 Table Api中的常识性问题:
2.7.1 Table Api中的常识性问题:
from_ 开头的是创建表
_temporary_view 表示临时表
drop_ 开头的是删除表
_catalog 对元数据的一些操作
list_ 开头的是获取。。。
2.7.2 Table Api中sql语句的常识性问题:
sql_query(query) 查
execute_sql(stmt) 执行作业
explain_sql(stmt, *extra_details) 返回指定语句的抽象语法树和执行计划。
代码举例:
orders = table_env.from_elements([('Jack', 'FRANCE', 10), ('Rose', 'ENGLAND', 30), ('Jack', 'FRANCE', 20)],
['name', 'country', 'revenue'])
map_function = udf(lambda x: pd.concat([x.name, x.revenue * 10], axis=1), # 这里是axis=1是水平拼接,变成[['Jack',100,'Jack',200],['Rose',300,NaN,NaN]]
result_type=DataTypes.ROW(
[DataTypes.FIELD("name", DataTypes.STRING()),
DataTypes.FIELD("revenue", DataTypes.BIGINT())]),
func_type="pandas")
代码的详细分析:

浙公网安备 33010602011771号