PRESTATIES MARIADB JAVA-CONNECTOR
We hebben het altijd over prestaties. Maar het ding is altijd "Meet, raad niet!".
Er is de laatste tijd veel prestatieverbetering gedaan op de MariaDB Java Connector. Dus, wat zijn de huidige prestaties van de bestuurder?
Laat me een benchmarkresultaat delen van 3 jdbc-stuurprogramma's die toegang geven tot een MySQL/MariaDB-database:DrizzleJDBC, MySQL Connector/J en MariaDB Java-connector.
De versies van de stuurprogramma's zijn de nieuwste beschikbare GA-versie op het moment van schrijven van deze blog:
- MariaDB 1.5.3
- MySQL 5.1.39
- Motregen 1.4
DE BENCHMARK
JMH is een Oracle microbenchmarking framework-tool ontwikkeld door Oracle, geleverd als openJDK-tools, die de officiële java 9 microbenchmark-suite zal zijn. Het onderscheidende voordeel ten opzichte van andere frameworks is dat het is ontwikkeld door dezelfde jongens in Oracle die JIT (Just In Time-compilatie) implementeren en toestaan om de meeste valkuilen van microbenchmarks te vermijden.
Benchmarkbron: https://github.com/rusher/mariadb-java-driver-benchmark.
Testen zijn vrij eenvoudig als u bekend bent met Java.
Voorbeeld:
public class BenchmarkSelect1RowPrepareText extends 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 drizzle(MyState state) throws Throwable { return select1RowPrepare(state.drizzleConnectionText, state); } } public abstract class BenchmarkSelect1RowPrepareAbstract extends BenchmarkInit { private String request = "SELECT CAST(? as char character set utf8)"; public String select1RowPrepare(Connection connection, MyState state) throws SQLException { try (PreparedStatement preparedStatement = connection.prepareStatement(request)) { preparedStatement.setString(1, state.insertData[state.counter++]); try (ResultSet rs = preparedStatement.executeQuery()) { rs.next(); return rs.getString(1); } } } }
Tests met INSERT-query's worden verzonden naar een BLACKHOLE-engine met het binaire logboek uitgeschakeld, om IO en afhankelijkheid van de opslagprestaties te voorkomen. Dit geeft stabielere resultaten.
(Zonder gebruik van de blackhole-engine en het uitschakelen van het binaire logboek, zouden de uitvoeringstijden tot 10% variëren).
Benchmark is uitgevoerd op MariaDB Server 10.1.17 en MySQL Community Server 5.7.13 databases. Het volgende document toont resultaten met behulp van de 3 stuurprogramma's met MariaDB Server 10.1.17. Zie de link onderaan het document voor de volledige resultaten, inclusief die met MySQL Server 5.7.13.
MILIEU
Uitvoering (client en server) gebeurt op een enkele serverdruppel op digitalocean.com met behulp van de volgende parameters:
- Java(TM) SE Runtime Environment (build 1.8.0_101-b13) 64 bits (werkelijke laatste versie bij het uitvoeren van deze benchmark)
- Ubuntu 16.04 64 bits
- 512 MB geheugen
- 1 CPU
- database MariaDB "10.1.17-MariaDB", MySQL Community Server build "5.7.15-0ubuntu0.16.04.1"
met standaardconfiguratiebestanden en deze extra opties:- max_allowed_packet =40M #uitwisselingspakket kan tot 40mb zijn
- character-set-server =utf8 #om UTF-8 als standaard te gebruiken
- collation-server =utf8_unicode_ci #om UTF-8 als standaard te gebruiken
Wanneer "ver" wordt aangegeven, worden benchmarks uitgevoerd met afzonderlijke client en server op 2 identieke hosts in hetzelfde datacenter met een gemiddelde ping van 0,350 ms.
RESULTATEN VOORBEELD UITLEG
Benchmark Score Error Units BenchmarkSelect1RowPrepareText.mariadb 62.715 ± 2.402 µs/op BenchmarkSelect1RowPrepareText.mysql 88.670 ± 3.505 µs/op BenchmarkSelect1RowPrepareText.drizzle 78.672 ± 2.971 µs/op
Dit betekent dat deze eenvoudige query gemiddeld 62,715 microseconden in beslag neemt met het MariaDB-stuurprogramma met een variatie van ± 2,402 microseconden voor 99,9% van de vragen.
Dezelfde uitvoering met behulp van de drizzle-driver duurt gemiddeld 88,670 microseconden, en 78.672 microseconden bij gebruik van MySQL-connector (kortere uitvoeringstijd, hoe beter).
De weergegeven percentages worden ingesteld op basis van het eerste mariadb-resultaat als referentie (100%), waardoor andere resultaten gemakkelijk kunnen worden vergeleken.
PRESTATIEVERGELIJKINGEN
De benchmark test de prestaties van de 3 belangrijkste verschillende gedragingen met behulp van dezelfde lokale database (dezelfde server) en een verre database (een andere identieke server) op hetzelfde datacenter met een gemiddelde ping van 0,450 ms
Ander gedrag:
Tekstprotocol
Dit komt overeen met de optie useServerPrepStmts uitgeschakeld.
Query's worden rechtstreeks naar de server gestuurd, waarbij de opgeschoonde parameters worden vervangen aan de clientzijde.
Gegevens worden als tekst verzonden. Voorbeeld:een tijdstempel wordt verzonden als tekst "1970-01-01 00:00:00.000500" met 26 bytes
Binair protocol
Dit komt overeen met de optie useServerPrepStmts ingeschakeld (standaardimplementatie op MariaDB-stuurprogramma).
Gegevens worden binair verzonden. Voorbeeld tijdstempel "1970-01-01 00:00:00.000500" wordt verzonden met 11 bytes.
Er zijn tot 3 uitwisselingen met de server voor één vraag:
- PREPARE – Bereidt de verklaring voor uitvoering voor.
- UITVOEREN – Parameters verzenden
- DEALLOCATE PREPARE – Geeft een voorbereide verklaring uit.
Zie de documentatie voor het voorbereiden van de server voor meer informatie.
PREPARE-resultaten worden opgeslagen in de cache aan de bestuurderszijde (standaardgrootte 250). Als Prepare al in de cache is, wordt PREPARE niet uitgevoerd, DEALLOCATE wordt alleen uitgevoerd als PREPARE niet meer wordt gebruikt en niet in de cache. Dat betekent dat sommige query-uitvoeringen 3 retourvluchten zullen hebben, maar sommige slechts één retourvlucht, waarbij een PREPARE-ID en parameters worden verzonden.
Herschrijven
Dit komt overeen met de optie rewriteBatchedStatements ingeschakeld.
Rewrite gebruikt het tekstprotocol en betreft alleen batches. Het stuurprogramma zal de zoekopdracht herschrijven voor snellere resultaten.
Voorbeeld:
Invoegen in ab (i) waarden (?) waarbij de eerste batchwaarden [1] en [2] worden herschreven in
Invoegen in ab (i) waarden (1), (2).
Als de query niet kan worden herschreven in "multi-waarden", zal herschrijven gebruik maken van meerdere query's:
Invoegen in tabel (col1) waarden (?) op dubbele sleutel update col2=? met waarden [1,2] en [2,3] worden herschreven tot
Invoegen in tabel(col1) waarden (1) op dubbele sleutel update col2=2;Invoegen in tabel(col1) waarden (3) op dubbele sleutel update col2=4
Nadelen van deze optie zijn:
- ID's voor automatische verhoging kunnen niet worden opgehaald metStatement.html#getGeneratedKeys().
- Meerdere zoekopdrachten in één uitvoering zijn ingeschakeld. Dat is geen probleem voorPreparedStatement, maar als de app Statement gebruikt, kan dat een verslechtering van de beveiliging zijn (SQL-injectie).
* MariaDB en MySQL hebben die 3 gedragingen geïmplementeerd, besprenkel alleen het tekstprotocol.
BENCHMARK-RESULTATEN
MariaDB-stuurprogrammaresultaten
ENKELE SELECTIE QUERY
private String request = "SELECT CAST(? as char character set utf8)"; public String select1RowPrepare(Connection connection, MyState state) throws SQLException { try (PreparedStatement preparedStatement = connection.prepareStatement(request)) { preparedStatement.setString(1, state.insertData[state.counter++]); //a random 100 bytes. try (ResultSet rs = preparedStatement.executeQuery()) { rs.next(); return rs.getString(1); } } }
LOCAL DATABASE: BenchmarkSelect1RowPrepareHit.mariadb 58.267 ± 2.270 µs/op BenchmarkSelect1RowPrepareMiss.mariadb 118.896 ± 5.500 µs/op BenchmarkSelect1RowPrepareText.mariadb 62.715 ± 2.402 µs/op
DISTANT DATABASE: BenchmarkSelect1RowPrepareHit.mariadb 394.354 ± 13.102 µs/op BenchmarkSelect1RowPrepareMiss.mariadb 709.843 ± 31.090 µs/op BenchmarkSelect1RowPrepareText.mariadb 422.215 ± 15.858 µs/op
Wanneer het PREPARE-resultaat voor deze exacte zoekopdracht al in de cache staat (cachetreffer), zal de zoekopdracht sneller zijn (7,1% in dit voorbeeld) dan bij het gebruik van tekstprotocol. Vanwege het extra verzoek om PREPARE- en DEALLOCATE-uitwisselingen te doen, is cachemisser 68,1% langzamer.
Dit benadrukt de voor- en nadelen van het gebruik van een binair protocol. Cache HIT is belangrijk.
EEN VRAAG INVOEREN
private String request = "INSERT INTO blackholeTable (charValue) values (?)"; public boolean executeOneInsertPrepare(Connection connection, String[] datas) throws SQLException { try (PreparedStatement preparedStatement = connection.prepareStatement(request)) { preparedStatement.setString(1, datas[0]); //a random 100 byte data return preparedStatement.execute(); } }
LOCAL DATABASE: BenchmarkOneInsertPrepareHit.mariadb 61.298 ± 1.940 µs/op BenchmarkOneInsertPrepareMiss.mariadb 130.896 ± 6.362 µs/op BenchmarkOneInsertPrepareText.mariadb 68.363 ± 2.686 µs/op
DISTANT DATABASE: BenchmarkOneInsertPrepareHit.mariadb 379.295 ± 17.351 µs/op BenchmarkOneInsertPrepareMiss.mariadb 802.287 ± 24.825 µs/op BenchmarkOneInsertPrepareText.mariadb 415.125 ± 14.547 µs/op
Resultaten voor INSERT's zijn vergelijkbaar met de resultaten van SELECT.
BATCH:1000 VRAAG INVOEREN
private String request = "INSERT INTO blackholeTable (charValue) values (?)"; public int[] executeBatch(Connection connection, String[] data) throws SQLException { try (PreparedStatement preparedStatement = connection.prepareStatement(request)) { for (int i = 0; i < 1000; i++) { preparedStatement.setString(1, data[i]); //a random 100 byte data preparedStatement.addBatch(); } return preparedStatement.executeBatch(); } }
LOCAL DATABASE: PrepareStatementBatch100InsertPrepareHit.mariadb 5.290 ± 0.232 ms/op PrepareStatementBatch100InsertRewrite.mariadb 0.404 ± 0.014 ms/op PrepareStatementBatch100InsertText.mariadb 6.081 ± 0.254 ms/op
DISTANT DATABASE: PrepareStatementBatch100InsertPrepareHit.mariadb 7.639 ± 0.476 ms/op PrepareStatementBatch100InsertRewrite.mariadb 1.164 ± 0.037 ms/op PrepareStatementBatch100InsertText.mariadb 8.148 ± 0.563 ms/op
Het gebruik van een binair protocol is hier belangrijker, met resultaten die 13% sneller zijn dan het gebruik van een tekstprotocol.
Bijlagen worden in bulk verzonden en de resultaten worden asynchroon gelezen (dat komt overeen met de optie om BatchMultiSend te gebruiken). Deze toestemming om verre resultaten te hebben met prestaties niet ver van die lokale.
Rewrite heeft verbazingwekkend goede prestaties, maar heeft geen auto-increment-id's. Als je id's niet meteen nodig hebt en geen ORM gebruikt, is deze oplossing de snelste. Sommige ORM's staan configuratie toe om reeksen intern af te handelen om increment-id's te leveren, maar die reeksen worden niet gedistribueerd en werken dus niet op clusters.
VERGELIJKING MET ANDERE BESTUURDERS
SELECT-query met resultaat van één rij
BenchmarkSelect1RowPrepareHit.mariadb 58.267 ± 2.270 µs/op BenchmarkSelect1RowPrepareHit.mysql 73.789 ± 1.863 µs/op BenchmarkSelect1RowPrepareMiss.mariadb 118.896 ± 5.500 µs/op BenchmarkSelect1RowPrepareMiss.mysql 150.679 ± 4.791 µs/op BenchmarkSelect1RowPrepareText.mariadb 62.715 ± 2.402 µs/op BenchmarkSelect1RowPrepareText.mysql 88.670 ± 3.505 µs/op BenchmarkSelect1RowPrepareText.drizzle 78.672 ± 2.971 µs/op BenchmarkSelect1RowPrepareTextHA.mariadb 64.676 ± 2.192 µs/op BenchmarkSelect1RowPrepareTextHA.mysql 137.289 ± 4.872 µs/op
HA staat voor "High Availability" bij gebruik van de Master-Slave-configuratie
(verbindings-URL is "jdbc:mysql:replication://localhost:3306,localhost:3306/testj").
Deze resultaten zijn te wijten aan veel verschillende implementatiekeuzes. Hier zijn enkele redenen die tijdsverschillen verklaren:
- MariaDB-stuurprogramma is geoptimaliseerd voor UTF-8, waardoor minder bytes-array hoeft te worden gemaakt, array-kopie en geheugengebruik worden vermeden.
- HA-implementatie:MariaDB- en MySQL-stuurprogramma's gebruiken een dynamische java-proxyklasse tussen Statement-objecten en sockets, waardoor failover-gedrag kan worden toegevoegd. Die toevoeging kost een overhead van 2 microseconden per query (62,715 zonder 64,676 microseconden te worden).
In de MySQL-implementatie worden bijna alle interne methoden via proxy's gebruikt, wat een overhead toevoegt voor veel methoden die niets met failover te maken hebben. een totale overhead van 50 microseconden voor elke zoekopdracht.
(Drizzle heeft geen PREPARE, noch HA-functionaliteit)
“Selecteer 1000 rijen”
private String request = "select * from seq_1_to_1000"; //using the sequence storage engine private ResultSet select1000Row(Connection connection) throws SQLException { try (Statement statement = connection.createStatement()) { try (ResultSet rs = statement.executeQuery(request)) { while (rs.next()) { rs.getString(1); } return rs; } }
BenchmarkSelect1000Rows.mariadb 244.228 ± 7.686 µs/op BenchmarkSelect1000Rows.mysql 298.814 ± 12.143 µs/op BenchmarkSelect1000Rows.drizzle 406.877 ± 16.585 µs/op
Bij het gebruik van veel gegevens wordt de meeste tijd besteed aan het lezen van de socket en het opslaan van het resultaat in het geheugen om het terug te sturen naar de klant. Als de benchmark alleen de SELECT uitvoerde zonder de resultaten te lezen, zou de uitvoeringstijd van MySQL en MariaDB gelijk zijn. Aangezien het doel van een SELECT-query is om resultaten te hebben, is het MariaDB-stuurprogramma geoptimaliseerd om resultaten terug te geven (waardoor het maken van bytes-arrays wordt vermeden).
'1000 rijen invoegen'
LOCAL DATABASE: PrepareStatementBatch100InsertPrepareHit.mariadb 5.290 ± 0.232 ms/op PrepareStatementBatch100InsertPrepareHit.mysql 9.015 ± 0.440 ms/op PrepareStatementBatch100InsertRewrite.mariadb 0.404 ± 0.014 ms/op PrepareStatementBatch100InsertRewrite.mysql 0.592 ± 0.016 ms/op PrepareStatementBatch100InsertText.mariadb 6.081 ± 0.254 ms/op PrepareStatementBatch100InsertText.mysql 7.932 ± 0.293 ms/op PrepareStatementBatch100InsertText.drizzle 7.314 ± 0.205 ms/op
DISTANT DATABASE: PrepareStatementBatch100InsertPrepareHit.mariadb 7.639 ± 0.476 ms/op PrepareStatementBatch100InsertPrepareHit.mysql 43.636 ± 1.408 ms/op PrepareStatementBatch100InsertRewrite.mariadb 1.164 ± 0.037 ms/op PrepareStatementBatch100InsertRewrite.mysql 1.432 ± 0.050 ms/op PrepareStatementBatch100InsertText.mariadb 8.148 ± 0.563 ms/op PrepareStatementBatch100InsertText.mysql 43.804 ± 1.417 ms/op PrepareStatementBatch100InsertText.drizzle 38.735 ± 1.731 ms/op
MySQL en Drizzle bulk insert zijn zoals X INSERT's:Driver verzendt 1 INSERT, wacht op het resultaat van de insert en verzend de volgende insert. De netwerklatentie tussen elke insertie vertraagt de inserties.
Bewaarprocedures
PROCEDURE OPROEP
//CREATE PROCEDURE inoutParam(INOUT p1 INT) begin set p1 = p1 + 1; end private 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); storedProc.execute(); return storedProc.getString(1); } }
BenchmarkCallableStatementWithOutParameter.mariadb 88.572 ± 4.263 µs/op BenchmarkCallableStatementWithOutParameter.mysql 714.108 ± 44.390 µs/op
MySQL- en MariaDB-implementaties verschillen volledig. Mysql-stuurprogramma zal veel verborgen zoekopdrachten gebruiken om uitvoerresultaten te verkrijgen:
SHOW CREATE PROCEDURE testj.inoutParam
om IN- en OUT-parameters te identificerenSET @com_mysql_jdbc_outparam_p1 = 1
om gegevens te verzenden volgens IN / OUT-parametersCALL testj.inoutParam(@com_mysql_jdbc_outparam_p1)
oproepprocedureSELECT @com_mysql_jdbc_outparam_p1
output resultaat lezen
MariaDB-implementatie is eenvoudig met de mogelijkheid om de OUT-parameter in het serverantwoord te hebben zonder aanvullende vragen. (Dat is de belangrijkste reden waarom het MariaDB-stuurprogramma MariaDB/MySQL-serverversie 5.5.3 of hoger vereist).
CONCLUSIE
MariaDB-stuurprogramma rockt!
Het binaire protocol heeft verschillende voordelen, maar is afhankelijk van het feit dat de PREPARE-resultaten al in de cache staan. Als applicaties veel verschillende soorten zoekopdrachten hebben en de database ver verwijderd is, is dat misschien niet de betere oplossing.
Herschrijven heeft verbluffende resultaten om gegevens in batch te schrijven
Bestuurder houdt goed ten opzichte van andere bestuurders. En er staat nog veel op stapel, maar dat is een ander verhaal.
Ruwe resultaten:
- met een MariaDB 10.1.17-database lokaal, op afstand
- met een MySQL Community Server 5.7.15-database (build 5.7.15-0ubuntu0.16.04.1) lokaal