flink写入mysql的两种方式
方式一 通过JDBCOutputFormat
在flink中没有现成的用来写入MySQL的sink,但是flink提供了一个类,JDBCOutputFormat,通过这个类,如果你提供了jdbc的driver,则可以当做sink使用。
JDBCOutputFormat其实是flink的batch api,但也可以用来作为stream的api使用,社区也推荐通过这种方式来进行。
JDBCOutputFormat用起来很简单,只需要一个prepared statement,driver和database connection,就可以开始使用了。
JDBCOutputFormat jdbcOutput = JDBCOutputFormat.buildJDBCOutputFormat()
.setDrivername("com.mysql.jdbc.Driver")
.setDBUrl("jdbc:mysql://localhost:1234/test?user=xxx&password=xxx")
.setQuery(query)
.finish();
如下的sql语句可以作为prepared statement:
String query = "INSERT INTO public.cases (caseid, tracehash) VALUES (?, ?)";
对应的表的结构:
CREATE TABLE cases
(
caseid VARCHAR(255),
tracehash VARCHAR(255)
);
但有一点要明确,JDBCOutputFormat只能处理Row,而Row是对prepared statement的参数的一个包装类。这意味着我们需要将流中的case转换为row,通过map就能做的。
DataStream<Case> cases = ...
DataStream<Row> rows = cases.map((MapFunction<Case, Row>) aCase -> {
Row row = new Row(2); // our prepared statement has 2 parameters
row.setField(0, aCase.getId()); //first parameter is case ID
row.setField(1, aCase.getTraceHash()); //second paramater is tracehash
return row;
});
这样,我们就能添加sink了:
rows.writeUsingOutputFormat(jdbcOutput);
这样,你就可以将数据写入mysql了。
但是在你在流上附加了窗口之后,可能会得到下面的报错:
"Unknown column type for column %s. Best effort approach to set its value: %s."
因为窗口处理的类型,没有明确的类型定义,如下修改之前的定义,显式的指定类型:
JDBCOutputFormat jdbcOutput = JDBCOutputFormat.buildJDBCOutputFormat()
.setDrivername("com.mysql.jdbc.Driver")
.setDBUrl("jdbc:mysql://localhost:1234/test?user=xxx&password=xxx")
.setQuery(query)
.setSqlTypes(new int[] { Types.VARCHAR, Types.VARCHAR }) //set the types
.finish();
JDBCOutputFormat has a batchInterval, which you can specify on the JDBCOutputFormatBuilder. If, however, I specify a batch interval of 5000, I would potentially never write anything to the database, or wait a very long time until anything was written.
JDBCOutputFormat 还有一个很有用的参数,batchInterval,见名知意,就是多少数据提交一次,尽量高效率的向数据库提交数据。当然还有比如timeout等其他参数,可以探索。
方式二 通过自定义sink提交
我们通过继承RichSinkFunction<IN>来实现自定义sink:
public class RichCaseSink extends RichSinkFunction<Case> {
private static final String UPSERT_CASE = "INSERT INTO public.cases (caseid, tracehash) "
+ "VALUES (?, ?) "
+ "ON CONFLICT (caseid) DO UPDATE SET "
+ " tracehash=?";
private PreparedStatement statement;
@Override
public void invoke(Case aCase) throws Exception {
statement.setString(1, aCase.getId());
statement.setString(2, aCase.getTraceHash());
statement.setString(3, aCase.getTraceHash());
statement.addBatch();
statement.executeBatch();
}
@Override
public void open(Configuration parameters) throws Exception {
Class.forName("com.mysql.jdbc.Driver");
Connection connection =
DriverManager.getConnection("jdbc:mysql://localhost:5432/casedb?user=signavio&password=signavio");
statement = connection.prepareStatement(UPSERT_CASE);
}
}
这样,就可以在流上添加sink 了:
DataStream<Case> cases = ...
cases.addSink(new RichCaseSink());
当然,上面的实现很简略,没有给出批量提交或者超时提交,这个都可以很容易的添加,比如close()中关闭连接。
但是上面的实现中,最大的问题还是没有跟flink的状态管理相结合,这个才是重头戏。
方式二 加强版的自定义sink
在checkpoint的时候保存数据,继承接口CheckpointedFunction :
@Override
public void snapshotState(FunctionSnapshotContext context) throws Exception {
long checkpointId = context.getCheckpointId();
List<Case> cases = pendingCasesPerCheckpoint.get(checkpointId);
if(cases == null){
cases = new ArrayList<>();
pendingCasesPerCheckpoint.put(checkpointId, cases);
}
cases.addAll(pendingCases);
pendingCases.clear();
}
在消息到达的时候不插入数据,只是留存数据:
@Override
public void invoke(Case aCase) throws Exception {
pendingCases.add(aCase);
}
这样,通过继承CheckpointListener,我们就能在某个checkpoint完成的时候插入数据:
@Override
public void notifyCheckpointComplete(long checkpointId) throws Exception { Iterator<Map.Entry<Long, List<Case>>> pendingCheckpointsIt =
pendingCasesPerCheckpoint.entrySet().iterator(); while (pendingCheckpointsIt.hasNext()) { Map.Entry<Long, List<Case>> entry = pendingCheckpointsIt.next();
Long pastCheckpointId = entry.getKey();
List<Case> pendingCases = entry.getValue(); if (pastCheckpointId <= checkpointId) { for (Case pendingCase : pendingCases) {
statement.setString(1, pendingCase.getId());
statement.setString(2, pendingCase.getTraceHash());
statement.setString(3, pendingCase.getTraceHash());
statement.addBatch();
}
pendingCheckpointsIt.remove();
}
}
statement.executeBatch(); }
前提,是需要设置checkpoint,比如:
ExecutionEnvironment env = ...
env.enableCheckpointing(10000L);
这样,每隔10s,当一个checkpoint做成功,就会插入一次数据。
当然,上面的代码验证可用,但不建议在生产环境使用,生产环境需要考虑更多的问题。
flink写入mysql的两种方式的更多相关文章
- flask 操作mysql的两种方式-sqlalchemy操作
flask 操作mysql的两种方式-sqlalchemy操作 二.ORM sqlalchemy操作 #coding=utf-8 # model.py from app import db class ...
- flask 操作mysql的两种方式-sql操作
flask 操作mysql的两种方式-sql操作 一.用常规的sql语句操作 # coding=utf-8 # model.py import MySQLdb def get_conn(): conn ...
- Navicate 连接阿里云MySQL(两种方式及原理讲解)
Navicate 连接阿里云(两种方式及原理讲解) 一.直连方式(通过3306端口) 1.概述 2. 环境准备 3.操作及讲解 二.使用SSH通道 1.概述 2.环境准备 3.操作及讲解 如果对你有帮 ...
- C++连接mysql的两种方式(ADO连接和mysql api连接)
一.ADO连接mysql 1.安装mysql-5.5.20-win32.msi和mysql-connector-odbc-5.3.4-win32.msi(一般两个安装程序要匹配,否则可能连接不上) ...
- MySql入门(2-1)windows下安装mysql的两种方式
一.下载mysql 1.下载解压MySQL 登录oracle主页,需要用户名和口令: lshengqi@netease.com/1wsx**** 下载路径:: https://dev.mysql.co ...
- php7 连接 mysql 的两种方式
PHP 5 的使用者可以使用 MySQL extension,mysqli 和 PDO_MYSQL .php 7移除了mysql extension,只剩下后面两种选择.这份文档解释了每个API 的术 ...
- spark2.2jdbc写入mysql 的两种方法(append,Overriedwrite)-不用Mysql建表
import org.apache.spark.{SparkConf, SparkContext} import org.apache.spark.sql.{SQLContext, SaveMode} ...
- Spark:DataFrame批量导入Hbase的两种方式(HFile、Hive)
Spark处理后的结果数据resultDataFrame可以有多种存储介质,比较常见是存储为文件.关系型数据库,非关系行数据库. 各种方式有各自的特点,对于海量数据而言,如果想要达到实时查询的目的,使 ...
- flink01--------1.flink简介 2.flink安装 3. flink提交任务的2种方式 4. 4flink的快速入门 5.source 6 常用算子(keyBy,max/min,maxBy/minBy,connect,union,split+select)
1. flink简介 1.1 什么是flink Apache Flink是一个分布式大数据处理引擎,可以对有限数据流(如离线数据)和无限流数据及逆行有状态计算(不太懂).可以部署在各种集群环境,对各种 ...
随机推荐
- 2017-2018-1 20155318 《信息安全系统设计基础》第2周课堂实践、makefile、以及myod
2017-2018-1 20155318 <信息安全系统设计基础>第2周课堂实践.makefile.以及myod 测试3-gdb测试 用gcc -g编译vi输入的代码 在main函数中设置 ...
- 2017-2018-1 20155329《信息安全技术》实验二——Windows口令破解
2017-2018-1 20155329<信息安全技术>实验二--Windows口令破解 实验原理 口令破解方法 字典破解: 指通过破解者对管理员的了解,猜测其可能使用某些信息作为密码,利 ...
- Python中的对象引用、浅拷贝与深拷贝
最近项目中遇到一个Python浅拷贝机制引起的bug,由于对于Python中对象引用.赋值.浅拷贝/深拷贝机制没有足够的认识,导致调试了很久才发现问题,这里简单记录一下相关概念. 在Python的设计 ...
- centos安装smokeping
本文摘自网友博客,并亲自验证 博客地址:https://blog.csdn.net/erica_yue/article/details/78455101 1.安装依赖包: yum install -y ...
- DSP5509项目之用FFT识别钢琴音调(3)之ccs v6环境变量的配置
1. 添加头文件路径,没搞明白"${C5500_CSL_CG_ROOT}/include"里面的C5500_CSL_CG_ROOT是在哪里定义的? 2. 打开CCS的窗口,优选项配 ...
- selenium自动化之切换iframe
许多人在执行脚本的时候会发现,明明自己的元素路径没写错啊!怎么还是报元素未找到的异常呢?是的,没错,你可能是遇上iframe啦!下面将介绍关于iframe的相关操作. 例子:以163邮箱登录页面为例 ...
- 【转】glumer Appium + Python环境搭建(移动端自动化)
最近整理了一下自动化的东西,好久没搭建环境又踩了不少坑,appium的环境搭建比较繁琐,好多同行估计都在环境上被卡死了.分享一下~~ 一.安装JDK,配置JDK环境 百度搜索下载就行,这里分享一 ...
- Laya 自适应 不拉伸处理
Laya.init(640, Laya.Browser.width / 640 * 1028, WebGL); Laya.stage.scaleMode = "fixedwidth" ...
- Git + Gerrit 操作备忘
Git review 作用 可以用来提交代码审核到Gerrit 安装 使用pip 安装 git-review 插件,执行 sudo -H pip install git-review 使用示例 可以参 ...
- JAVA学习笔记--迭代器
迭代器(Iterator)是一种设计模式.它是一个对象,它的工作是遍历并选择序列中的对象,而客户端程序员不必知道或关心该序列底层的结构.创建迭代器的代价小,因而迭代器通常被称为轻量级对象. 一.Ite ...