Flink高性能書き込みリレーショナルデータベースOracleまたはMySql

ビッグデータ開発に携わる人はますますリアルタイムコンピューティングに携わっていると思います。Flinkテクノロジーはそれが非常に重要であることを示しています。このテクノロジーはストリーミングコンピューティングだけでなく、そのためにも重要であると言われています。他のテクノロジーとの統合。より強力です。開発プロセスでは、メッセージミドルウェアやその他のシナリオへの書き込みに加えて、OracleMySqlなどの従来のデータベースへの書き込みも必要になる場合があります

リレーショナルデータベースに接続するときは、c3p0などの接続プールを使用することに慣れています。従来のビジネス開発やデータ量がそれほど多くない場合は問題ありませんが、データ量が多い場合は書き込み速度が高くなります。この方法では十分とは言えません。そういえば、ブロガーは多くの人がリソースを犠牲にして並列度を上げて効率を上げると言うだろうと信じています。この方法は効率を上げることはできますが、フリンクスロットを使いすぎ、接続プールのサイズもそれです。が適切に制御されておらず、接続が多すぎて、データベース接続のプレッシャーが高すぎることにも注意してください。また、接続プールが適切に閉じられていることにも注意してください。flinkタスクが再起動またはクラスターが再起動すると、接続プール接続が解放されているかどうかに関係なく、これらの問題ブロガーが発生します。開発過程で遭遇したので、ブロガーの解決策を紹介します。SQLを最適化するそしてマルチスレッド

Oracleのシナリオを使用します。現在のデータに基づいてOracleにクエリを実行し、Oracleに現在のデータがある場合は現在のデータを更新し、現在のデータがない場合は現在のデータをOracleに挿入します。

SQLの最適化

ETLを専門としないほとんどの人が最初に選択し、次に挿入または更新すると思います。これは問題ありませんが、データへのプレッシャーは大きくなります。データベースからプレッシャーを転送するためにマージインメソッドを使用します。自分の計算に関して、ブロガーは後で自分のs'q'lを投稿します。

接続プールの代わりにマルチスレッドを使用する

接続プールは接続の頻繁な作成を回避できますが、メソッドは1回しかロードされないため、f'lin'kのopenメソッドでもこの機能を実現できます。

コード上で言うことはあまりありません

import org.apache.flink.api.java.tuple.Tuple2;
import org.apache.flink.api.java.tuple.Tuple5;
import org.apache.flink.configuration.Configuration;
import org.apache.flink.streaming.api.functions.sink.RichSinkFunction;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ***OracleSinkMutlThread extends RichSinkFunction<Tuple5<String, Long, Long, Double, Double>> {
    private static final Logger LOGGER = LoggerFactory.getLogger(***OracleSinkMultThread.class);
    private List<Tuple2<Connection, PreparedStatement>> connectionList = new ArrayList<>();
    private int index = 0;
    private ExecutorService executorService;

    @Override
    public void open(Configuration parameters) throws Exception {
        super.open(parameters);
        Class.forName("oracle.jdbc.driver.OracleDriver");
        for (int i = 0; i < 10; i++) {
            Connection connection = DriverManager.getConnection("***", "***", "***");
            PreparedStatement statement = connection.prepareStatement("merge 表名 a using (select ? DAY_TIME,? HOUR_TIME,? PROV_NAME,? INTERFACE_NAME,? TOTAL_COUNT,? FAIL_COUNT,? TOTAL_TIME,? FAIL_TIME from dual) b on(a.DAY_TIME = b.DAY_TIME and a.HOUR_TIME=b.HOUR_TIME and a.PROV_NAME=b.PROV_NAME and a.INTERFACE_NAME=b.INTERFACE_NAME) when matched then update set a.TOTAL_COUNT=a.TOTAL_COUNT+b.TOTAL_COUNT, a.FAIL_COUNT=a.FAIL_COUNT+b.FAIL_COUNT,a.TOTAL_TIME=a.TOTAL_TIME+b.TOTAL_TIME,a.FAIL_TIME=a.FAIL_TIME+b.FAIL_TIME where DAY_TIME=? and HOUR_TIME=? and PROV_NAME=? and INTERFACE_NAME=? when not matched then insert values (b.DAY_TIME,b.HOUR_TIME,b.PROV_NAME,b.INTERFACE_NAME,b.TOTAL_COUNT,b.FAIL_COUNT,b.TOTAL_TIME,b.FAIL_TIME,?)");
            connectionList.add(new Tuple2(connection, statement));
        }
        executorService = Executors.newFixedThreadPool(10);

    }

    @Override
    public void invoke(Tuple5<String, Long, Long, Double, Double> value, Context context) throws Exception {
        String[] split = value.f0.split("_");
        String[] time = split[2].split("\t");
        String day_time = time[1];
        String hour_time = time[2];

        String provinceName = split[3];
        String INTERFACE_NAME = split[4];
        PreparedStatement statement;

        try {
            statement = connectionList.get(index).f1;
            statement.setString(1,day_time);
            statement.setString(2,hour_time);
            statement.setString(3,provinceName);
            statement.setString(4,INTERFACE_NAME);
            statement.setString(5,value.f1+"");

            statement.setString(6,value.f2+"");
            statement.setString(7,value.f3+"");
            statement.setString(8,value.f4+"");
            statement.setString(9,day_time);
            statement.setString(10,hour_time);

            statement.setString(11,provinceName);
            statement.setString(12,INTERFACE_NAME);

            long current_time = new Date().getTime();
            java.sql.Date dateSql = new java.sql.Date(current_time);
            statement.setDate(13,dateSql);

            executorService.execute(new Runnable() {
                @Override
                public void run() {
                    try {
                        statement.execute();
                    } catch (SQLException e) {
                        e.printStackTrace();
                    }
                }
            });
            index += 1;
            if (index == 10) {
                index = 0;
            }
        }catch (SQLException e){
            e.printStackTrace();
            LOGGER.error(String.format("%s -> *** !", "***"));
        }
    }

    @Override
    public void close() throws Exception {
        super.close();
        if (executorService != null) {
            executorService.shutdown();
        }
        for (Tuple2<Connection, PreparedStatement> tuple2 : connectionList) {
            if (tuple2.f0 != null) {
                tuple2.f0.close();
            }
            if (tuple2.f1 != null) {
                tuple2.f1.close();
            }
        }
    }
}

このメソッドはrichSinkFunctionを実装します。従来の接続プールは接続プールの初期化に時間がかかり、プログラムの並列処理はサイズを制御することで制御できるため、プログラムを最初に実行すると、この実装はすばやく初期化できます。ループのクラスタースロット消費を削減すると同時に、タスクが閉じられると、接続は問題なく解放されます。

おすすめ

転載: blog.csdn.net/qq_44962429/article/details/105976314