方式一 通过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的两种方式的更多相关文章

  1. flask 操作mysql的两种方式-sqlalchemy操作

    flask 操作mysql的两种方式-sqlalchemy操作 二.ORM sqlalchemy操作 #coding=utf-8 # model.py from app import db class ...

  2. flask 操作mysql的两种方式-sql操作

    flask 操作mysql的两种方式-sql操作 一.用常规的sql语句操作 # coding=utf-8 # model.py import MySQLdb def get_conn(): conn ...

  3. Navicate 连接阿里云MySQL(两种方式及原理讲解)

    Navicate 连接阿里云(两种方式及原理讲解) 一.直连方式(通过3306端口) 1.概述 2. 环境准备 3.操作及讲解 二.使用SSH通道 1.概述 2.环境准备 3.操作及讲解 如果对你有帮 ...

  4. C++连接mysql的两种方式(ADO连接和mysql api连接)

    一.ADO连接mysql 1.安装mysql-5.5.20-win32.msi和mysql-connector-odbc-5.3.4-win32.msi(一般两个安装程序要匹配,否则可能连接不上)  ...

  5. MySql入门(2-1)windows下安装mysql的两种方式

    一.下载mysql 1.下载解压MySQL 登录oracle主页,需要用户名和口令: lshengqi@netease.com/1wsx**** 下载路径:: https://dev.mysql.co ...

  6. php7 连接 mysql 的两种方式

    PHP 5 的使用者可以使用 MySQL extension,mysqli 和 PDO_MYSQL .php 7移除了mysql extension,只剩下后面两种选择.这份文档解释了每个API 的术 ...

  7. spark2.2jdbc写入mysql 的两种方法(append,Overriedwrite)-不用Mysql建表

    import org.apache.spark.{SparkConf, SparkContext} import org.apache.spark.sql.{SQLContext, SaveMode} ...

  8. Spark:DataFrame批量导入Hbase的两种方式(HFile、Hive)

    Spark处理后的结果数据resultDataFrame可以有多种存储介质,比较常见是存储为文件.关系型数据库,非关系行数据库. 各种方式有各自的特点,对于海量数据而言,如果想要达到实时查询的目的,使 ...

  9. 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是一个分布式大数据处理引擎,可以对有限数据流(如离线数据)和无限流数据及逆行有状态计算(不太懂).可以部署在各种集群环境,对各种 ...

随机推荐

  1. 有关javaEE及WebGIS的一些地址

    jdk:https://www.oracle.com/index.html eclipse:https://www.eclipse.org spring:http://repo.springsourc ...

  2. elasticsearch 安装问题

    Elasticsearch5.0 安装问题集锦 elasticsearch 5.0 安装过程中遇到了一些问题,通过查找资料几乎都解决掉了,这里简单记录一下 ,供以后查阅参考,也希望可以帮助遇到同样问题 ...

  3. 无法获得锁 /var/lib/apt/lists/lock - open (11 资源临时不可用)

    具体如下: 1.ps-aux 查出apt-get进程的PID,通常是一个四位数字. 2.用sudo kill PID代码 杀死进程 3.用sudo apt-get update,sudo apt-ge ...

  4. php-laravel中间件使用

    中间件使用 1.项目目录下cmd中php artisan make:middleware adminLogin,创建中间件 2.注册中间件(\Http\kernel.php) protected $r ...

  5. DataTable保存与读取 stream

    1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 3 ...

  6. 强化学习读书笔记 - 13 - 策略梯度方法(Policy Gradient Methods)

    强化学习读书笔记 - 13 - 策略梯度方法(Policy Gradient Methods) 学习笔记: Reinforcement Learning: An Introduction, Richa ...

  7. tomcat 设定自定义图片路径

    1.问题 平常图片路径都是在项目目录下存放,都是ip地址+端口号+项目名+图片路径,因为项目需要要把图片从tomcat中分离出来,并且设置可以通过自定义地址访问自定义图片路径. 2.解决 在 tomc ...

  8. selenium自动化之定位多个元素

    前面我们讲的都是如何定位单个元素,下面讲下怎么去定位多个元素,并且输出文本. 以百度为例:获取标红的这一组元素的文本 这里我用到的是xpath来定位的://div[@id="u1" ...

  9. Java基础知识:Java实现Map集合二级联动2

    2. 定义获取省份的方法,创建一个Map集合,将上一步得到的映射集合赋值给它,使用Map集合的keySet()方法获取该集合中的所有键对象组成的Set 集合,即为省分集合,创建一个Object型一维数 ...

  10. 如何使用phpredis连接Redis的方法

    本文跟大家介绍使用同一VPC内弹性云服务器ECS上的phpredis连接Redis的方法. 更多的客户端的使用方法,请参考https://redis.io/clients 前提条件 已成功申请Redi ...