MariaDB
 sql >> база данни >  >> RDS >> MariaDB

Производителност на драйвера на MariaDB Java Connector

ПРОИЗВОДИТЕЛСТВО НА КОНЕКТОРА MARIADB JAVA

Винаги говорим за представяне. Но нещото винаги е „Мери, не гадай!“.

Напоследък бяха направени много подобрения в производителността на MariaDB Java Connector. И така, каква е текущата производителност на драйвера?

Позволете ми да споделя резултат от сравнителен анализ на 3 драйвера jdbc, позволяващи достъп до база данни MySQL/MariaDB: DrizzleJDBC, MySQL Connector/J и MariaDB java конектор.

Версиите на драйвера са най-новата налична версия на GA към момента на писане на този блог:

  • MariaDB 1.5.3
  • MySQL 5.1.39
  • Drizzle 1.4

ЕТАРИТЕЛЪТ

JMH е рамков инструмент за микробенчмаркинг на Oracle, разработен от Oracle, доставян като инструменти за openJDK, който ще бъде официалният пакет за микробенчмарк на Java 9. Неговото отличително предимство пред другите рамки е, че е разработено от същите момчета в Oracle, които прилагат JIT (Just In Time компилация) и позволяват да се избегнат повечето капани на микро-бенчмарк.

Източник на сравнителен анализ: https://github.com/rusher/mariadb-java-driver-benchmark.

Тестовете са доста прости, ако сте запознати с java.
Пример:

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); } }публичен абстрактен клас BenchmarkSelect1RowPrepareAbstract разширява BenchmarkInit { private String request ="SELECT CAST(? като набор от символи utf8)"; public String select1RowPrepare(Connection connection, MyState state) хвърля SQLException { try (PreparedStatement readyStatement =connection.prepareStatement(request)) { readyStatement.setString(1, state.insertData[state.counter++]); опитайте (ResultSet rs =readyStatement.executeQuery()) { rs.next(); върнете rs.getString(1); } } }}

Тестовете, използващи заявките на INSERT, се изпращат към движка BLACKHOLE с деактивиран двоичен регистрационен файл, за да се избегне IO и зависимост от производителността на съхранението. Това позволява по-стабилни резултати.
(Без използването на двигателя на черната дупка и деактивирането на двоичния дневник, времето за изпълнение ще варира до 10%).

Бенчмаркът е изпълнен в бази данни MariaDB Server 10.1.17 и MySQL Community Server 5.7.13. Следният документ показва резултати при използване на 3-те драйвера с MariaDB Server 10.1.17. За пълните резултати, включително тези с MySQL Server 5.7.13, моля, вижте връзката в долната част на документа.

СРЕДА

Изпълнението (клиент и сървър) се извършва на една капчица сървър на digitalocean.com, като се използват следните параметри:

  • Java(TM) SE Runtime Environment (build 1.8.0_101-b13) 64 бита (действителна последна версия при изпълнение на този сравнителен тест)
  • Ubuntu 16.04 64 бита
  • 512 Mb памет
  • 1 процесор
  • база данни MariaDB “10.1.17-MariaDB”, MySQL Community Server build “5.7.15-0ubuntu0.16.04.1”
    използвайки конфигурационни файлове по подразбиране и тези допълнителни опции:

    • max_allowed_packet =40M #exchange пакетът може да бъде до 40mb
    • character-set-server =utf8 #за използване на UTF-8 по подразбиране
    • collation-server =utf8_unicode_ci #за използване на UTF-8 по подразбиране

Когато е указано като „отдалечено“, бенчмарковете се изпълняват с отделни клиент и сървър на 2 идентични хоста в един и същ център за данни със среден ping от 0,350 мс.

ПРИМЕРНИ ОБЯСНЕНИЯ ЗА РЕЗУЛТАТИ

Единици за грешка при бенчмарк резултатаBenchmarkSelect1RowPrepareText.mariadb 62.715 ± 2.402 µs/opBenchmarkSelect1RowPrepareText.mysql 88.670 ± 3.505 µs/opTBench.mysql 88.670 ± 3.505 µs/opTBench. 

Това означава, че тази проста заявка ще отнеме средно време от 62,715 микросекунди, използвайки драйвера MariaDB с вариация от ± 2,402 микросекунди за 99,9% от заявките.
Същото изпълнение с помощта на драйвер за дъжд ще отнеме средно време от 88,670 микросекунди и 78,672 микросекунди с помощта на MySQL конектор (по-малко време за изпълнение, толкова по-добре).

