-
建设背景:构建跨业务数据治理平台,获取各烟囱业务基本数据,构建统一化的业务数据治理平台。
-
现有架构:
-
难点刨析:
难点 | 说明 |
---|---|
资源瓶颈 | 前置机;核心数据库都存有大量应用,不能基于该服务器开发数据治理服务,但是数据只有这2类服务器上有 。 |
治理逻辑 | 传统SQL一个语句块对应一个质检,增加重复人力,检验问题分散。 |
可插拔 | 新增服务器1台(8核32G+500 )用于数据治理服务,随着治理要求要强,能够快速的应对软硬建的插拔,快速集成 。 |
数据血缘 | 治理发现的问题来源关联查询 ,对应指标的错误来源左关联表还是右关联表? |
-
- 处理方案
- 构建计算引擎,将
算子从数据库层面分离
,计算引擎能够使用SQL,并且能结合开发语言(python,JAVA等)进行UDF的开发。(资源瓶颈) - 自生分布式,通过修改配置文件即可完成硬件的
快速扩容,负载均衡
。通过UDF函数实现软件的快速融合。(可插拔) - 基于
Calcite解析列级数据血缘
(数据血缘) - 使用侧边流的方式实现数据的
多路校验
。(治理逻辑)
DEMO介绍
我这边使用的是Flink1.16.1+pyflink1.16相关环境已经进行docker封装
链接:https://pan.baidu.com/s/1pvvPAwo4gY515laI1G-YFA?pwd=5oct
提取码:5oct
docker-compose up -d
docker-compose exec flink bash
test.py---->ff/init-data目录下
*.csv--->ff/init-data/examples目录下
方式1:python
/*test.py*/
!/usr/bin/env python
from pyflink.table import EnvironmentSettings, TableEnvironment,DataTypes
from pyflink.table.udf import ScalarFunction, udf
from pyflink.table.expressions import call, col
# 1. 创建 TableEnvironment
env_settings = EnvironmentSettings.in_streaming_mode()
table_env = TableEnvironment.create(env_settings)
# 创建一个udf函数用于校验XXMDJ*MXXMSL是否等于MXXMJE
@udf(result_type=DataTypes.STRING())
def rule_1(MXXMDJ, MXXMSL,MXXMJE):
if MXXMDJ*MXXMSL!=MXXMJE:
return "校验:XXMDJ*MXXMSL!=MXXMJE"
else:
return None
#def rule_1(MXXMDJ, MXXMSL, MXXMJE):
# assert MXXMDJ * MXXMSL == MXXMJE, "校验:XXMDJ*MXXMSL!=MXXMJE"
# 2. 创建 source 表
table_env.execute_sql("""
CREATE TABLE TB_HIS_MZ_FEE_DETAIL(
KH VARCHAR,
KLX VARCHAR,
MXXMMC VARCHAR,
MXXMDW VARCHAR,
MXXMDJ float,
MXXMSL float,
MXXMJE float
) WITH (
'connector' = 'filesystem',
'path' = '/usr/local/app/examples/20230305TB_HIS_MZ_FEE_DETAIL.csv',
'format' = 'csv'
);
""")
table_env.execute_sql("""
CREATE TABLE TB_DIC_MATERIALS(
YBMLBM VARCHAR,
XMMC VARCHAR
) WITH (
'connector' = 'filesystem',
'path' = '/usr/local/app/examples/20230306tb_dic_materials.csv',
'format' = 'csv'
);
""")
# 3. 创建 sink 表
table_env.execute_sql("""
CREATE TABLE print (
KH VARCHAR,
KLX VARCHAR,
MXXMMC VARCHAR,
MXXMDW VARCHAR,
MXXMDJ float,
MXXMSL float,
MXXMJE float,
ERR_MSg VARCHAR
) WITH (
'connector' = 'print'
)
""")
# 4. 查询 source 表,同时执行计算
# 通过 Table API 创建一张表:
# source_table = table_env.from_path("TB_HIS_MZ_FEE_DETAIL")
# 或者通过 SQL 查询语句创建一张表:
source_table = table_env.sql_query("SELECT T1.* FROM TB_HIS_MZ_FEE_DETAIL T1 join TB_DIC_MATERIALS T2 on T1.MXXMMC=T2.XMMC")
result_table = source_table.select( source_table.KH,
source_table.KLX,
source_table.MXXMMC,
source_table.MXXMDW,
source_table.MXXMDJ,
source_table.MXXMSL,
source_table.MXXMJE,
rule_1(source_table.MXXMDJ,source_table.MXXMSL,source_table.MXXMJE)
)
# 5. 将计算结果写入给 sink 表
# 将 Table API 结果表数据写入 sink 表:
result_table.execute_insert("print").wait()
# 或者通过 SQL 查询语句来写入 sink 表:
# table_env.execute_sql("INSERT INTO print SELECT * FROM TB_HIS_MZ_FEE_DETAIL").wait()
数据血缘
PyFlink 中可以通过 Table 类的 explain() 方法来展示 SQL 执行计划,并且在执行计划中包含了数据流经过的算子、输入输出源等信息,可以通过解析执行计划来获取数据血缘信息。
具体步骤如下:
1.使用 TableEnvironment 类的 explain() 方法来获取 SQL 执行计划:
from pyflink.table import EnvironmentSettings, BatchTableEnvironment
# 创建 TableEnvironment
t_env = BatchTableEnvironment.create(
environment_settings=EnvironmentSettings.new_instance().in_batch_mode().use_blink_planner().build())
# 解析 SQL 并获取执行计划
sql = "SELECT T1.MXXMMC,T1.KH,T2.YBMLBM FROM TB_HIS_MZ_FEE_DETAIL T1 join TB_DIC_MATERIALS T2 on T1.MXXMMC=T2.XMMC"
exec_plan = t_env.explain_sql(sql)
2.解析执行计划中的算子信息,将算子信息存储为有向图,使用深度优先遍历(DFS)算法获取数据血缘信息。
# 定义一个有向图类
class Graph:
def __init__(self, vertices):
self.V = vertices
self.adj = [[] for i in range(self.V)]
def addEdge(self, u, v):
self.adj[u].append(v)
# DFS 递归函数
def DFS(self, v, visited, table_list):
visited[v] = True
table_list.append(v)
for i in self.adj[v]:
if not visited[i]:
self.DFS(i, visited, table_list)
# 获取有向图的数据血缘信息
def get_data_lineage(self, start_node):
visited = [False] * self.V
table_list = []
# 对起点进行 DFS 遍历,获取所有节点
self.DFS(start_node, visited, table_list)
# 返回所有节点列表
return table_list
# 定义算子信息的类
class OpNode:
def __init__(self, op_id, op_type, input_list, output_list):
self.op_id = op_id
self.op_type = op_type
self.input_list = input_list
self.output_list = output_list
# 解析 SQL 执行计划并获取算子信息
plan_str = exec_plan.get_execution_plan()
op_list = []
for line in plan_str.split("\n"):
if line.startswith("#") or line == "":
continue
op_info = line.split(" ")
op_id, op_type, input_list, output_list = op_info[0], op_info[1], op_info[3].split(","), op_info[4].split(",")
op_node = OpNode(op_id, op_type, input_list, output_list)
op_list.append(op_node)
# 构建有向图并获取数据血缘
graph = Graph(len(op_list))
for op in op_list:
output_node_id = int(op.op_id.split("_")[0])
for input_id in op.input_list:
input_node_id = int(input_id.split("_")[0])
graph.addEdge(input_node_id, output_node_id)
data_lineage = graph.get_data_lineage(start_node=0)
通过上述代码,就可以获取到 SQL 执行计划中所有数据流经过的算子节点,并且整理成有向图的形式,使用深度优先遍历算法获取数据血缘信息,返回的是一个列表,列表中的节点按照数据的流动顺序排列。
服务端执行
[root@f1eb2c863bd8 app]# python test.py
Flink样例
方式2:Flink-sql
./bin/sql-client.sh
CREATE TABLE TB_HIS_MZ_FEE_DETAIL(
KH VARCHAR,
KLX VARCHAR,
MXXMMC VARCHAR,
MXXMDW VARCHAR,
MXXMDJ float,
MXXMSL float,
MXXMJE float
) WITH (
'connector' = 'filesystem',
'path' = '/usr/local/app/examples/20230305TB_HIS_MZ_FEE_DETAIL.csv',
'format' = 'csv'
);
CREATE TABLE TB_DIC_MATERIALS(
YBMLBM VARCHAR,
XMMC VARCHAR
) WITH (
'connector' = 'filesystem',
'path' = '/usr/local/app/examples/20230306tb_dic_materials.csv',
'format' = 'csv'
);
SELECT T1.* FROM TB_HIS_MZ_FEE_DETAIL T1 join TB_DIC_MATERIALS T2 on T1.MXXMMC=T2.XMMC
FLink-SQL
查看数据血缘
EXPLAIN PLAN FOR
SELECT T1.MXXMMC,T1.KH,T2.YBMLBM FROM TB_HIS_MZ_FEE_DETAIL T1 join TB_DIC_MATERIALS T2 on T1.MXXMMC=T2.XMMC;
扩展内容
FLink-SQL中使用Java,Python,Scala UDF,详见:
https://github.com/appleyuchi/Flink_SQL_Client_UDF
FLink链接数据库,详见:https://nightlies.apache.org/flink/flink-docs-release-1.16/zh/docs/connectors/table/jdbc/(也可以看下我之前得优快云)
注意:下载对应得Jar包
如果使用第三方 JAR,则可以在 Python 表 API 中指定 JAR
环境:
PyFlink版本是1.16.0,所以需要下载 :flink-connector-jdbc-1.16.0.jar
flink-connector-jdbc-1.16.0.jar下载地址
Mysql版本是8.0.30,所以需要下载:mysql-connector-java-8.0.30.jar
mysql-connector-java-8.0.30.jar下载地址
flinkcdc链接数据库的文档
flink-cdc-connectors
https://github.com/ververica/flink-cdc-connectors
使用JDBC
-- 在 Flink SQL 中注册一张 MySQL 表 'users'
CREATE TABLE MyUserTable (
id BIGINT,
name STRING,
age INT,
status BOOLEAN,
PRIMARY KEY (id) NOT ENFORCED
) WITH (
'connector' = 'jdbc',
'url' = 'jdbc:mysql://localhost:3306/mydatabase',
'table-name' = 'users'
);
-- 从另一张表 "T" 将数据写入到 JDBC 表中
INSERT INTO MyUserTable
SELECT id, name, age, status FROM T;
-- 查看 JDBC 表中的数据
SELECT id, name, age, status FROM MyUserTable;
-- JDBC 表在时态表关联中作为维表
SELECT * FROM myTopic
LEFT JOIN MyUserTable FOR SYSTEM_TIME AS OF myTopic.proctime
ON myTopic.key = MyUserTable.id;
使用flink-cdc-connectors,详细见:https://gitee.com/mario1oreo/flink-cdc-connectors
-- creates a mysql cdc table source
CREATE TABLE mysql_binlog (
id INT NOT NULL,
name STRING,
description STRING,
weight DECIMAL(10,3)
) WITH (
'connector' = 'mysql-cdc',
'hostname' = 'localhost',
'port' = '3306',
'username' = 'flinkuser',
'password' = 'flinkpw',
'database-name' = 'inventory',
'table-name' = 'products'
);
-- read snapshot and binlog data from mysql, and do some transformation, and show on the client
SELECT id, UPPER(name), description, weight FROM mysql_binlog;
FLink-SQL中血缘关系得扩展:FlinkSQL字段血缘解决方案及源码