MariaDB
 sql >> Teknologi Basis Data >  >> RDS >> MariaDB

Kinerja Driver Konektor Java MariaDB

KINERJA KONEKTOR JAVA MARIADB

Kami selalu berbicara tentang kinerja. Tapi masalahnya selalu “Ukur, jangan menebak!”.

Banyak peningkatan kinerja yang telah dilakukan akhir-akhir ini pada MariaDB Java Connector. Jadi, bagaimana performa driver saat ini?

Izinkan saya membagikan hasil benchmark dari 3 driver jdbc yang mengizinkan akses ke database MySQL/MariaDB: DrizzleJDBC, MySQL Connector/J dan konektor java MariaDB.

Versi driver adalah versi GA terbaru yang tersedia saat blog ini ditulis:

  • MariaDB 1.5.3
  • MySQL 5.1.39
  • gerimis 1.4

TENTUKAN TUJUAN

JMH adalah alat kerangka kerja benchmark mikro Oracle yang dikembangkan oleh Oracle, dikirimkan sebagai alat openJDK, yang akan menjadi rangkaian microbenchmark java 9 resmi. Keuntungan khasnya dibandingkan kerangka kerja lain adalah ia dikembangkan oleh orang yang sama di Oracle yang mengimplementasikan JIT (kompilasi Just In Time) dan memungkinkan untuk menghindari sebagian besar jebakan benchmark mikro.

Sumber tolok ukur: https://github.com/rusher/mariadb-java-driver-benchmark.

Pengujian cukup mudah jika Anda terbiasa dengan java.
Contoh:

kelas publik BenchmarkSelect1RowPrepareText meluas BenchmarkSelect1RowPrepareAbstract { @Benchmark public String mysql(MyState state) throws Throwable { return select1RowPrepare(state.mysqlConnectionText, state); } @Benchmark public String mariadb(MyState state) throws Throwable { return select1RowPrepare(state.mariadbConnectionText, state); } @Benchmark public String gerimis(MyState state) throws Throwable { return select1RowPrepare(state.drizzleConnectionText, state); } }kelas abstrak publik BenchmarkSelect1RowPrepareAbstract extends BenchmarkInit { private String request ="SELECT CAST(? as char character set utf8)"; public String select1RowPrepare(Koneksi koneksi, status MyState) melempar SQLException { coba (PreparedStatement prepareStatement =connection.prepareStatement(request)) { prepareStatement.setString(1, state.insertData[state.counter++]); coba (ResultSet rs =readyStatement.executeQuery()) { rs.next(); kembali rs.getString(1); } } }}

Pengujian menggunakan kueri INSERT dikirim ke mesin BLACKHOLE dengan log biner dinonaktifkan, untuk menghindari IO dan ketergantungan pada kinerja penyimpanan. Ini memungkinkan untuk mendapatkan hasil yang lebih stabil.
(Tanpa menggunakan mesin lubang hitam dan menonaktifkan log biner, waktu eksekusi akan bervariasi hingga 10%).

Benchmark telah dieksekusi pada database MariaDB Server 10.1.17 dan MySQL Community Server 5.7.13. Dokumen berikut menunjukkan hasil menggunakan 3 driver dengan MariaDB Server 10.1.17. Untuk hasil lengkap termasuk yang dengan MySQL Server 5.7.13, silakan lihat tautan di bagian bawah dokumen.

LINGKUNGAN

Eksekusi (klien dan server) dilakukan pada droplet server tunggal di digitalocean.com menggunakan parameter berikut:

  • Java(TM) SE Runtime Environment (build 1.8.0_101-b13) 64bit (versi terakhir sebenarnya saat menjalankan benchmark ini)
  • Ubuntu 16.04 64bit
  • Memori 512Mb
  • 1 CPU
  • database MariaDB “10.1.17-MariaDB”, MySQL Community Server membangun “5.7.15-0ubuntu0.16.04.1”
    menggunakan file konfigurasi default dan opsi tambahan ini :

    • max_allowed_packet =40M #exchange paket bisa sampai 40mb
    • character-set-server =utf8 #untuk menggunakan UTF-8 sebagai default
    • collation-server =utf8_unicode_ci #untuk menggunakan UTF-8 sebagai default

