Flink——实战之MySQL Sink

4 篇文章 0 订阅

准备工作

Flink自身并没有提供连接MySQL的连接器,需要手动引入:

<!-- mysql connector -->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.44</version>
</dependency>

代码实现

核心代码在于:

streaming.addSink(new MyJdbcSink).setParallelism(1)

这里你需要1个参数:

  • MyJdbcSink:自定义的JdbcSink。需要注意的是,实现该方法时要继承RichSinkFunction函数,可利用open()函数初始化JDBC连接、SQL预编译器等运行时环境,也可以利用close()函数做清理工作。若选择继承SinkFunction,会在每次写入一条数据时都会创建一个JDBC连接。源码注解中给出的解释:

Writes the given value to the sink. This function is called for every record.

完整代码如下:

package org.ourhome.streamapi

import java.sql.{Connection, DriverManager, PreparedStatement}

import org.apache.flink.streaming.api.scala._
import org.apache.flink.api.java.utils.ParameterTool
import org.apache.flink.configuration.Configuration
import org.apache.flink.runtime.state.filesystem.FsStateBackend
import org.apache.flink.streaming.api.CheckpointingMode
import org.apache.flink.streaming.api.functions.sink.{RichSinkFunction, SinkFunction}
import org.apache.flink.streaming.api.scala.{DataStream, StreamExecutionEnvironment}
/**
 * @Author Do
 * @Date 2020/4/18 22:23
 */
object MysqlSinkTest {
  private val URL: String = "jdbc:mysql://ip:port/database?characterEncoding=utf8&useSSL=false"
  private val USER: String = "root"
  private val PASSWORD: String = "123456"

  def main(args: Array[String]): Unit = {
    val params: ParameterTool = ParameterTool.fromArgs(args)
    val runType:String = params.get("runtype")
    println("runType: " + runType)

    val env: StreamExecutionEnvironment = StreamExecutionEnvironment.getExecutionEnvironment
    env.getCheckpointConfig.setCheckpointingMode(CheckpointingMode.EXACTLY_ONCE)
    env.enableCheckpointing(5000) // checkpoint every 5000
    env.setStateBackend(new FsStateBackend("file:///D:/Work/Code/flinkdev/src/main/resources/checkpoint"))

    val inputStream: DataStream[String] = env.socketTextStream("host", 9000)
    // 处理inputStream,包装成Person类
    val streaming: DataStream[Person] = inputStream.map(line => {
      println(line)
      val strings: Array[String] = line.split(",")
      Person(strings(0).trim, strings(1).trim.toInt, strings(2).trim, strings(3).trim.toFloat)
    })

    streaming.addSink(new MyJdbcSink).setParallelism(1)

    env.execute("Mysql Sink")

  }

  case class Person (name: String, age: Int, gender: String, height: Float)

  /**
   * 若选择SinkFunction "Writes the given value to the sink. This function is called for every record."
   * RichFunction有生命周期和初始化配置功能,在初始化时创建连接,后面直接调用连接
   */
  class MyJdbcSink extends RichSinkFunction[Person] {
    // 定义一些变量:JDBC连接、sql预编译器()
    var conn: Connection = _
    var updateStmt: PreparedStatement = _
    var insertStmt: PreparedStatement = _

    // open函数用于初始化富函数运行时的上下文等环境,如JDBC连接
    override def open(parameters: Configuration): Unit = {
      println("----------------------------open函数初始化JDBC连接及预编译sql-------------------------")
      super.open(parameters)
      conn = DriverManager.getConnection(URL, USER, PASSWORD)
      insertStmt = conn.prepareStatement("INSERT INTO person_message (name, age, gender, height) VALUES (?, ?, ?, ?)")
      updateStmt = conn.prepareStatement("UPDATE person_message set age = ?, gender = ?, height = ? where name = ?")
    }
    // 调JDBC连接,执行SQL
    override def invoke(value: Person, context: SinkFunction.Context[_]): Unit = {
      println("-------------------------执行sql---------------------------")
      // 执行更新语句
      updateStmt.setInt(1, value.age)
      updateStmt.setString(2, value.gender)
      updateStmt.setDouble(3, value.height)
      updateStmt.setString(4, value.name)
      updateStmt.execute()
      // 如果update没有查到数据,那么执行insert语句
      if (updateStmt.getUpdateCount == 0) {
        insertStmt.setString(1, value.name)
        insertStmt.setInt(2, value.age)
        insertStmt.setString(3, value.gender)
        insertStmt.setDouble(4, value.height)
        insertStmt.execute()
      }
    }
    // 关闭时做清理工作
    override def close(): Unit = {
      println("-----------------------关闭连接,并释放资源-----------------------")
      updateStmt.close()
      insertStmt.close()
      conn.close()
    }
  }

}

Socket发送数据

nc -lk 9999
小明,20,man,180.2
小红,22,woman,178.4
小黑,18,man,192.9
小兰,19,woman,188.0
小爱,30,woman,177.3

输出

--------------open函数初始化JDBC连接及预编译sql---------------

