Подтвердить что ты не робот

Почему транзакции базы данных spring/hibernate выполняются медленнее, чем чтение-запись?

Я занимался некоторыми исследованиями в области производительности транзакций базы данных только для чтения и чтения-записи. Сервер MySQL удален, поэтому мне легко увидеть различия между различными типами транзакций. Это связано с пулом соединений, который, как я знаю, работает на основе сравнения 1-го и 2-го вызовов JDBC.

Когда я настраиваю AOP Spring для использования транзакции только для чтения в моем вызове DAO, вызовы на 30-40% медленнее по сравнению с read-write:

<!-- slower -->
<tx:method name="find*" read-only="true" propagation="REQUIRED" />

или

// slower
@Transaction(readOnly = true)

Versus:

<!-- faster -->
<tx:method name="find*" read-only="false" propagation="REQUIRED" />

или

// faster
@Transaction

Глядя на tcpdump, похоже, что транзакция только для чтения делает больше взад и вперед, разговаривая с MySQL. Здесь дамп только для чтения по сравнению с read-write.

  • Может кто-нибудь объяснить, почему вызовы только для чтения занимают больше времени. Ожидается ли это?

  • Есть ли что-то, что я делаю неправильно или что-то, что я могу сделать, чтобы улучшить их скорость, кроме улучшения сети? Просто нашел этот потрясающий пост с некоторыми хорошими рекомендациями по эффективности. Любые другие комментарии?

Большое спасибо.

4b9b3361

Ответ 1

Почему транзакции базы данных spring/hibernate для чтения работают медленнее, чем чтение-запись?

Хорошо, это была интересная поездка. Лот для меня, чтобы учиться и делиться. Некоторые из нижеследующих должны были быть очевидными, но, надеюсь, мое невежество и то, что я узнал, будут полезны для других.

Короткий ответ на вопрос № 1 состоял в том, что спящий режим запускает сеанс @Transaction(readOnly = true) с синхронным вызовом JDBC set session.transaction.read.only и заканчивается вызовом set session.transaction.read.write. Эти вызовы не отправляются при выполнении вызовов чтения и записи, поэтому сообщения только для чтения были медленнее.

Более длинный ответ на вопрос № 2 содержит следующие сведения о шагах, которые я предпринял, чтобы попытаться снизить производительность удаленной базы данных:

  • Первое, что мы сделали, это переключение нашей базы данных VPN с TCP на UDP после прочтения этой страницы оптимизации OpenVPN. Вздох. Я должен был знать об этом. Я также добавил следующие настройки в конфигурацию клиента и сервера OpenVPN. Я еще не тестировал их, чтобы понять, как/если они способствовали увеличению скорости. Накладные расходы на транзакции только для чтения снизились с 480 мс до 141 мс, но были еще больше, чем чтение-запись на 100 мс. Большая победа.

    ; Got these from: https://community.openvpn.net/openvpn/wiki/Gigabit_Networks_Linux
    proto udp
    tun-mtu 6000
    fragment 0
    mssfix 0
    
  • Внимательно изучая вывод tcpdump (tcpdump ... -X для выигрыша), я заметил, что было сделано много ненужных вызовов для автоматической фиксации и чтения/чтения-записи JDBC. Переход к более новой версии удивительной библиотеки пулов HikariCP, которую мы используем, помог с этим. В версии 2.4.1 они добавили некоторый интеллект, который уменьшил некоторые из этих вызовов. Накладные расходы на чтение до 120 мс. Чтение-запись еще на 100 мс. Ницца.

  • Brett Wooldridge, автор HikariCP, указал мне на настройки драйвера MySQL, которые могут помочь. Спасибо большое чувак. Добавление следующих параметров в наш MySQL JDBC URL говорит драйверу использовать состояние программного обеспечения соединения и не запрашивать у сервера статус.

    jdbc:mysql://.../database?useLocalSessionState=true&useLocalTransactionState=true
    

    Эти настройки заставили больше удалять синхронные команды JDBC. Накладные расходы на транзакции только для чтения снизились до 60 мс и теперь такие же, как чтение-запись. Woo hoo.

    Изменить/ПРЕДУПРЕЖДЕНИЕ: мы фактически откатились, добавив useLocalTransactionState=true после обнаружения ошибок, когда драйвер не отправлял информацию о транзакции.

  • Но, глядя больше на вывод tcpdump, я все еще видел, что параметры транзакции только для чтения/чтения-записи. Последнее исправление заключалось в том, чтобы написать настраиваемый пул обнаружения только для чтения, который выдает соединения из специального пула, если он видит первый вызов соединения, - это connection.setReadOnly(true).

    Использование этого настраиваемого пула удалило служебные данные транзакции как для чтения, так и для чтения и записи на 20 мс. Я думаю, что он в основном удалил последние из служебных вызовов транзакций JDBC. Здесь источник этого двух классов, которые я написал на моей домашней странице, написал все это. Код относительно хрупкий и полагается на Hibernate, делающий connection.setReadOnly(true) первое, но, похоже, он работает хорошо, и я тщательно документировал его в XML и коде.

Таким образом, основные накладные расходы @Transaction пошли от 480 мс до 20 мсек за пару дней работы. 100 "real life" hibernate вызывает метод dao.find(...), запущенный через 55 секунд и заканчивающийся на 4,5 секунды. Довольно удар задницу. Желание всегда было легко получить 10-кратное повышение скорости.

Надеюсь, мой опыт помогает другим.