Saat ditunjukkan "jauh", benchmark dijalankan dengan klien dan server terpisah pada 2 host identik di pusat data yang sama dengan rata-rata ping 0,350 md.

CONTOH PENJELASAN HASIL

Unit Kesalahan Skor BenchmarkBenchmarkSelect1RowPrepareText.mariadb 62.715 ± 2.402 s/opBenchmarkSelect1RowPrepareText.mysql 88.670 ± 3.505 s/opBenchmarkSelect1RowPrepareText.drizzle 78.672 ± 2.971 s/op

Artinya query sederhana ini akan memakan waktu rata-rata 62,715 mikrodetik menggunakan driver MariaDB dengan variasi ± 2,402 mikrodetik untuk 99,9% kueri.
Eksekusi yang sama menggunakan driver gerimis akan memakan waktu rata-rata 88,670 mikrodetik, dan 78,672 mikrodetik menggunakan konektor MySQL (semakin kecil waktu eksekusi semakin baik).

Persentase yang ditampilkan diatur menurut hasil pertama mariadb sebagai referensi (100%), memungkinkan untuk membandingkan hasil lainnya dengan mudah.

PERBANDINGAN KINERJA

Benchmark akan menguji kinerja 3 perilaku utama yang berbeda menggunakan database lokal yang sama (server yang sama), dan database jauh (server identik lainnya) pada pusat data yang sama dengan rata-rata ping 0,450 md

Perilaku yang berbeda:

Protokol teks

Ini sesuai dengan opsi useServerPrepStmts dinonaktifkan.
Kueri dikirim langsung ke server dengan penggantian parameter yang disanitasi dilakukan di sisi klien.
Data dikirim seperti teks. Contoh:Stempel waktu akan dikirim seperti teks “1970-01-01 00:00:00.000500” menggunakan 26 byte

Protokol biner

Ini sesuai dengan opsi yang diaktifkan useServerPrepStmts (implementasi default pada driver MariaDB).
Data dikirim dalam biner. Contoh timestamp “1970-01-01 00:00:00.000500” akan dikirim menggunakan 11 byte.

Ada hingga 3 pertukaran dengan server untuk satu permintaan :

  1. SIAPKAN – Mempersiapkan pernyataan untuk dieksekusi.
  2. JALANKAN – Kirim parameter
  3. DEALLOCATE PREPARE – Merilis pernyataan yang sudah disiapkan.

Lihat Dokumentasi persiapan server untuk informasi selengkapnya.

Hasil SIAPKAN disimpan dalam cache di sisi driver (ukuran default 250). Jika Prepare sudah ada di cache, PREPARE tidak akan dieksekusi, DEALLOCATE hanya akan dieksekusi ketika PREPARE tidak digunakan lagi dan tidak di cache. Itu berarti bahwa beberapa eksekusi kueri akan memiliki 3 perjalanan pulang pergi, tetapi beberapa hanya akan memiliki satu perjalanan pulang pergi, mengirimkan pengidentifikasi dan parameter SIAPKAN.

Tulis ulang

Ini sesuai dengan opsi rewriteBatchedStatements diaktifkan.
Rewrite menggunakan protokol teks dan hanya menyangkut kumpulan. Pengemudi akan menulis ulang kueri untuk hasil yang lebih cepat.

Contoh:
Masukkan ke dalam nilai ab (i) (?) dengan nilai batch pertama [1] dan [2] akan ditulis ulang menjadi
Masukkan ke dalam nilai ab (i) (1), (2).

Jika kueri tidak dapat ditulis ulang dalam "multi-nilai", penulisan ulang akan menggunakan multi-kueri :
Masukkan ke dalam tabel(col1) nilai (?) pada kunci duplikat pembaruan col2=? dengan nilai [1,2] dan [2,3] akan ditulis ulang menjadi
Insert into table(col1) values ​​(1) pada duplikat kunci update col2=2;Insert into table(col1) nilai (3) pada duplikat kunci pembaruan col2=4

Kelemahan dari opsi ini adalah:

  • ID kenaikan otomatis tidak dapat diambil menggunakanStatement.html#getGeneratedKeys().
  • Multi-kueri dalam satu eksekusi diaktifkan. Itu bukan masalah untukPreparedStatement, tetapi jika aplikasi menggunakan Statement itu bisa menjadi penurunan keamanan (injeksi SQL).

