Драйвер JDBC MariaDB не выполняет пакетную загрузку обновлений по сравнению с SQL Server

0

Я сравнивал производительность процедуры вставки/обновления/удаления в своем приложении, которую я переношу с SQL Server на MariaDB.

  • Java 1.8 на локальной рабочей станции Win10 с i7 2.80GHz CPU + 16GB RAM
  • JDBC org.mariadb.jdbc: mariadb-java-client: 2.2.4
  • 10.2.12-MariaDB-log Сервер MariaDB на AWS

Эталонный огонь запускает 50 000 вставок, те же обновления и удаляет.

SQL Server через драйвер net.sourceforge.jtds JDBC обрабатывает их всех менее чем за 1 секунду.

MariaDB с драйвером MariaDB-java-client делает вставки быстрее, но обновления (и удаления) намного медленнее на 3,5 секунды.

Схема одинакова в обеих базах данных, и я предполагаю, что, поскольку вставки в MariaDB бывают быстрыми, это, вероятно, исключает проблему индексирования или неверную конфигурацию сервера.

Я попробовал несколько вариантов для строки подключения JDBC, в результате чего это стало самым быстрым:

  ?verifyServerCertificate=true\
  &useSSL=true\
  &requireSSL=true\
  &allowMultiQueries=true\
  &cachePrepStmts=true\
  &cacheResultSetMetadata=true\
  &cacheServerConfiguration=true\
  &elideSetAutoCommits=true\
  &maintainTimeStats=false\
  &prepStmtCacheSize=50000\
  &prepStmtCacheSqlLimit=204800\
  &rewriteBatchedStatements=false\
  &useBatchMultiSend=true\
  &useBatchMultiSendNumber=50000\
  &useBulkStmts=true\
  &useLocalSessionState=true\
  &useLocalTransactionState=true\
  &useServerPrepStmts=true

Производительность в mysql и с mysql-connectorj была хуже во всех случаях, чем mariadb.

Я смотрю на это уже неделю, и я думаю о том, чтобы использовать обходные пути, предложенные в моем предыдущем вопросе. Как увеличить скорость большой серии UPDATE в mySQL и SQL Server?

На всякий случай это может быть неправильная конфигурация сервера, вот что у меня есть для ключевых переменных:

key_buffer_size                16MB
innodb_buffer_pool_size        24GB (mem 30GB)
innodb_log_file_size           134MB
innodb_log_buffer_size         8MB
innodb_flush_log_at_trx_commit 0
max_allowed_packet             16MB

Мои 50 000 записей - это всего лишь небольшое количество данных - около 2 МБ. Но с синтаксисом SQL это, по-видимому, в 10 раз больше, когда он перейдет через соединение JDBC - это правильно?

Здесь SQL и объясняют планы:

Describe 'data'
+---------------+------------------+------+-----+---------------------+-------------------------------+
| Field         | Type             | Null | Key | Default             | Extra                         |
+---------------+------------------+------+-----+---------------------+-------------------------------+
| parentId      | int(10) unsigned | NO   | PRI | NULL                |                               |
| modifiedDate  | date             | NO   | PRI | NULL                |                               |
| valueDate     | date             | NO   | PRI | NULL                |                               |
| value         | float            | NO   |     | NULL                |                               |
| versionstamp  | int(10) unsigned | NO   |     | 1                   |                               |
| createdDate   | datetime         | YES  |     | current_timestamp() |                               |
| last_modified | datetime         | YES  |     | NULL                | on update current_timestamp() |
+---------------+------------------+------+-----+---------------------+-------------------------------+

INSERT INTO 'data' ('value', 'parentId', 'modifiedDate', 'valueDate') VALUES (4853.16314229298,52054,'20-Apr-18','28-Dec-18')

+------+-------------+-------+------+---------------+------+---------+------+------+-------+
| id   | select_type | table | type | possible_keys | key  | key_len | ref  | rows | Extra |
+------+-------------+-------+------+---------------+------+---------+------+------+-------+
|    1 | INSERT      | data  | ALL  | NULL          | NULL | NULL    | NULL | NULL | NULL  |
+------+-------------+-------+------+---------------+------+---------+------+------+-------+



UPDATE 'data' SET 'value' = 4853.16314229298 WHERE 'parentId' = 52054 AND 'modifiedDate' = '20-Apr-18' AND 'valueDate' = '28-Dec-18'

+------+-------------+-------+-------+---------------+---------+---------+------+------+-------------+
| id   | select_type | table | type  | possible_keys | key     | key_len | ref  | rows | Extra       |
+------+-------------+-------+-------+---------------+---------+---------+------+------+-------------+
|    1 | SIMPLE      | data  | range | PRIMARY       | PRIMARY | 10      | NULL |    1 | Using where |
+------+-------------+-------+-------+---------------+---------+---------+------+------+-------------+


DELETE FROM 'data' WHERE 'parentId' = 52054 AND 'modifiedDate' = '20-Apr-18' AND 'valueDate' = '29-Jan-16'

