本文已参与「新人创作礼」活动,一起开启掘金创作之路。
尽管在大数据处理中直接与 MySQL 交互的场景不多,但最终处理的计算结果是要给外部应用消费使用的,而外部应用读取的数据存储往往就是 MySQL。所以我们也需要知道如何将数据输出到 MySQL 这样的传统数据库。
写入数据的 MySQL 的测试步骤如下。 (1)添加依赖
<!--Flink 对接 MySQL 依赖-->
<dependency>
<groupId>org.apache.flink</groupId>
<artifactId>flink-connector-jdbc_${scala.version}</artifactId>
<version>1.11.0</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
(2)启动 MySQL,在 database 库下建表 clicks
CREATE TABLE `writetodb` (
`sensorname` varchar(23) DEFAULT NULL,
`timestamp` bigint(20) DEFAULT NULL,
`wendu` double DEFAULT NULL
)
(3)编写输出到 MySQL 的示例代码
object TestToMySQLEasy {
def main(args: Array[String]): Unit = {
val env = StreamExecutionEnvironment.getExecutionEnvironment
// env.enableCheckpointing(3000L) //毫秒,写入 Mysql 必须开启检查点
env.setParallelism(1)
val sql = "INSERT INTO writetodb (sensorname, timestamp, wendu) VALUE (?,?,?)"
env.readTextFile("/home/rjxy/zlp/Code/CodePro/GuoSai/Task01/src/main/resources/test.txt")
.map(elem => {
val data = elem.split(",")
(data(0), data(1).toLong, data(2).toDouble)
})
.addSink(JdbcSink.sink(
sql, //
new JdbcStatementBuilder[(String,Long,Double)] {
override def accept(ps: PreparedStatement, u: (String, Long, Double)): Unit = {
ps.setString(1, u._1);
ps.setLong(2, u._2);
ps.setDouble(3, u._3);
}
},
new JdbcConnectionOptions.JdbcConnectionOptionsBuilder()
.withDriverName("com.mysql.jdbc.Driver")
.withUrl("jdbc:mysql://192.168.23.34:3306/zlp")
.withUsername("root")
.withPassword("123456")
.build()
));
env.execute()
}
}
(4)运行代码,用客户端连接 MySQL,查看是否成功写入数据。
方式二 通过JDBCOutputFormat
在flink中没有现成的用来写入MySQL的sink,但是flink提供了一个类,JDBCOutputFormat,通过这个类,如果你提供了jdbc的driver,则可以当做sink使用。
JDBCOutputFormat其实是flink的batch api,但也可以用来作为stream的api使用,社区也推荐通过这种方式来进行。
JDBCOutputFormat用起来很简单,只需要一个prepared statement,driver和database connection,就可以开始使用了。
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;
});
rows.writeUsingOutputFormat(jdbcOutput);
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();