Flink 之 mysql二阶段提交简述

1. 2PC简介

两阶段提交(two-phase commit, 2PC)是最基础的分布式一致性协议,应用广泛。在分布式系统中,为了让每个节点都能够感知到其他节点的事务执行状况,需要引入一个中心节点来统一处理所有节点的执行逻辑,这个中心节点叫做协调者(coordinator),被中心节点调度的其他业务节点叫做参与者(participant)。

2PC。顾名思义,2PC将分布式事务分成了两个阶段,两个阶段分别为提交请求(投票)和提交(执行)。协调者根据参与者的响应来决定是否需要真正地执行事务,具体流程如下。

2. Flink 中的 2PC 应用

在 Flink 中,2PC的最常见应用场景其实是关系型数据库,比如MySQL InnoDB存储引擎的XA事务系统。

Flink作为流式处理引擎,自然也提供了对 Exactly-Once 语义的保证。端到端的 Exactly-Once 语义,是输入、处理逻辑、输出三部分协同作用的结果。Flink内部依托检查点机制(CheckPoint)和轻量级分布式快照算法(SnapShot)来保证 Exactly-Once。而要实现精确一次的输出逻辑,则需要施加以下两种限制之一:幂等性写入(idempotent write)、事务性写入(transactional write)。

在文章:自定义Redis Sink 中,我们有介绍过 Redis 的容错机制是 At Least Once,我们通过幂等操作,使用新数据覆盖旧数据的方式,以此来实现 Exactly-Once 。

在 Spark Streaming 中,要实现事务性写入完全靠用户自己,框架本身并没有提供任何实现。但是在 Flink 中提供了基于 2PC 的 SinkFunction ,名为 TwoPhaseCommitSinkFunction,帮助我们做了一些基础的工作。

3.关系型数据库如何实现二阶提交

在日常,我们使用到最多的还是关系型数据库(MySQL),但是针对关系型数据库,我们并不能通过幂等机制来实现 Exactly-Once。我们从 Flink官方文档 中可以了解到,将数据 Sink(下沉)到 Kafka 可以保证 Exactly-Once。
Flink 之 mysql二阶段提交简述_第1张图片
Flink 已经为我们提供了实现 Exactly-Once 的 FlinkKafkaProducer 类。如下图所示:它实现了 TwoPhaseCommitSinkFunction类,并重写了其中的方法,通过 2PC (Two Phase Comit) 二阶提交的方式,实现了 Exactly-Once。
源码图片两阶段提交

在关系型数据库mysql的使用场景下,我们开启二阶段提交的原因:

使用关系型数据库 MySQL,开启 CheckPoint 机制的前提下,为了保证前一次 CheckPoint 成功后到这次 CheckPoint 成功之前这段时间内的数据不丢失,如果执行到一半过程任务失败了,从而导致前一次CheckPoint成功后到任务失败前的数据已经存储到了MySQL,然而这部分数据并没有写入到 CheckPoint。如果任务重启后,前一次CheckPoint成功后到任务失败前的数据便会再次写入MySQL,从而导致数据重复的问题。这种情况,便使用到了 TwoPhaseCommitSinkFunction类,以此来实现 MySQL 关系型数据库的二阶提交。

4.MySQL 二阶提交实现

4.1 DBConnectUtil.java

package cn.huimin100.bigdata.learn.utils;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;


/**
 * 数据库连接工具类
 */
public class DBConnectUtil {
   
    private static final Logger log = LoggerFactory.getLogger(DBConnectUtil.class);

    /**
     * 获取连接
     *
     * @param url
     * @param user
     * @param password
     * @return
     * @throws SQLException
     */
    public static Connection getConnection(String url, String user, String password) throws SQLException {
   
        Connection conn = null;
        try {
   
            Class.forName("com.mysql.jdbc.Driver");
        } catch (ClassNotFoundException e) {
   
            log.error("获取mysql.jdbc.Driver失败");
            e.printStackTrace();
        }
        try {
   
            conn = DriverManager.getConnection(url, user, password);
            log.info("获取连接:{" + conn + "} 成功...");
        } catch (Exception e) {
   
            log.error("获取连接失败,url:" + url + ",user:" + user);
        }

        //设置手动提交
        conn.setAutoCommit(false);
        return conn;
    }

    /**
     * 提交事务
     */
    public static void commit(Connection conn) {
   
        if (conn != null) {
   
            try {
   
                conn.commit();
            } catch (SQLException e) {
   
                log.error("提交事务失败,Connection:" + conn);
                e.

你可能感兴趣的:(flink,flink)