小明,20,man,180.2
-------------------------执行sql---------------------------
小红,22,woman,178.4
-------------------------执行sql---------------------------
小黑,18,man,192.9
-------------------------执行sql---------------------------
小兰,19,woman,188.0
-------------------------执行sql---------------------------
小爱,30,woman,177.3
-------------------------执行sql---------------------------

--------------------关闭连接,并释放资源----------------------

可见,程序会先调用open()函数,创建JDBC连接及预编译SQL,然后使用该链接多次执行SQL语句,最终调用close()函数关闭连接释放资源。注意:代码里需要将并行度设置为了1,以便观察运行机制。

MySQL表数据

nameagegenderheight
小明20man180.2
小红22woman178.4
小黑18man192.9
小兰19woman188.0
小爱30woman177.3

注意:需要提前创建表person_message。

  • 1
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
### 回答1: Apache Flink 是一个分布式计算框架,可以用于实时流处理和批处理。Flink 提供了许多内置的 Sink,其中包括将数据写入 MySQL 数据库的 Sink。下面是使用 Flink 将数据写入 MySQL 的示例代码: ```java DataStream<Tuple2<String, Integer>> dataStream = ...; // 从某个地方获取数据流 // 将数据写入 MySQL dataStream.addSink(new JdbcSink<>(getConnection(), // 获取数据库连接 "INSERT INTO my_table (name, count) VALUES (?, ?)", // 插入语句 new JdbcStatementBuilder<Tuple2<String, Integer>>() { // 用于构建 PreparedStatement 的类 @Override public void accept(PreparedStatement preparedStatement, Tuple2<String, Integer> t) throws SQLException { // 设置参数 preparedStatement.setString(1, t.f0); preparedStatement.setInt(2, t.f1); } })); ``` 在上面的代码中,我们使用了 Flink 提供的 JdbcSink,将数据流中的 Tuple2<String, Integer> 类型的数据写入 MySQL 数据库。我们需要提供数据库连接、插入语句和用于构建 PreparedStatement 的类。在 accept 方法中,我们设置了 PreparedStatement 的参数,将数据写入数据库。 需要注意的是,我们需要在项目中添加 MySQL JDBC 驱动程序的依赖,以便在代码中使用。 ### 回答2: flink是一个用于数据流处理和批处理的开源框架,而MySQL是一个流行的关系型数据库管理系统。在flink中,可以使用sink来将数据流或批处理结果输出到MySQL中。 要实现flink sinkMySQL,首先需要创建一个StreamingExecutionEnvironment或ExecutionEnvironment对象,然后根据需要定义数据源和转换操作。接下来,通过定义一个JDBCOutputFormat,将转换后的数据流或批处理结果写入MySQL数据库。JDBCOutputFormat是flink提供的用于将数据写入关系型数据库的输出格式。 在JDBCOutputFormat的构造函数中,需要提供MySQL数据库的连接信息,例如URL、用户名和密码。然后,可以使用writeRecord方法将转换后的数据写入MySQL数据库。在写入数据之前,还可以使用setBatchInterval方法设置批量提交的间隔时间,以减少数据库的压力。 除了使用JDBCOutputFormat,还可以使用flink提供的Table API或SQL API来将数据写入MySQL。通过将MySQL作为一个外部系统注册到flink中,并定义一个INSERT INTO语句,可以直接将数据流或批处理结果插入到MySQL表中。 总之,flink可以通过使用JDBCOutputFormat、Table API或SQL API来将数据流或批处理结果输出到MySQL数据库中。这样可以方便地将flink处理的数据持久化到关系型数据库中,以供后续的分析和应用程序使用。 ### 回答3: Flink是一种开源的流式处理引擎,可以用来实现实时数据流处理。而MySQL是一个关系型数据库管理系统,它可以用来存储和管理结构化数据。 在Flink中,可以使用JDBC Sink将数据流输出到MySQL数据库中。首先,我们需要在Flink的配置文件中添加相应的依赖,以便能够与MySQL进行连接。 接下来,我们需要创建一个自定义的Sink函数,用来将数据流写入MySQL数据库。这个Sink函数需要继承自RichSinkFunction类,并实现其invoke方法。在invoke方法中,我们可以通过JDBC连接将数据写入MySQL数据库。 在Sink函数中,我们需要在open方法中获取MySQL数据库的连接,并在invoke方法中执行插入操作。同时,我们还可以在Sink函数中实现其他的数据处理逻辑,比如数据转换、筛选等。 最后,我们将Sink函数与Flink的DataStream进行关联,即可将数据流输出到MySQL数据库中。在使用Flink的execute方法执行任务时,数据流会经过Sink函数,并将结果写入MySQL数据库。 总结来说,Flink可以通过JDBC Sink将数据流输出到MySQL数据库中,我们只需要编写相应的Sink函数,并将其与DataStream关联起来即可实现。 需要注意的是,在使用Flink Sink写入MySQL数据库时,要确保数据库连接的可靠性和性能。可以考虑使用连接池来管理数据库连接,以提高系统的稳定性和吞吐量。同时,还要注意处理可能出现的异常情况,比如数据写入失败等。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值