22
Июл
2021

HikariPool и Firebird: снижение производительности при многопоточности

Дано: служба на джаве, посылающая в базу запросы (insert и update, иногда ещё select для проверки вставленного). Для соединения используем Hikari. Его инициализация:

HikariConfig config = new HikariConfig();
config.setJdbcUrl(connectionString + String.format(";poolId=%s", poolId));
config.setMinimumIdle(minConn);
config.setMaximumPoolSize(maxConn);
config.setConnectionTimeout(30 * 1000);
config.setAutoCommit(false);
config.setMaxLifetime(0);
config.setIdleTimeout(600 * 1000);
config.setTransactionIsolation("TRANSACTION_READ_COMMITTED");
if (connectionString.startsWith("jdbc:firebirdsql")) {
    Properties props = new Properties();
    props.setProperty("TRANSACTION_READ_COMMITTED", "isc_tpb_read_committed,isc_tpb_rec_version,isc_tpb_wait");
    config.setDataSourceProperties(props);
}
connectionPool = new HikariPool(config);

Вот как пересылаем запрос в базу:

connection = getConnectionPool().getConnection();
preparedStatement = connection.prepareStatement(strQuery);
preparedStatement.setFetchSize(fetchSize);
if (strQuery.startsWith("exec")) {
    preparedStatement.executeUpdate();
} else {
    resultSet = preparedStatement.executeQuery();
}
if (resultSet != null && resultSet.next()) {
    //...
}

//закрытие всего. всё обёрнуто try-catch так, что дальше некуда
if (resultSet != null) {
    try {
        if (!resultSet.isClosed()) {
            try {
                resultSet.clearWarnings();
            } catch (SQLException ex) {
                logger.warn();
            }
            resultSet.close();
        }
    } catch (SQLException ex) {
        logger.error();
    }
    resultSet = null;
}
if (preparedStatement != null) {
    try {
        if (!preparedStatement.isClosed()) {
            try {
                preparedStatement.clearWarnings();
            } catch (SQLException ex) {
                logger.warn();
            }
            preparedStatement.clearBatch();
            preparedStatement.close();
        }
    } catch (SQLException ex) {
        logger.error();
    }
    preparedStatement = null;
}
try {
    if (connection != null && !connection.isClosed()) {
        try {
            connection.clearWarnings();
        } catch (SQLException ex) {
            logger.warn();
        }
        connection.commit();
        connection.close();
    }
} catch (SQLException ex) {
    logger.error();
}

Вроде бы всё работает. Сложности начинаются при многопоточности. Если у нас есть только один поток, то он всё пишет влёт даже при многих тысячах запросов. Если потоков два, то отправка в базу каждого отдельного запроса замедляется. При четырёх потоках запросы на вход службы поступают быстрее, чем служба вставляет их в базу (поэтому днём копится очередь в десятки тысяч запросов, а ночью при низкой нагрузке рассасывается).

Может быть, база или драйвер jdbc не успевают прожевать? Мы попробовали ввести семафор, чтобы из четырёх потоков одновременно в базу писал только один - по идее, это должно быть равносильно одному потоку. Но нет, запросы отрабатывают так же медленно, как при четырёх потоках без семафора, и очередь копится.

Вопрос: на чём могут быть потери? Понятно, что база тут первый кандидат, но наш базист говорит, что на файрбёрде отладиться всё равно нельзя. Может, есть какой-то способ определить, кто виноват - Firebird, Java или что-то на их стыке (например, Hikari или jdbc).

Источник: https://ru.stackoverflow.com/questions/1307689/hikaripool-%D0%B8-firebird-%D1%81%D0%BD%D0%B8%D0%B6%D0%B5%D0%BD%D0%B8%D0%B5-%D0%BF%D1%80%D0%BE%D0%B8%D0%B7%D0%B2%D0%BE%D0%B4%D0%B8%D1%82%D0%B5%D0%BB%D1%8C%D0%BD%D0%BE%D1%81%D1%82%D0%B8-%D0%BF%D1%80%D0%B8-%D0%BC%D0%BD%D0%BE%D0%B3%D0%BE%D0%BF%D0%BE%D1%82%D0%BE%D1%87%D0%BD%D0%BE%D1%81%D1%82%D0%B8

Тебе может это понравится...

Добавить комментарий