Mysql
 sql >> Teknologi Basis Data >  >> RDS >> Mysql

Kinerja pernyataan Sisipkan MySQL di Java:Pernyataan mode batch yang disiapkan vs sisipan tunggal dengan banyak nilai

JDBC hanyalah standar akses basis data Java SE yang menawarkan antarmuka standar sehingga Anda tidak benar-benar terikat pada implementasi JDBC tertentu. Konektor MySQL Java (Connector/J) adalah implementasi antarmuka JDBC untuk database MySQL saja. Di luar pengalaman, saya terlibat dalam sebuah proyek yang menggunakan sejumlah besar data menggunakan MySQL, dan kami kebanyakan lebih memilih MyISAM untuk data yang dapat dihasilkan:memungkinkan untuk mencapai transaksi kehilangan kinerja yang jauh lebih tinggi, tetapi secara umum, MyISAM lebih cepat, tetapi InnoDB lebih dapat diandalkan.

Saya bertanya-tanya untuk kinerja pernyataan INSERT juga sekitar setahun yang lalu, dan menemukan kode pengujian lama berikut di rak kode saya (maaf, ini agak rumit dan sedikit di luar cakupan pertanyaan Anda). Kode di bawah ini berisi contoh 4 cara memasukkan data uji:

  • lajang INSERT s;
  • berkelompok INSERT s;
  • jumlah manual INSERT (jangan pernah menggunakannya - itu berbahaya);
  • dan akhirnya siapkan massal INSERT ).

Ini menggunakan TestNG sebagai pelari, dan menggunakan beberapa warisan kode khusus seperti:

  • runWithConnection() metode - memastikan bahwa koneksi ditutup atau dimasukkan kembali ke kumpulan koneksi setelah panggilan balik dijalankan (tetapi kode di bawah ini tidak menggunakan strategi penutupan pernyataan yang dapat diandalkan - bahkan tanpa try /finally untuk mengurangi kode);
  • IUnsafeIn<T, E extends Throwable> - antarmuka panggilan balik khusus untuk metode yang menerima parameter tunggal tetapi berpotensi memunculkan pengecualian tipe E, seperti: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();
                    }
                });
            }
        });
    }

}

Lihatlah metode yang dianotasi dengan anotasi @Test:mereka benar-benar menjalankan INSERT pernyataan. Lihat juga CREATE_TABLE_QUERY konstan:dalam kode sumber menggunakan InnoDB menghasilkan hasil berikut di mesin saya dengan MySQL 5.5 diinstal (Konektor MySQL/J 5.1.12):

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

Jika Anda mengubah CREATE_TABLE_QUERY InnoDB ke MyISAM, Anda akan melihat peningkatan kinerja yang signifikan:

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

Semoga membantu.

UPD:

Untuk cara ke-4 Anda harus menyesuaikan max_allowed_packet dengan benar di mysql.ini ([mysqld] section) agar cukup besar untuk mendukung paket yang sangat besar.



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. kesalahan terus muncul di kode saya (koneksi sudah terbuka)

  2. Uji waktu eksekusi kueri di laravel

  3. Bagaimana menerapkan tipe data char(N) alih-alih varchar(N) di bidang model Django

  4. TIMEDIFF() Contoh – MySQL

  5. Pemicu MySQL jika kondisi ada