【Java + PostgreSQL】JDBC接続でバルクインサートのパフォーマンスを確かめる
【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秒ぐらいで処理完了しています。
以上となります。
ここまでお読みいただきありがとうございました。