+------+-------------+-------+-------+---------------+---------+---------+------+------+-------------+
| id   | select_type | table | type  | possible_keys | key     | key_len | ref  | rows | Extra       |
+------+-------------+-------+-------+---------------+---------+---------+------+------+-------------+
|    1 | SIMPLE      | data  | range | PRIMARY       | PRIMARY | 10      | NULL |    1 | Using where |
+------+-------------+-------+-------+---------------+---------+---------+------+------+-------------+

[ОБНОВИТЬ]

Использование JDBC - это упрощенная версия, поэтому оправдайте любые вопиющие ошибки:

    final Connection connection = dataSource.getConnection();
    connection.setAutoCommit(false);
    try (PreparedStatement statement = connection.prepareStatement(
                 "UPDATE data SET value = ? " +
                         "WHERE parentId = ? " +
                         "AND modifiedDate = ? " +
                         "AND valueDate = ? ")) {
        // timeSeries is a list of 50,000 data points
        Arrays.stream(timeSeries)
                .forEach(ts -> {
            try {
                statement.setDouble(1, value);
                statement.setLong(2, parentId);
                statement.setDate(3, new java.sql.Date(
                        modifiedDate.getTime()));
                statement.setDate(4, new java.sql.Date(
                        valueDate.getTime()));
                statement.addBatch();
            } catch (SQLException e) {
                throw new RuntimeException(
                        "Bad batch statement handling", e);
            }
        });
        int[] results = statement.executeBatch();
        connection.commit();
    } catch (SQLException e) {
        connection.rollback();
        throw e;
    } finally {
        connection.close();
    }

У меня также есть некоторые данные из general_log, показывающие входящие вызовы JDBC, и это выглядит довольно просто - вызов "подготовить", чтобы настроить оператор, а затем отдельные обновления.

Это меня удивляет - похоже, нет дозирования:

13/06/2018 15:09    service_user_t[service_user_t] @  [9.177.2.31]  75954   298206495   Query   set autocommit=0
13/06/2018 15:09    service_user_t[service_user_t] @  [9.177.2.31]  75954   298206495   Prepare UPDATE 'data' SET 'value' = ? WHERE 'parentId' = ? AND 'modifiedDate' = ? AND 'valueDate' = ?
13/06/2018 15:09    service_user_t[service_user_t] @  [9.177.2.31]  75954   298206495   Execute UPDATE 'data' SET 'value' = ? WHERE 'parentId' = ? AND 'modifiedDate' = ? AND 'valueDate' = ?
13/06/2018 15:09    service_user_t[service_user_t] @  [9.177.2.31]  75954   298206495   Execute UPDATE 'data' SET 'value' = ? WHERE 'parentId' = ? AND 'modifiedDate' = ? AND 'valueDate' = ?
13/06/2018 15:09    service_user_t[service_user_t] @  [9.177.2.31]  75954   298206495   Execute UPDATE 'data' SET 'value' = ? WHERE 'parentId' = ? AND 'modifiedDate' = ? AND 'valueDate' = ?
13/06/2018 15:09    service_user_t[service_user_t] @  [9.177.2.31]  75954   298206495   Execute UPDATE 'data' SET 'value' = ? WHERE 'parentId' = ? AND 'modifiedDate' = ? AND 'valueDate' = ?
13/06/2018 15:09    service_user_t[service_user_t] @  [9.177.2.31]  75954   298206495   Execute UPDATE 'data' SET 'value' = ? WHERE 'parentId' = ? AND 'modifiedDate' = ? AND 'valueDate' = ?
etc
etc
  • 1
    Точно, каков твой вопрос? SQL Server отличается от MySQL или MariaDB, поэтому можно ожидать различий в производительности. Маловероятно, что есть волшебное исправление, которое приведёт производительность в соответствие с SQL Server. Итак, что вы хотите достичь с этим вопросом?
  • 0
    Нет гарантии, что любой из них будет быстрее / медленнее / одинаковым для любой данной операции.
Показать ещё 13 комментариев
Теги:
sql-server
mariadb
jdbc

1 ответ

0

добавьте инструкции "begin" и "commit" между некоторыми строками в вашей партии. или начать транзакцию перед партией и совершить после. Это будет намного быстрее, чем тысячи отдельных заявлений.

Если вы вставляете только вставки, rewriteBatchStatements = true должен значительно ускорить его, без транзакции. Также вы можете увеличить max_packet_size до 1 ГБ, это сделает больше дозирования, может быть, вся ваша партия будет преобразована в 1 очень большую мульти-вставку.

  • 0
    В первую очередь это одна транзакция. Я настрою max_packet_size. Поскольку rewriteBatchStatements предназначен только для вставок, он не поможет с обновлениями, но есть также useBatchMultiSend , allowMultiQueries и другие, но в документах не упоминается, применимы ли они к обновлениям. Возможно нет. Я только что заметил useCompression который может помочь.
  • 1
    С JDBC вы не должны вручную запускать и совершать транзакции. Это должно обрабатываться драйвером JDBC (если вам нужно зафиксировать, то вы должны использовать connection.commit() , транзакции будут запускаться автоматически при необходимости).
Показать ещё 12 комментариев

Ещё вопросы

Сообщество Overcoder
Наверх
Меню