本文正在参加「技术专题19期 漫谈数据库技术」活动
前言
随着企业的业务的快速发展,我们在处理实际的需求的过程中会发现,有些数据是实时要求查看,有些是可以T+1的需求,因此为了应付这些需求,实时计算和离线计算去处理这些需求,因此对大数据的要求也越来越高,你既要会离线,又要会实时,这就好比你要会前端也要会后端开发一样。既然实时计算这么重要,我们来看看它解决哪些问题呢?
实时数据解决什么问题
- 指标监控:比如有实时大盘,用来即时反馈业务当日运转的健康度等场景;
- 实时特征:比如搜索、广告CTR预估等,对算法特征数据新鲜度要求较高的场景;
- 事件处理:比如一些风控类、运营活动发券等事件驱动型场景;
- 数据对账:比如 金融的支付业务,支付部门与业务部门各自独立,当业务部门的支付单据与支付部门不一致时,会造成资损,这时数据的实时对账就非常关键。
既然聊到了实时计算,当下最火的计算框架可以了解下--Apache Flink。 Flink和Spark一样都是基于内存计算,由于它具备高吞吐和低延迟特性,但是它的速度和效率很高,它也是一个分布式的流处理框架,主要对数据流(有界数据和无边界数据)进行处理。Spark streaming虽然也是流式框架,但是它是基于微批处理,这也是大家认为Spark并不是真正的实时流式处理。 这里我简单介绍一下,更多的知识点可以去官网看看
正文
废话这么多了,那么现在我们来看看整个实时存储案例。
- 依赖文件,flink依赖kafka,jbdc,clickhouse
<dependency>
<groupId>org.apache.flink</groupId>
<artifactId>flink-connector-kafka_2.11</artifactId>
<version>${flink.version}</version>
</dependency>
<dependency>
<groupId>org.apache.flink</groupId>
<artifactId>flink-connector-jdbc_${scala.binary.version}</artifactId>
<version>${flink.version}</version>
<!-- <scope>provided</scope>-->
</dependency>
<dependency>
<groupId>org.apache.flink</groupId>
<artifactId>flink-csv</artifactId>
<version>${flink.version}</version>
</dependency>
<dependency>
<groupId>org.apache.flink</groupId>
<artifactId>flink-json</artifactId>
<version>1.11.1</version>
</dependency>
<!-- old planner flink table-->
<dependency>
<groupId>org.apache.flink</groupId>
<artifactId>flink-table-planner_2.11</artifactId>
<version>${flink.version}</version>
<!-- <scope>provided</scope>-->
</dependency>
<!--new planner-->
<dependency>
<groupId>org.apache.flink</groupId>
<artifactId>flink-table-planner-blink_2.11</artifactId>
<version>${flink.version}</version>
<!-- <scope>provided</scope>-->
</dependency>
<dependency>
<groupId>com.alibaba.ververica</groupId>
<artifactId>flink-format-changelog-json</artifactId>
<version>1.0.0</version>
</dependency>
<dependency>
<groupId>org.apache.flink</groupId>
<artifactId>flink-json</artifactId>
<version>${flink.version}</version>
</dependency>
<dependency>
<groupId>ru.yandex.clickhouse</groupId>
<artifactId>clickhouse-jdbc</artifactId>
<version>0.2.4</version>
</dependency>
<dependency>
<groupId>org.apache.flink</groupId>
<artifactId>flink-streaming-java_2.11</artifactId>
<version>${flink.version}</version>
</dependency>
<dependency>
<groupId>org.apache.flink</groupId>
<artifactId>flink-clients_2.11</artifactId>
<version>${flink.version}</version>
</dependency>
2.创建一个case class
case class UserAll(id:Int,name:String, create_time:Timestamp,etl_time:Timestamp,bid:Int, username:String,password:String)
3、看看flink的环境以及kafka配置等(包括并行度、jdbcurl、kafka属性)
val env = StreamExecutionEnvironment.getExecutionEnvironment
val tableEnv = StreamTableEnvironment.create(env)
env.setParallelism(1)
val ckJdbcUrl = "jdbc:clickhouse://hadoop101:8123/tutorial"
val ckUserName = "default"
val ckPassword = ""
val batchSize = 5
val props = new Properties()
props.setProperty(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, "hadoop101:9092")
props.setProperty(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, "latest")
props.setProperty(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, classOf[StringDeserializer].getName)
props.setProperty(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, classOf[StringDeserializer].getName)
4、我们按照自己的业务需求,对数据进行过滤操作,这里会看到cdc的一些信息,因为整个数据是通过cdc将mysql传输到kafka的。
val jsonStream: DataStream[JSONObject] = input.map(data => {JSON.parseObject(data)})
val inputStream: DataStream[JSONObject] = jsonStream.filter(data => "+I".equals(data.getString("op")))
5、手动实现 interface 的方式来传入相关 JDBC Statement build 函数
class CkSinkBuilderJson extends JdbcStatementBuilder[(Int,String, Timestamp,Timestamp,Int,String,String)] {
def accept(ps: PreparedStatement, v: (Int,String, Timestamp,Timestamp,Int,String,String)): Unit = {
ps.setInt(1, v._1)
ps.setString(2, v._2)
ps.setTimestamp(3, v._3)
ps.setTimestamp(4, v._4)
ps.setInt(5, v._5)
ps.setString(6, v._6)
ps.setString(7, v._7)
}
}
6、现在我们来看看整个写入的一个代码。整个过程我们需要将流式数据转为table数据,然后通过sink function 实现jdbc去实现这个写入。
val resultDataStream =
tableEnv.toAppendStream[(Int,String, Timestamp,Timestamp,Int,String,String)](resultTable)
val insertIntoCkSql =
"""
| INSERT INTO user_all (
| id, name,create_time,etl_time,bid,username,password
| ) VALUES (
| ?,?,?,?,?,?,?
| )
""".stripMargin
resultDataStream.addSink(
JdbcSink.sink[(Int,String, Timestamp,Timestamp,Int,String,String)](
insertIntoCkSql,
new CkSinkBuilderJson,
new JdbcExecutionOptions
.Builder()
.withMaxRetries(5)
.withBatchIntervalMs(50)
.withBatchSize(batchSize)
.build(),
new JdbcConnectionOptions.JdbcConnectionOptionsBuilder()
.withDriverName("ru.yandex.clickhouse.ClickHouseDriver")
.withUrl(ckJdbcUrl)
.withUsername(ckUserName)
.withPassword(ckPassword)
.build()
)
)
env.execute("ck测试")
}
}
好了,这个代码案例over了,看着简单,但是如果你去使用都会存在一些问题,这个都是正常吧,大数据没有那么好学,只要坚持其实你会发现就那么回事。
总结
本节主要讲解的是Flink消费Kafka数据存储到Clickhouse数据库的一个实战案例。对于实时需求,一旦你数据存储好了,那么就可以利用Clickhouse的优势去实现实时处理。