【Java + PostgreSQL】JDBC接続でバルクインサートのパフォーマンスを確かめる

6月 18, 2023

【Java + PostgreSQL】JDBC接続でバルクインサートのパフォーマンスを確かめる

今回使用した環境

インターネット接続可能のオンラインの環境

64 ビット オペレーティング システム

Windows 10 22H2

PostgreSQL 14.4

検証内容

PostgreSQLのテーブルに以下の2パターンで10万件をインサートしてパフォーマンスを確かめます。

・10万件を1件ずつインサート

・100件を一括でバルクインサート x 1000回で10万件をインサート

※JDBC接続を使用していきます。

ソースコード

INSERTに使用するテーブルは以下を使います。勘定科目マスタにしてます。

実業務で勘定科目マスタに10万件も登録することは実際にはないですけどね…。テストなのでご了承ください。

CREATE TABLE ms_kamoku
(
    kamoku_cd character varying(8) NOT NULL,
    kamoku_name character varying(40),
    kinds character varying(1),
    start_date date NOT NULL,
    insert_user_id character varying(4),
    insert_date timestamp without time zone,
    update_user_id character varying(4),
    update_date timestamp without time zone,
    CONSTRAINT ms_kamoku_pkey PRIMARY KEY (kamoku_cd, start_date)
)

以下のJdbcTestクラスをmainメソッド実行クラスにしています。

コマンドライン引数で"0″を与えた場合、通常のインサート

コマンドライン引数で"1″を与えた場合、バルクインサート

となります。

package jdbctest;

import java.sql.SQLException;

public class JdbcTest {

	public static void main(String[] args) {

		if (args.length == 0) {
			System.out.println("引数が設定されていません。");
		}
		
		// コマンド引数よりバルクインサートを行うかどうかを判断
		boolean isBulkInsert = args[0].equals("0") ? false : true;
		
		MsKamokuDao dao = new MsKamokuDao();
		try {
			dao.insert(isBulkInsert);
			
		} catch (SQLException e) {
			e.printStackTrace();
		}
	}
}

以下がMsKamokuDaoクラスです。

10万件のインサート処理を行い、処理前後で時間を出力します。

package jdbctest;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;

public class MsKamokuDao {

	// DB接続情報
	private static final String URL = "jdbc:postgresql://localhost:5432/sampledb";
	private static final String USER = "testuser";
	private static final String PASS = "xxxxx";

	// ms_kamokuのテーブルに10万件のデータをインサートする
	public void insert(boolean isBulkInsert) throws SQLException {
	
		DateFormat format = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss.SSS");
		
		try (Connection conn = DriverManager.getConnection(URL, USER, PASS);
				Statement stmt = conn.createStatement();) {

			// インサート開始前の時刻を出力
			System.out.println("インサート処理開始:" + format.format(new Date()));
			
			// 処理前にテーブルデータを削除
			stmt.executeUpdate("TRUNCATE TABLE ms_kamoku;");
			
			conn.setAutoCommit(false);

			StringBuilder sb = new StringBuilder();
			if (isBulkInsert) {
				// BULK INSERT (100件単位でインサート)
				for (int i = 0; i < 1000; i++) {
					sb.delete(0, sb.length());
					sb.append("INSERT INTO ms_kamoku(");
					sb.append("   kamoku_cd");
					sb.append("  ,kamoku_name");
					sb.append("  ,kinds");
					sb.append("  ,start_date");
					sb.append("  ,insert_user_id");
					sb.append("  ,insert_date");
					sb.append("  ,update_user_id");
					sb.append("  ,update_date");
					sb.append(")");
					sb.append(" VALUES ");
					for (int j = 0; j < 100; j++) {
						if (j == 0) {
							sb.append("(");
						} else {
							sb.append(",(");
						}
						sb.append("   '" + String.format("%06d", i * 100 + j) + "'");
						sb.append("  ,'テスト科目'");
						sb.append("  ,'1'");
						sb.append("  ,'2023/06/08'");
						sb.append("  ,'0001'");
						sb.append("  ,'2023/06/08'");
						sb.append("  ,'0001'");
						sb.append("  ,'2023/06/08'");
						sb.append(")");
					}
					sb.append(";");
					stmt.executeUpdate(sb.toString());
				}
			} else {
				// 通常のINSERT (1件ずつインサート)
				for (int i = 0; i < 100000; i++) {
					sb.delete(0, sb.length());
					sb.append("INSERT INTO ms_kamoku(");
					sb.append("   kamoku_cd");
					sb.append("  ,kamoku_name");
					sb.append("  ,kinds");
					sb.append("  ,start_date");
					sb.append("  ,insert_user_id");
					sb.append("  ,insert_date");
					sb.append("  ,update_user_id");
					sb.append("  ,update_date");
					sb.append(")");
					sb.append(" VALUES (");
					sb.append("   '" + String.format("%06d", i) + "'");
					sb.append("  ,'テスト科目'");
					sb.append("  ,'1'");
					sb.append("  ,'2023/06/08'");
					sb.append("  ,'0001'");
					sb.append("  ,'2023/06/08'");
					sb.append("  ,'0001'");
					sb.append("  ,'2023/06/08'");
					sb.append(");");
					stmt.executeUpdate(sb.toString());
				}
			}
			
			conn.commit();
			
			// インサート終了後の時刻を出力
			System.out.println("インサート処理終了:" + format.format(new Date()));
			
		} catch (SQLException e) {
			throw e;
		}
	}
}

動作確認

まずは1件ずつ10万件登録する通常のINSERTです。3回実行してだいたい平均10秒ぐらいかかっております。

次に100件を一括でバルクインサート x 1000回で10万件登録するINSERTです。3回実行してだいたい平均3秒ぐらいで処理完了しています。

以上となります。

ここまでお読みいただきありがとうございました。

Java

Posted by だゆう