sql >> データベース >  >> RDS >> Mysql

JavaでのMySQLInsertステートメントのパフォーマンス:バッチモードのプリペアドステートメントと複数の値を持つ単一の挿入

    JDBCは、データベースアクセスのJava SE標準であり、標準インターフェイスを提供するため、特定のJDBC実装に実際に拘束されることはありません。 MySQL Javaコネクタ(Connector / J)は、MySQLデータベース専用のJDBCインターフェイスの実装です。経験から、私はMySQLを使用して大量のデータを使用するプロジェクトに携わっています。生成できるデータには、主にMyISAMを使用します。これにより、パフォーマンスが大幅に低下するトランザクションを実現できますが、一般的に、MyISAMの方が高速です。ただし、InnoDBの方が信頼性が高くなります。

    約1年前にもINSERTステートメントのパフォーマンスについて疑問に思い、コードシェルフに次の古いテストコードが見つかりました(申し訳ありませんが、少し複雑で、質問の範囲から少し外れています)。以下のコードには、テストデータを挿入する4つの方法の例が含まれています。

    • シングル INSERT s;
    • バッチ処理 INSERT s;
    • 手動バルク INSERT (絶対に使用しないでください-危険です);
    • そして最後に準備されたバルク INSERT

    TestNG を使用します ランナーとして、次のようなカスタムコードのレガシーを使用します:

    • runWithConnection() メソッド-コールバックの実行後に接続が閉じられるか、接続プールに戻されるようにします(ただし、以下のコードは、tryがなくても、ステートメントを閉じるという信頼できる戦略を使用していません /finally コードを減らすため);
    • IUnsafeIn<T, E extends Throwable> -単一のパラメータを受け入れるが、タイプEの例外をスローする可能性のあるメソッドのカスタムコールバックインターフェイス:void handle(T argument) throws E;
    package test;
    
    import test.IUnsafeIn;
    
    import java.sql.Connection;
    import java.sql.PreparedStatement;
    import java.sql.SQLException;
    
    import static java.lang.String.format;
    import static java.lang.String.valueOf;
    import static java.lang.System.currentTimeMillis;
    
    import core.SqlBaseTest;
    import org.testng.annotations.AfterSuite;
    import org.testng.annotations.BeforeSuite;
    import org.testng.annotations.BeforeTest;
    import org.testng.annotations.Test;
    
    public final class InsertVsBatchInsertTest extends SqlBaseTest {
    
        private static final int ITERATION_COUNT = 3000;
    
        private static final String CREATE_TABLE_QUERY = "CREATE TABLE IF NOT EXISTS ttt1 (c1 INTEGER, c2 FLOAT, c3 VARCHAR(5)) ENGINE = InnoDB";
        private static final String DROP_TABLE_QUERY = "DROP TABLE ttt1";
        private static final String CLEAR_TABLE_QUERY = "DELETE FROM ttt1";
    
        private static void withinTimer(String name, Runnable runnable) {
            final long start = currentTimeMillis();
            runnable.run();
            logStdOutF("%20s: %d ms", name, currentTimeMillis() - start);
        }
    
        @BeforeSuite
        public void createTable() {
            runWithConnection(new IUnsafeIn<Connection, SQLException>() {
                @Override
                public void handle(Connection connection) throws SQLException {
                    final PreparedStatement statement = connection.prepareStatement(CREATE_TABLE_QUERY);
                    statement.execute();
                    statement.close();
                }
            });
        }
    
        @AfterSuite
        public void dropTable() {
            runWithConnection(new IUnsafeIn<Connection, SQLException>() {
                @Override
                public void handle(Connection connection) throws SQLException {
                    final PreparedStatement statement = connection.prepareStatement(DROP_TABLE_QUERY);
                    statement.execute();
                    statement.close();
                }
            });
        }
    
        @BeforeTest
        public void clearTestTable() {
            runWithConnection(new IUnsafeIn<Connection, SQLException>() {
                @Override
                public void handle(Connection connection) throws SQLException {
                    final PreparedStatement statement = connection.prepareStatement(CLEAR_TABLE_QUERY);
                    statement.execute();
                    statement.close();
                }
            });
        }
    
        @Test
        public void run1SingleInserts() {
            withinTimer("Single inserts", new Runnable() {
                @Override
                public void run() {
                    runWithConnection(new IUnsafeIn<Connection, SQLException>() {
                        @Override
                        public void handle(Connection connection) throws SQLException {
                            for ( int i = 0; i < ITERATION_COUNT; i++ ) {
                                final PreparedStatement statement = connection.prepareStatement("INSERT INTO ttt1 (c1, c2, c3) VALUES (?, ?, ?)");
                                statement.setInt(1, i);
                                statement.setFloat(2, i);
                                statement.setString(3, valueOf(i));
                                statement.execute();
                                statement.close();
                            }
                        }
                    });
                }
            });
        }
    
        @Test
        public void run2BatchInsert() {
            withinTimer("Batch insert", new Runnable() {
                @Override
                public void run() {
                    runWithConnection(new IUnsafeIn<Connection, SQLException>() {
                        @Override
                        public void handle(Connection connection) throws SQLException {
                            final PreparedStatement statement = connection.prepareStatement("INSERT INTO ttt1 (c1, c2, c3) VALUES (?, ?, ?)");
                            for ( int i = 0; i < ITERATION_COUNT; i++ ) {
                                statement.setInt(1, i);
                                statement.setFloat(2, i);
                                statement.setString(3, valueOf(i));
                                statement.addBatch();
                            }
                            statement.executeBatch();
                            statement.close();
                        }
                    });
                }
            });
        }
    
        @Test
        public void run3DirtyBulkInsert() {
            withinTimer("Dirty bulk insert", new Runnable() {
                @Override
                public void run() {
                    runWithConnection(new IUnsafeIn<Connection, SQLException>() {
                        @Override
                        public void handle(Connection connection) throws SQLException {
                            final StringBuilder builder = new StringBuilder("INSERT INTO ttt1 (c1, c2, c3) VALUES ");
                            for ( int i = 0; i < ITERATION_COUNT; i++ ) {
                                if ( i != 0 ) {
                                    builder.append(",");
                                }
                                builder.append(format("(%s, %s, '%s')", i, i, i));
                            }
                            final String query = builder.toString();
                            final PreparedStatement statement = connection.prepareStatement(query);
                            statement.execute();
                            statement.close();
                        }
                    });
                }
            });
        }
    
        @Test
        public void run4SafeBulkInsert() {
            withinTimer("Safe bulk insert", new Runnable() {
                @Override
                public void run() {
                    runWithConnection(new IUnsafeIn<Connection, SQLException>() {
                        private String getInsertPlaceholders(int placeholderCount) {
                            final StringBuilder builder = new StringBuilder("(");
                            for ( int i = 0; i < placeholderCount; i++ ) {
                                if ( i != 0 ) {
                                    builder.append(",");
                                }
                                builder.append("?");
                            }
                            return builder.append(")").toString();
                        }
    
                        @SuppressWarnings("AssignmentToForLoopParameter")
                        @Override
                        public void handle(Connection connection) throws SQLException {
                            final int columnCount = 3;
                            final StringBuilder builder = new StringBuilder("INSERT INTO ttt1 (c1, c2, c3) VALUES ");
                            final String placeholders = getInsertPlaceholders(columnCount);
                            for ( int i = 0; i < ITERATION_COUNT; i++ ) {
                                if ( i != 0 ) {
                                    builder.append(",");
                                }
                                builder.append(placeholders);
                            }
                            final int maxParameterIndex = ITERATION_COUNT * columnCount;
                            final String query = builder.toString();
                            final PreparedStatement statement = connection.prepareStatement(query);
                            int valueIndex = 0;
                            for ( int parameterIndex = 1; parameterIndex <= maxParameterIndex; valueIndex++ ) {
                                statement.setObject(parameterIndex++, valueIndex);
                                statement.setObject(parameterIndex++, valueIndex);
                                statement.setObject(parameterIndex++, valueIndex);
                            }
                            statement.execute();
                            statement.close();
                        }
                    });
                }
            });
        }
    
    }
    

    @Testアノテーションが付けられたメソッドを見てください。実際にはINSERTを実行します。 ステートメント。 CREATE_TABLE_QUERYもご覧ください。 定数:ソースコードでは、InnoDBを使用して、MySQL 5.5がインストールされているマシン(MySQL Connector / J 5.1.12)で次の結果を生成します。

    InnoDB
    Single inserts: 74148 ms
    Batch insert: 84370 ms
    Dirty bulk insert: 178 ms
    Safe bulk insert: 118 ms
    

    CREATE_TABLE_QUERYを変更した場合 InnoDBからMyISAMに移行すると、パフォーマンスが大幅に向上します。

    MyISAM
    Single inserts: 604 ms
    Batch insert: 447 ms
    Dirty bulk insert: 63 ms
    Safe bulk insert: 26 ms
    

    これがお役に立てば幸いです。

    UPD:

    4番目の方法では、max_allowed_packetを適切にカスタマイズする必要があります mysql.ini内 ([mysqld] セクション)本当に大きなパケットをサポートするのに十分な大きさである必要があります。



    1. mysqlの手順

    2. IN句のパラメータを使用したOracleストアドプロシージャ

    3. 配列をmysqlに渡す

    4. Java-MySQLからHiveへのインポート。MySQLはWindowsで実行され、HiveはCent OSで実行されます(Horton Sandbox)