方式一 通过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. ASP.NET <% %>的各种形式用法

    1.<% %>用来绑定后台代码 < % ;i<;i++) { Reaponse.Write(i.ToString()); } %> 2.<%# %> 是在绑定 ...

  2. 20155236 《Java程序设计》实验五(网络编程与安全)实验报告

    20155236 <Java程序设计>实验五(网络编程与安全)实验报告 一.实验内容及步骤 任务一: 编写MyBC.java实现中缀表达式转后缀表达式的功能 编写MyDC.java实现从上 ...

  3. 20155313 2016-2017-2 《Java程序设计》第四周学习总结

    20155313 2016-2017-2 <Java程序设计>第四周学习总结 教材学习内容总结 6 继承与多态 面对对象中,子类继承父类,避免重复的行为定义,不过并非为了避免重复定义行为就 ...

  4. switchsharp

    https://www.switchysharp.com/file/switchysharp-v1.10.4.zip

  5. 关于解决idea 输入法不跟随问题

    网上查了很多方法  自己试验了一种方式 jdk版本采用的是 java version "1.8.0_191"Java(TM) SE Runtime Environment (bui ...

  6. JS基础,课堂作业,相亲问答

    相亲问答 <script> var a = prompt("你有房子么?"); var b = prompt("你有钱么?"); var c = p ...

  7. C#特性的简单介绍

    特性应该我们大多接触过,比喻经常使用的[Obsolete],[Serializable]等下面我就主要介绍一个特性的一些用法 摘自MSDN定义:用以将元数据或声明信息与代码(程序集.类型.方法.属性等 ...

  8. [深度学习] 使用Darknet YOLO 模型破解中文验证码点击识别

    内容 背景 准备 实践 结果 总结 引用 背景 老规矩,先上代码吧 代码所在: https://github.com/BruceDone/darknet_demo 最近在做深度学习相关的项目的时候,了 ...

  9. 一、EnterpriseFrameWork框架总体介绍

    EnterpriseFrameWork框架是自己在工作之余的得意之作,经过了几年时间的不断重构,现在终于有了现在的样子:刚开始只是为了方便开发WEB系统,随着项目越做越多,新的功能也就不断补充进去,补 ...

  10. 240. 搜索二维矩阵 II

    二维数组搜索 编写一个高效的算法来搜索 m x n 矩阵 matrix 中的一个目标值 target.该矩阵具有以下特性: 每行的元素从左到右升序排列. 每列的元素从上到下升序排列. 示例: 现有矩阵 ...