Показаните проценти се задават според първия резултат на mariadb като референтен (100%), което позволява лесно да се сравняват други резултати.

СРАВНЕНИЯ НА ЕФЕКТИВНОСТ

Бенчмаркът ще тества представянето на 3-те основни различни поведения, като се използва една и съща локална база данни (един и същ сървър) и отдалечена база данни (друг идентичен сървър) в същия център за данни със среден ping от 0,450 мс

Различно поведение:

Текстов протокол

Това съответства на опцията useServerPrepStmts деактивирана.
Заявките се изпращат директно към сървъра с подмяна на дезинфекцирани параметри, извършена от страна на клиента.
Данните се изпращат като текст. Пример:Времева марка ще бъде изпратена като текст „1970-01-01 00:00:00.000500“ с помощта на 26 байта

Двоичен протокол

Това съответства на опцията useServerPrepStmts активирана (внедряване по подразбиране в драйвер на MariaDB).
Данните се изпращат в двоичен вид. Примерно времеви печат „1970-01-01 00:00:00.000500“ ще бъде изпратен с помощта на 11 байта.

Има до 3 обмена със сървъра за една заявка :

  1. PREPARE – Подготвя оператор за изпълнение.
  2. ИЗПЪЛНЯВАНЕ – Изпращане на параметри
  3. DEALLOCATE PREPARE – Пуска подготвен оператор.

Вижте документацията за подготовка на сървъра за повече информация.

Резултатите от PREPARE се съхраняват в кеша от страната на драйвера (размер по подразбиране 250). Ако Prepare вече е в кеша, PREPARE няма да се изпълни, DEALLOCATE ще се изпълни само когато PREPARE вече не се използва и не е в кеша. Това означава, че изпълнението на някои заявки ще има 3 двупосочни пътувания, но някои ще имат само едно двупосочно пътуване, изпращайки PREPARE идентификатор и параметри.

Пренаписване

Това съответства на опцията rewriteBatchedStatements активирана.
Rewrite използва текстовия протокол и се отнася само за партиди. Драйверът ще пренапише заявката за по-бързи резултати.

Пример:
Вмъкване в ab (i) стойности (?) с първата партида стойности [1] и [2] ще бъдат пренаписани на
Вмъкване в ab (i) стойности (1), (2).

Ако заявката не може да бъде пренаписана в „multi-values“, rewrite ще използва множество заявки :
Вмъкване в таблица (col1) стойности (?) при актуализиране на дублиран ключ col2=? със стойности [1,2] и [2,3] ще бъдат пренаписани на
Вмъкване в таблица(col1) стойности (1) при актуализиране на дублиран ключ col2=2;Вмъкване в таблица(col1) стойности (3) на актуализация на дублиран ключ col2=4

Недостатъците на тази опция са:

  • Идентификаторите за автоматично увеличение не могат да бъдат извлечени чрезStatement.html#getGeneratedKeys().
  • Много заявки в едно изпълнение са активирани. Това не е проблем за PreparedStatement, но ако приложението използва изявление това може да бъде влошаване на сигурността (SQL инжекция).

* В MariaDB и MySQL са внедрени тези 3 поведения, Дръпнете само протокола Text.

РЕЗУЛТАТИ ОТ СПРАВЛЕНИЯ ОТЧЕТ

Резултати от драйвери на MariaDB

ЕДИНИЧНА ЗАЯВКА ЗА ИЗБИРАНЕ

