Jawaban:
https://dev.mysql.com/doc/refman/8.0/id/insert-optimization.html
Waktu yang diperlukan untuk memasukkan baris ditentukan oleh faktor-faktor berikut, di mana angka-angka menunjukkan perkiraan proporsi:
- Menghubungkan: (3)
- Mengirim kueri ke server: (2)
- Permintaan parsing: (2)
- Memasukkan baris: (1 × ukuran baris)
- Memasukkan indeks: (1 × jumlah indeks)
- Penutup: (1)
Dari sini harus jelas, bahwa mengirim satu pernyataan besar akan menghemat biaya tambahan 7 per pernyataan penyisipan, yang selanjutnya membaca teks juga mengatakan:
Jika Anda memasukkan banyak baris dari klien yang sama secara bersamaan, gunakan pernyataan INSERT dengan beberapa daftar VALUES untuk menyisipkan beberapa baris sekaligus. Ini jauh lebih cepat (beberapa kali lebih cepat dalam beberapa kasus) daripada menggunakan pernyataan INSERT baris tunggal yang terpisah.
Saya tahu saya menjawab pertanyaan ini hampir dua setengah tahun setelah ditanya, tetapi saya hanya ingin memberikan beberapa data keras dari proyek yang sedang saya kerjakan sekarang yang menunjukkan bahwa memang melakukan beberapa blok VALUE per sisipan adalah JAUH lebih cepat daripada laporan VALUE blok INSERT tunggal berurutan.
Kode yang saya tulis untuk tolok ukur ini dalam C # menggunakan ODBC untuk membaca data ke dalam memori dari sumber data MSSQL (~ 19.000 baris, semua dibaca sebelum penulisan dimulai), dan konektor MySql .NET (Mysql.Data. *) Digunakan untuk Masukkan data dari memori ke dalam tabel di server MySQL melalui pernyataan yang disiapkan. Itu ditulis sedemikian rupa sehingga memungkinkan saya untuk secara dinamis menyesuaikan jumlah blok VALUE per INSERT yang disiapkan (yaitu, masukkan n baris pada suatu waktu, di mana saya bisa menyesuaikan nilai n sebelum lari.) Saya juga menjalankan tes beberapa kali untuk setiap n.
Melakukan satu blok VALUE tunggal (mis. 1 baris sekaligus) membutuhkan waktu 5,7 - 5,9 detik untuk dijalankan. Nilai lainnya adalah sebagai berikut:
2 baris sekaligus: 3.5 - 3.5 detik
5 baris sekaligus: 2.2 - 2.2 detik
10 baris sekaligus: 1.7 - 1.7 detik
50 baris sekaligus: 1.17 - 1.18 detik
100 baris sekaligus: 1.1 - 1.4 detik
500 baris sekaligus: 1.1 - 1.2 detik
1000 baris sekaligus: 1.17 - 1.17 detik
Jadi ya, bahkan hanya bundling 2 atau 3 menulis bersama memberikan peningkatan dramatis dalam kecepatan (runtime dipotong oleh faktor n), sampai Anda sampai di suatu tempat antara n = 5 dan n = 10, di mana titik peningkatan menurun secara nyata, dan di suatu tempat dalam kisaran n = 10 hingga n = 50 peningkatan menjadi diabaikan.
Harapan yang membantu orang memutuskan (a) apakah akan menggunakan ide multiprepare, dan (b) berapa banyak blok VALUE untuk dibuat per pernyataan (dengan asumsi Anda ingin bekerja dengan data yang mungkin cukup besar untuk mendorong kueri melewati ukuran kueri maks. untuk MySQL, yang saya percaya adalah 16MB secara default di banyak tempat, mungkin lebih besar atau lebih kecil tergantung pada nilai set max_allowed_packet di server.)
Faktor utama adalah apakah Anda menggunakan mesin transaksional dan apakah Anda memiliki autocommit.
Autocommit aktif secara default dan Anda mungkin ingin membiarkannya; Oleh karena itu, setiap sisipan yang Anda lakukan melakukan transaksi sendiri. Ini berarti bahwa jika Anda melakukan satu memasukkan per baris, Anda akan melakukan transaksi untuk setiap baris.
Dengan asumsi satu utas, itu berarti bahwa server perlu menyinkronkan beberapa data ke disk untuk SETIAP BARIS. Perlu menunggu data untuk mencapai lokasi penyimpanan yang persisten (mudah-mudahan ram yang didukung baterai di controller RAID Anda). Ini secara inheren agak lambat dan mungkin akan menjadi faktor pembatas dalam kasus ini.
Saya tentu saja mengasumsikan bahwa Anda menggunakan mesin transaksional (biasanya innodb) DAN bahwa Anda belum mengubah pengaturan untuk mengurangi daya tahan.
Saya juga berasumsi bahwa Anda menggunakan utas tunggal untuk melakukan sisipan ini. Menggunakan beberapa thread muddies hal-hal sedikit karena beberapa versi MySQL memiliki kelompok kerja-komit di innodb - ini berarti bahwa beberapa thread melakukan komitmen mereka sendiri dapat berbagi satu tulis ke log transaksi, yang baik karena berarti lebih sedikit sinkronisasi untuk penyimpanan persisten .
Di sisi lain, hasilnya adalah, bahwa Anda BENAR-BENAR INGIN MENGGUNAKAN sisipan multi-baris.
Ada batas di mana ia menjadi kontra-produktif, tetapi dalam kebanyakan kasus itu setidaknya 10.000 baris. Jadi, jika Anda mengelompokkannya hingga 1.000 baris, Anda mungkin aman.
Jika Anda menggunakan MyISAM, ada banyak hal lain, tetapi saya tidak akan membuat Anda bosan dengan itu. Perdamaian.
Secara umum semakin sedikit jumlah panggilan ke basis data semakin baik (artinya lebih cepat, lebih efisien), jadi cobalah untuk membuat kode sisipan sedemikian rupa sehingga meminimalkan akses basis data. Ingat, kecuali Anda menggunakan kumpulan koneksi, setiap akses databse harus membuat koneksi, jalankan sql, dan kemudian hancurkan koneksi. Sedikit overhead!
Anda mungkin ingin :
Bergantung pada seberapa baik skala server Anda (secara definitif ok dengan PostgreSQl
, Oracle
dan MSSQL
), lakukan hal di atas dengan beberapa utas dan banyak koneksi.
Secara umum, beberapa sisipan akan lebih lambat karena overhead koneksi. Melakukan banyak insert sekaligus akan mengurangi biaya overhead per insert.
Bergantung pada bahasa yang Anda gunakan, Anda mungkin dapat membuat batch dalam bahasa pemrograman / scripting Anda sebelum pergi ke db dan menambahkan setiap sisipan ke batch. Maka Anda akan dapat menjalankan batch besar menggunakan satu operasi terhubung. Berikut ini contohnya di Jawa.
MYSQL 5.5 Satu pernyataan memasukkan sql mengambil ~ 300 hingga ~ 450ms. sedangkan statistik di bawah ini adalah untuk statemen penyisipan banyak inline.
(25492 row(s) affected)
Execution Time : 00:00:03:343
Transfer Time : 00:00:00:000
Total Time : 00:00:03:343
Saya akan mengatakan inline adalah cara untuk pergi :)
Sungguh konyol betapa buruknya Mysql dan MariaDB dioptimalkan ketika datang ke sisipan. Saya menguji mysql 5.7 dan mariadb 10.3, tidak ada perbedaan nyata pada mereka.
Saya sudah menguji ini pada server dengan disk NVME, 70.000 IOPS, throughput seq 1,1 GB / detik dan itu mungkin dupleks penuh (baca dan tulis).
Server adalah server berkinerja tinggi juga.
Berikan 20 GB ram.
Basis data benar-benar kosong.
Kecepatan yang saya terima adalah 5.000 sisipan per detik saat melakukan sisipan multi baris (mencobanya dengan potongan data 1MB hingga 10MB)
Sekarang petunjuknya:
Jika saya menambahkan utas lain dan memasukkan ke dalam tabel SAMA saya tiba-tiba memiliki 2x5000 / detik. Satu utas lagi dan saya memiliki total 15000 / detik
Pertimbangkan ini: Ketika melakukan SATU utas menyisipkan itu berarti Anda dapat menulis secara berurutan ke disk (dengan pengecualian indeks). Saat menggunakan utas, Anda sebenarnya menurunkan kinerja yang mungkin karena sekarang perlu melakukan lebih banyak akses acak. Tetapi kenyataan menunjukkan mysql sangat dioptimalkan sehingga utas banyak membantu.
Performa nyata yang mungkin terjadi dengan server seperti itu mungkin jutaan per detik, CPU menganggur, disk menganggur.
Alasannya cukup jelas bahwa mariadb seperti halnya mysql memiliki penundaan internal.
id
, parent_id
) VALUES (1, NULL). Salah satu dari kumpulan nilai berikutnya menghubungkan ke baris itu. Jika Anda membagi dalam potongan dan set yang sampai ke potongan lain, itu mungkin diproses sebelum yang pertama, gagal seluruh proses. Adakah cara mengatasinya?
beberapa sisipan lebih cepat tetapi harus dilakukan. thrik lain sedang menonaktifkan memeriksa kendala membuat insert jauh lebih cepat. Tidak masalah meja Anda memilikinya atau tidak. Misalnya, coba nonaktifkan kunci asing dan nikmati kecepatannya:
SET FOREIGN_KEY_CHECKS=0;
Anda harus mengaktifkannya kembali setelah sisipan dengan:
SET FOREIGN_KEY_CHECKS=1;
ini adalah cara umum untuk memasukkan data besar. integritas data mungkin rusak sehingga Anda harus mengatasinya sebelum menonaktifkan pemeriksaan kunci asing.