Я сравнивал производительность процедуры вставки/обновления/удаления в своем приложении, которую я переношу с SQL Server на MariaDB.
Эталонный огонь запускает 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
добавьте инструкции "begin" и "commit" между некоторыми строками в вашей партии. или начать транзакцию перед партией и совершить после. Это будет намного быстрее, чем тысячи отдельных заявлений.
Если вы вставляете только вставки, rewriteBatchStatements = true должен значительно ускорить его, без транзакции. Также вы можете увеличить max_packet_size до 1 ГБ, это сделает больше дозирования, может быть, вся ваша партия будет преобразована в 1 очень большую мульти-вставку.
rewriteBatchStatements
предназначен только для вставок, он не поможет с обновлениями, но есть также useBatchMultiSend
, allowMultiQueries
и другие, но в документах не упоминается, применимы ли они к обновлениям. Возможно нет. Я только что заметил useCompression
который может помочь.
connection.commit()
, транзакции будут запускаться автоматически при необходимости).