private String request ="SELECT CAST(? as char character set utf8)";public String select1RowPrepare(Connection connection, MyState state) хвърля SQLException { try (PreparedStatement readyStatement =connection.prepareStatement(request)) { readyStatement.setString( 1, state.insertData[state.counter++]); // произволни 100 байта. опитайте (ResultSet rs =readyStatement.executeQuery()) { rs.next(); върнете rs.getString(1); } }}
ЛОКАЛНА БАЗА ДАННИ:BenchmarkSelect1RowPrepareHit.mariadb 58.267 ± 2.270 µs/opBenchmarkSelect1RowPrepareMiss.mariadb 118.896 ± 5.500 µs/opBenchmark2. 
ОТДАЛЕНА БАЗА ДАННИ:BenchmarkSelect1RowPrepareHit.mariadb 394.354 ± 13.102 µs/opBenchmarkSelect1RowPrepareMiss.mariadb 709.843 ± 31.090 µs/t1. 

Когато резултатът PREPARE за тази точна заявка вече е в кеша (попадане в кеш), заявката ще бъде по-бърза (7,1% в този пример), отколкото използването на текстов протокол. Поради допълнителната заявка за обмен PREPARE и DEALLOCATE пропускането на кеша е с 68,1% по-бавно.

Това подчертава предимствата и неудобствата от използването на двоичен протокол. Cache HIT е важен.

ЕДИНИЧНА ЗАПИСВАНЕ ЗА ВМЕСВАНЕ

private String request ="INSERT INTO blackholeTable (charValue) values ​​(?)";public boolean executeOneInsertPrepare(Connection connection, String[] datas) хвърля SQLException { try (PreparedStatement createdStatement =connection.prepareStatement)) {pripremljen(SrequestteStatement). setString(1, datas[0]); // произволни 100 байта връщат данни readyStatement.execute(); }}
ЛОКАЛНА БАЗА ДАННИ:BenchmarkOneInsertPrepareHit.mariadb 61.298 ± 1.940 µs/opBenchmarkOneInsertPrepareMiss.mariadb 130.896 ± 6.362 µs/opBenchmarkOne3.6t6.mariad. 
ОТДАЛЕНА БАЗА ДАННИ:BenchmarkOneInsertPrepareHit.mariadb 379.295 ± 17.351 µs/opBenchmarkOneInsertPrepareMiss.mariadb 802.287 ± 24.825 µs/opBertmax. 

Резултатите за INSERT са подобни на резултатите от SELECT.

ПАРТИДА:1000 ВЪВЕТЕ ЗАПИСВАНЕ

private String request ="INSERT INTO blackholeTable (charValue) values ​​(?)";public int[] executeBatch(Connection connection, String[] data) хвърля SQLException { try (PreparedStatement readyStatement =connection.prepareStatement(request)) { for (int i =0; i <1000; i++) { readyStatement.setString(1, data[i]); // произволни 100 байтови данни readyStatement.addBatch(); } върне readyStatement.executeBatch(); }}
ЛОКАЛНА БАЗА ДАННИ:PrepareStatementBatch100InsertPrepareHit.mariadb 5.290 ± 0.232 ms/opPrepareStatementBatch100InsertRewrite.mariadb 0.404 ± 0.014 ms/op.PrepareHit.mariadb 0.404 ± 0.014 ms/op. 
ОТДАЛЕНА БАЗА ДАННИ:PrepareStatementBatch100InsertPrepareHit.mariadb 7,639 ± 0,476 ms/opPrepareStatementBatch100InsertRewrite.mariadb 1,164 ± 0,037 ms/op.PrepareHit.mariadb 1,164 ± 0,037 ms/op. 

Използването на двоичен протокол тук е по-важно, резултатите са с 13% по-бързи от използването на текстов протокол.

Вмъкванията се изпращат групово и резултатите се четат асинхронно (което съответства на optionuseBatchMultiSend). Това позволява да има далечни резултати с производителност, недалеч от местните.

Rewrite има невероятно добра производителност, но няма да има идентификатори за автоматично увеличение. Ако нямате нужда от идентификатори незабавно и не използвате ORM, това решение ще бъде най-бързото. Някои ORM позволяват конфигурацията да обработва последователност вътрешно, за да предостави идентификатори на нарастване, но тези последователности не се разпределят, така че няма да работят в клъстери.

СРАВНЕНИЕ С ДРУГИ ДАЙВЕРИ

SELECT заявка с резултат от един ред

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/opBenchmarkSelect1RowPrepareText/opBenchmarkSelect1RowPrepareTextH. 

HA означава „Висока достъпност“, използвайки конфигурацията Master-Slave
(URL на връзката е „jdbc:mysql:replication://localhost:3306,localhost:3306/testj“).

Тези резултати се дължат на много различни възможности за изпълнение. Ето някои причини, които обясняват разликите във времето:

  • Драйверът на MariaDB е оптимизиран за UTF-8, позволявайки по-малко създаване на масив от байтове, избягване на копиране на масив и консумация на памет.
  • Внедряване на HA :Драйверите на MariaDB и MySQL използват динамичен Proxyclass на Java, разположен между обекти на Statement и сокети, което позволява добавяне на поведение при отказ. Тези добавки ще струват допълнителни разходи от 2 микросекунди на заявка (62.715 без да станат 64.676 микросекунди).
    В реализацията на MySQL почти всички вътрешни методи са прокси, добавяйки допълнителни разходи за много методи, които нямат нищо общо с отказване, добавяйки общо режийни разходи от 50 микросекунди за всяка заявка.

(Drizzle няма PREPARE, нито HA функционалност)

„Изберете 1000 реда“

private String request ="изберете * от seq_1_to_1000"; //използвайки механизма за съхранение на последователностиprivate ResultSet select1000Row(Connection connection) хвърля SQLException { try (изявление на декларация =connection.createStatement()) { try (ResultSet rs =statement.executeQuery(request)) { while (rs.next()) { rs.getString(1); } върне rs; } }
BenchmarkSelect1000Rows.mariadb 244.228 ± 7.686 µs/opBenchmarkSelect1000Rows.mysql 298.814 ± 12.143 µs/opBenchmarkSelect1004 ± 7.686 µs. 

Когато използвате много данни, времето се изразходва най-вече за четене от сокета и съхраняване на резултата в паметта за изпращането им обратно на клиента. Ако бенчмаркът изпълняваше само SELECT, без да чете резултатите, времето за изпълнение на MySQL и MariaDB би било еквивалентно. Тъй като целта на заявката SELECT е да има резултати, драйверът на MariaDB е оптимизиран да дава резултати (избягвайки създаването на масиви от байтове).

„Вмъкване на 1000 реда“

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

MySQL и Drizzle групово вмъкване са като X INSERT:Драйверът изпраща 1 INSERT, изчаква резултата за вмъкване и изпраща следващото вмъкване. Мрежовата латентност между всяко вмъкване ще забави вмъкванията.

Процедури за съхранение

ИЗвикване на ПРОЦЕДУРА

//СЪЗДАВАНЕ НА ПРОЦЕДУРА inoutParam(INOUT p1 INT) започва набор p1 =p1 + 1; endprivate String request ="{call inOutParam(?)}";private String callableStatementWithOutParameter(Connection connection, MyState state) хвърля SQLException { try (CallableStatement storedProc =connection.prepareCall(request)) { storedProc.setInt(request) { storedProc.setInt(V1) state; //2 storedProc.registerOutParameter(1, Types.INTEGER); storedProc.execute(); върне storedProc.getString(1); }}
BenchmarkCallableStatementWithOutParameter.mariadb 88.572 ± 4.263 µs/opBenchmarkCallableStatementWithOutParameter.mysql 714.108 ± 44.390 µs/op 

Реализациите на MySQL и MariaDB напълно се различават. Драйверът на Mysql ще използва много скрити заявки, за да получи резултат:

  • SHOW CREATE PROCEDURE testj.inoutParam за идентифициране на IN и OUT параметри
  • SET @com_mysql_jdbc_outparam_p1 = 1 за изпращане на данни според IN / OUT параметри
  • CALL testj.inoutParam(@com_mysql_jdbc_outparam_p1) процедура за повикване
  • SELECT @com_mysql_jdbc_outparam_p1 за да прочетете изходния резултат

Внедряването на MariaDB е лесно, като се използва възможност да има параметър OUT в отговора на сървъра без допълнителни заявки. (Това е основната причина, поради която драйверът на MariaDB изисква MariaDB/MySQL сървър версия 5.5.3 или по-нова).

ЗАКЛЮЧЕНИЕ

Драйверът на MariaDB е страхотен!

Двоичният протокол има различни предимства, но разчита на това, че резултатите от PREPARE вече са в кеша. Ако приложенията имат много различни видове заявки и базата данни е отдалечена, това може да не е по-доброто решение.

Rewrite има невероятни резултати за запис на данни в пакет

Шофьорът се държи добре в сравнение с други драйвери. Има още много неща, но това е друга история.

Необработени резултати:

  1. с база данни MariaDB 10.1.17 локална, отдалечена
  2. с база данни MySQL Community Server 5.7.15 (build 5.7.15-0ubuntu0.16.04.1) локална

  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Миграция от Oracle Database към MariaDB - Дълбоко гмуркане

  2. Сигнали и известия от SkySQL

  3. MariaDB JSON_SET() Обяснено

  4. Увеличаване на ефективността на заявки към база данни за MySQL - част втора

  5. Как COLLATION() работи в MariaDB