* MariaDB dan MySQL menerapkan 3 perilaku tersebut, Gerimis hanya protokol Teks.

HASIL BENCHMARK

Hasil driver MariaDB

KURI PILIH TUNGGAL

private String request ="SELECT CAST(? as char character set utf8)";public String select1RowPrepare(Connection connection, MyState state) throws SQLException { try (PreparedStatement prepareStatement =connection.prepareStatement(request)) { readyStatement.setString( 1, state.insertData[state.counter++]); // 100 byte acak. coba (ResultSet rs =readyStatement.executeQuery()) { rs.next(); kembali rs.getString(1); } }}
BASIS DATA LOKAL:BenchmarkSelect1RowPrepareHit.mariadb 58.267 ± 2.270 s/opBenchmarkSelect1RowPrepareMiss.mariadb 118.896 ± 5.500 s/opBenchmarkDATABASE JAUH:BenchmarkSelect1RowPrepareHit.mariadb 394.354 ± 13.102 s/opBenchmarkSelect1RowPrepareMiss.mariadb 709.843 ± 31.090 s/opBenchmarkSelect1RowPrepareText.mariadb 422.215 ± 15.8s/op 

Ketika hasil SIAPKAN untuk kueri persis ini sudah ada dalam cache (tekan cache), kueri akan lebih cepat (7,1% dalam contoh ini) daripada menggunakan protokol teks. Karena permintaan tambahan pertukaran PREPARE dan DEALLOCATE, cache miss 68,1% lebih lambat.

Ini menekankan keuntungan dan ketidaknyamanan menggunakan protokol biner. Cache HIT itu penting.

SINGLE INSERT QUERY

private String request ="INSERT INTO blackholeTable (charValue) nilai (?)";public boolean executeOneInsertPrepare(Connection connection, String[] datas) melempar SQLException { try (PreparedStatement ReadyStatement =connection.prepareStatement(request)) { ReadyStatement. setString(1, data[0]); //pengembalian data 100 byte acak readyStatement.execute(); }}
BASIS DATA LOKAL:BenchmarkOneInsertPrepareHit.mariadb 61.298 ± 1.940 s/opBenchmarkOneInsertPrepareMiss.mariadb 130.896 ± 6.362 s/opBenchmarkOneInsertPrepareText.mariadb 68.363 ± 2.686 s/op
DATABASE JAUH:BenchmarkOneInsertPrepareHit.mariadb 379.295 ± 17.351 s/opBenchmarkOneInsertPrepareMiss.mariadb 802.287 ± 24.825 s/opBenchmarkOneInsertPrepareText.mariadb 415.125 ± 14.547 

Hasil untuk INSERT mirip dengan hasil SELECT.

BATCH :1000 INSERT QUERY

private String request ="INSERT INTO blackholeTable (charValue) nilai (?)";public int[] executeBatch(Connection connection, String[] data) melempar SQLException { try (PreparedStatement prepareStatement =connection.prepareStatement(request)) { for (int i =0; i <1000; i++) { prepareStatement.setString(1, data[i]); //data 100 byte acak disiapkanStatement.addBatch(); } mengembalikan readyStatement.executeBatch(); }}
BASIS DATA LOKAL:PrepareStatementBatch100InsertPrepareHit.mariadb 5.290 ± 0.232 ms/opPrepareStatementBatch100InsertRewrite.mariadb 0.404 ± 0.014 ms/opPrepareStatementBatch100InsertText.mariadb 6.081 ± 0. 
DATABASE JAUH:PrepareStatementBatch100InsertPrepareHit.mariadb 7.639 ± 0.476 ms/opPrepareStatementBatch100InsertRewrite.mariadb 1.164 ± 0.037 ms/opPrepareStatementBatch100InsertText.mariadb 8.148 ± 0.563> ms 

Menggunakan protokol biner di sini lebih signifikan, memiliki hasil 13% lebih cepat daripada menggunakan protokol teks.

Sisipan dikirim secara massal dan hasilnya dibaca secara asinkron (yang sesuai dengan optionuseBatchMultiSend). Ini memungkinkan untuk mendapatkan hasil yang jauh dengan kinerja yang tidak jauh dari yang lokal.

Penulisan ulang memiliki kinerja bagus yang luar biasa, tetapi tidak akan memiliki id peningkatan otomatis. Jika Anda tidak membutuhkan id segera dan tidak menggunakan ORM, solusi ini akan menjadi yang tercepat. Beberapa konfigurasi izin ORM untuk menangani urutan secara internal untuk memberikan id kenaikan, tetapi urutan tersebut tidak didistribusikan, jadi tidak akan bekerja pada cluster.

PERBANDINGAN DENGAN DRIVER LAIN

PILIH kueri dengan hasil satu baris

BenchmarkSelect1RowPrepareHit.mariadb 58.267 ± 2.270 µs/opBenchmarkSelect1RowPrepareHit.mysql 73.789 ± 1.863 µs/opBenchmarkSelect1RowPrepareMiss.mariadb 118.896 ± 5.500 µs/opBenchmarkSelect1RowPrepareMiss.mysql 150.679 ± 4.791 µs/opBenchmarkSelect1RowPrepareText.mariadb 62.715 ± 2.402 µs/opBenchmarkSelect1RowPrepareText.mysql 88.670 ± 3.505 µs /opBenchmarkSelect1RowPrepareText.drizzle 78.672 ± 2.971 s/opBenchmarkSelect1RowPrepareTextHA.mariadb 64.676 ± 2.192 s/opBenchmarkSelect1RowPrepareTextHA.mysql 137.289 ± 4.872 s/op

HA adalah singkatan dari "Ketersediaan Tinggi" menggunakan konfigurasi Master-Slave
(URL koneksi adalah "jdbc:mysql:replication://localhost:3306,localhost:3306/testj").

Hasil ini disebabkan oleh banyak pilihan implementasi yang berbeda. Berikut adalah beberapa alasan yang menjelaskan perbedaan waktu:

  • Driver MariaDB dioptimalkan untuk UTF-8, memungkinkan lebih sedikit pembuatan larik byte, menghindari salinan larik, dan konsumsi memori.
  • Implementasi HA :Driver MariaDB dan MySQL menggunakan kelas Proxy dinamis java yang berada di antara objek Pernyataan dan soket, yang memungkinkan untuk menambahkan perilaku failover. Penambahan tersebut akan dikenakan biaya overhead 2 mikrodetik per kueri (62,715 tanpa menjadi 64,676 mikrodetik).
    Dalam implementasi MySQL, hampir semua metode internal diproksi, menambahkan overhead untuk banyak metode yang tidak ada hubungannya dengan failover, menambahkan total overhead 50 mikrodetik untuk setiap kueri.

(Drizzle tidak memiliki SIAPKAN, juga fungsi HA)

“Pilih 1000 baris”

permintaan String pribadi ="pilih * dari seq_1_to_1000"; //menggunakan mesin penyimpanan urutan pribadi ResultSet select1000Row(Connection connection) throws SQLException { try (Pernyataan pernyataan =connection.createStatement()) { try (ResultSet rs =statement.executeQuery(request)) { while (rs.next()) { rs.getString(1); } mengembalikan rs; } }
BenchmarkSelect1000Rows.mariadb 244.228 ± 7.686 s/opBenchmarkSelect1000Rows.mysql 298.814 ± 12.143 s/opBenchmarkSelect1000Rows.drizzle 406.877 ± 16.585 s/op

Saat menggunakan banyak data, sebagian besar waktu dihabiskan untuk membaca dari soket, dan menyimpan hasilnya di memori untuk mengirimnya kembali ke klien. Jika benchmark hanya mengeksekusi SELECT tanpa membaca hasilnya, waktu eksekusi MySQL dan MariaDB akan setara. Karena tujuan kueri SELECT adalah untuk mendapatkan hasil, driver MariaDB dioptimalkan untuk mengembalikan hasil (menghindari pembuatan array byte).

“Sisipkan 1000 baris”

LOCAL DATABASE:PrepareStatementBatch100InsertPrepareHit.mariadb 5.290 ± 0.232 ms/opPrepareStatementBatch100InsertPrepareHit.mysql 9.015 ± 0.440 ms/opPrepareStatementBatch100InsertRewrite.mariadb 0.404 ± 0.014 ms/opPrepareStatementBatch100InsertRewrite.mysql 0.592 ± 0.016 ms/opPrepareStatementBatch100InsertText.mariadb 6.081 ± 0.254 ms/opPrepareStatementBatch100InsertText.mysql 7.932 ± 0.293 ms/opPrepareStatementBatch100InsertText.drizzle 7.314 ± 0.205 ms/op
DISTANT DATABASE:PrepareStatementBatch100InsertPrepareHit.mariadb 7.639 ± 0.476 ms/opPrepareStatementBatch100InsertPrepareHit.mysql 43.636 ± 1.408 ms/opPrepareStatementBatch100InsertRewrite.mariadb 1.164 ± 0.037 ms/opPrepareStatementBatch100InsertRewrite.mysql 1.432 ± 0.050 ms/opPrepareStatementBatch100InsertText.mariadb 8.148 ± 0.563 ms/opPrepareStatementBatch100InsertText.mysql 43.804 ± 1,417 ms/opPrepareStatementBatch100InsertText.drizzle 38,735 ± 1,731 ms/op

Penyisipan massal MySQL dan Gerimis seperti X INSERT:Driver mengirim 1 INSERT, menunggu hasil penyisipan, dan mengirim penyisipan berikutnya. Latensi jaringan di antara setiap penyisipan akan memperlambat penyisipan.

Prosedur toko

PROSEDUR PANGGILAN

//CREATE PROCEDURE inoutParam(INOUT p1 INT) mulai set p1 =p1 + 1; endprivate String request ="{call inOutParam(?)}";Private String callableStatementWithOutParameter(Connection connection, MyState state) throws SQLException { try (CallableStatement storedProc =connection.prepareCall(request)) { storedProc.setInt(1, state.functionVar1); //2 storedProc.registerOutParameter(1, Types.INTEGER); disimpanProc.execute(); kembalikan storedProc.getString(1); }}
BenchmarkCallableStatementWithOutParameter.mariadb 88.572 ± 4.263 s/opBenchmarkCallableStatementWithOutParameter.mysql 714.108 ± 44.390 s/op

Implementasi MySQL dan MariaDB sangat berbeda. Driver MySQL akan menggunakan banyak query tersembunyi untuk mendapatkan hasil keluaran :

  • SHOW CREATE PROCEDURE testj.inoutParam untuk mengidentifikasi parameter IN dan OUT
  • SET @com_mysql_jdbc_outparam_p1 = 1 untuk mengirim data sesuai dengan parameter IN / OUT
  • CALL testj.inoutParam(@com_mysql_jdbc_outparam_p1) prosedur panggilan
  • SELECT @com_mysql_jdbc_outparam_p1 untuk membaca hasil keluaran

Implementasi MariaDB sangat mudah menggunakan kemampuan untuk memiliki parameter OUT dalam respons server tanpa pertanyaan tambahan. (Itulah alasan utama mengapa driver MariaDB memerlukan server MariaDB/MySQL versi 5.5.3 atau lebih baru).

kesimpulan

Pengemudi MariaDB terguncang !

Protokol biner memiliki keuntungan yang berbeda tetapi bergantung pada memiliki hasil SIAPKAN sudah dalam cache. Jika aplikasi memiliki banyak jenis kueri yang berbeda dan basis datanya jauh, itu mungkin bukan solusi yang lebih baik.

Penulisan ulang memiliki hasil yang luar biasa untuk menulis data dalam batch

Pengemudi bertahan dengan baik dibandingkan pengemudi lain. Dan masih banyak yang akan datang, tapi itu cerita lain.

Hasil mentah:

  1. dengan database MariaDB 10.1.17 lokal, jauh
  2. dengan database MySQL Community Server 5.7.15 (build 5.7.15-0ubuntu0.16.04.1) lokal

  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Kerberos untuk SQLyog oleh MariaDB Connector/C

  2. DROP TABLE JIKA ADA di MariaDB

  3. 8 Cara Menambahkan Hari ke Tanggal di MariaDB

  4. COUNT() Fungsi di MariaDB

  5. Cara Mudah Menyebarkan Cluster MySQL Galera di AWS