Последствия медленной отправки почты на производительность Битрикс, влияние использования постоянных соединений с БД (MySQL Innodb) на блокировки
Партнёры "Битрикс" готовят к запуску сайт с высокой ожидаемой посещаемостью и большой активностью по добавлению и модификации контента (инфоблоки). Конфигурация: два выделенных сервера (для Веб и БД), в качестве БД использовалась MySQL с обязательным хранением таблиц в Innodb, учитывая планируемую нагрузку.
Предварительная конфигурация сайта, Apache и MySQL проводилась в соответствии с рекомендациями курса «Конфигурирование веб-систем для оптимальной работы» и активно используя "Монитор производительности" для диагностики "тяжёлых" страниц и запросов, мониторинга и настройки параметров БД (query_cache_size, tmp_table_size, max_heap_table_size, max_tmp_tables, table_cache и т.д.). Однако, когда уже казалось, что сайт показывает хорошие результаты по производительности, проявились 2 неприятные проблемы:
периодически без системы возникающие ошибки вида MySQL Query Error: UPDATE b_stat_day SET ... [Lock wait timeout exceeded; try restarting transaction], и в этом случае все серверные процессы Apache оказывались блокированными, сайт блокирован до перезапуска MySQL.
непредсказуемое бессистемное замедление формирования отдельных страниц, которое проявлялось либо в большом времени формирования страниц - 30-60 секунд, либо даже ошибке 504 nginx timeout!
Начали разбираться с MySQL, мониторинг состояния производился командами:
mysql> show full processlist; mysql> drop table if exists innodb_lock_monitor; mysql> CREATE TABLE innodb_lock_monitor (a INT) ENGINE=INNODB; mysql> SHOW ENGINE INNODB STATUS\G; mysql> DROP TABLE innodb_lock_monitor;
Выяснилось, что перед ошибкой, которую выводил PHP, в БД происходил deadlock на одних и тех же SQL-запросах типа
INSERT INTO b_iblock_section_element...
Попытались решить проблему на уровне приложения - уменьшая количество одновременных вставок элементов инфоблоков - безрезультатно. DEADLOCK этот довольно интересного типа insert intention waiting, описание которого можно посмотреть в багах MySQL. Возникает при множественных одновременных вставках в таблицу (как раз наш случай - активная работа с инфоблоками) и, по мнению специалистов MySQL, [как бы] багом вовсе не является, а есть правильное поведение MySQL+Innodb в определённых условиях. Ну да шут с ним Интереснее показалось нам другое: судя по диагностике блокировок Innodb (которая выводится в секции TRANSACTION команды SHOW ENGINE INNODB STATUS), всякий раз при возникновении проблем, блокирующей оказывалась транзакция с тем же OS thread id, что и транзакция, которую ранее MySQL выбирал в качестве "жертвы" при разборе DEADLOCK'а и должен был откатить. В подтверждение этого предположения, проблема с возникшими блокировками решалась силовым удалением "виновного" thread'а:
MYSQL> kill thread_id
Бесплатная служба поддержки MySQL пока не сильно помогла нам в анализе причин происходящего
По совету Максима Смирнова, обратили внимание на используемое постоянное соединение с БД, которое могло быть причиной подобного поведения - см., например, обсуждение в блоге Peter Zaitsev Are PHP persistent connections evil ?. Для исключения каких бы то ни было проблем и учитывая, что в случае с MySQL новые соединения создаются быстро и незначительно влияют на общую производительность сайта, мы отключили постоянное соединение с БД:
define("DBPersistent", false); в файле dbconn.php
Блокировки больше не проявлялись.
В это же время Денис Шаромов с Максимом Смирновым обнаружили похожую периодически возникающую проблему (nginx timeout) на другом сайте, связанную с медленной работой процедуры отправки почты.
Проверили на нашем проекте - 60 секунд на отправку сообщения! Это и была причина появления обеих проблем: и Nginx timeout, и MySQL Lock! В первом случае связь очевидна, во втором - задержка отправки почтового уведомления на хите задерживала завершение транзакции по добавлению/модификации элемента инфоблока, дальше - заложенный в MySQL-Innodb DEADLOCK и, видимо, проблема с открытыми транзакциями и постоянными соединениями. Возникала проблема неожиданно, при обработке события отправки почты на хите. Администраторы разобрались с почтой, обработка почтовых событий была перенесена на cron:
1) define("BX_CRONTAB_SUPPORT", true); в dbconn.php 2) добавить в crontab вызов php -f /..../bitrix/modules/main/tools/cron_events.php
Последнюю рекомендацию, с моей точки зрения, нужно применять на всех сайтах во избежание зависимости доступности сайта от работы службы почты. Либо постоянно мониторить скорость работы почты.
Подробно и интересно написано + готовые рецепты по решению проблем: отключаем постоянное соединение, почту вешаем на крон. Спасибо! Обязательно воспользуемся вашими советами!
Также следует не забывать об используемом хостинге. Многие хостеры для отправки почты советуют использовать SmtpClient relay-hosting.HOSTER.net, который не делает тайм-ауты и, следовательно, убирает Ваши деадлоки.
Группы на сайте создаются не только сотрудниками «1С-Битрикс», но и партнерами компании. Поэтому мнения участников групп могут не совпадать с позицией компании «1С-Битрикс».