Статьи

Команды Hibernate autocommit заставляют MySQL чрезмерно использовать дисковый ввод-вывод

Дорогие все,

Я уверен, что многие из вас используют Hibernate и MySQL, я использую его здесь и там сам. Обычно модель программирования хороша, но не секрет, что простой JDBC может быть намного быстрее. В этом посте я хотел бы обратить ваше внимание на небольшую проблему, которая вызывает Hibernate на вашем сервере MySQL.

Если вы проследите SQL, который Hibernate отправляет в базу данных MySQL, вы увидите, что Hibernate последовательно начинает каждую транзакцию с «SET autocommit = 0» и заканчивает его «commit», а затем «SET autocommit = 1». Эти операторы могут показаться безвредными, но они заставляют MySQL сбрасывать некоторое внутреннее состояние на диск. Проще говоря, каждый раз, когда Hibernate вызывает одно из этих двух утверждений, MySQL будет писать вещи, которые обычно могут не записываться. Таким образом, использование Hibernate заставляет ваш сервер MySQL опираться на диски гораздо больше, чем при использовании простого JDBC.

Я провел небольшой эксперимент, чтобы продемонстрировать это. Чтобы повторить этот эксперимент, найдите простаивающую машину с базой данных MySQL. На машине не должно быть ничего, что вызывает дисковый ввод-вывод, иначе эффект будет не таким простым, как для меня.

Сначала я послал целую серию «ВЫБЕРИТЕ 1 ИЗ ДВОЙНОГО;» Команды в приглашение MySQL. Вот так:

1
2
3
while true; do
        echo "SELECT 1 FROM DUAL;"
done | mysql

Просмотр выходных данных iostat (8) показал, что на машине нет операций ввода-вывода. top (1) действительно показал, что машина работает. Отсюда видно, что эти операторы SELECT не вызывают дисковый ввод-вывод.

Затем я добавил команды автоматической фиксации Hibernate, как показано ниже.

1
2
3
4
5
6
while true; do
        echo "SET AUTOCOMMIT=0;"
        echo "SELECT 1 FROM DUAL;"
        echo "COMMIT;"
        echo "SET AUTOCOMMIT=1;"
done | mysql

На этот раз iostat (8) показывает, что существует дисковый ввод-вывод. Сервер MySQL работает намного тяжелее, чем раньше, и в то же время обслуживает точно такие же ответы. Это только из одного потока. Ваше приложение, вероятно, будет выдавать эти операторы одновременно на нескольких потоках и соединениях, усугубляя проблему.

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

Мне не удалось найти способ объяснить Hibernate, что я не хочу, чтобы он отправлял операторы autocommit в базу данных. Вы можете отключить автокоммит в Hibernate, но это только отключит внутренний автокоммит Hibernate. Он не прекращает отправку этих команд в базу данных.

Чтение исходного кода Hibernate (в частности, источников для org.hibernate.transaction.JDBCTransaction) показывает, что в hibernate он заставляет autocommit быть ложным в соединении перед каждой транзакцией и сбрасывать его после. Это жестко закодировано. Hibernate * хочет * отключить автокоммит.

Если мой сервер MySQL будет обслуживать только приложения с поддержкой Hibernate, я мог бы рассмотреть возможность отключения режима автоматической фиксации по умолчанию для моего сервера базы данных. Кроме того, я мог бы использовать флаг elideSetAutoCommits, который может уменьшить громкость переключателей автоматической фиксации . Однако это серьезно нарушает ПОЛА . Кроме того, мой сервер обслуживает не только приложения с поддержкой Hibernate, поэтому изменение значений по умолчанию, безусловно, приведет к поломке в другом месте.

Итак, это оставляет меня в тупике. Я не могу сказать Hibernate не выдавать «SET autocommit», драйвер JDBC не будет подавлять их, и я не могу сказать MySQL игнорировать их.

Ссылка: Hibernate отправляя команды autocommit заставляет MySQL делать чрезмерный дисковый ввод-вывод от нашего партнера JCG Киса Яна на форуме монитора Java

Удачного кодирования