Как обрабатывать ошибки «время ожидания блокировки» в сеансах на основе базы данных PHP, используя set save handler? - PullRequest
0 голосов
/ 14 марта 2012

У меня есть собственный session.set_save_handler для обработки сеансов на основе базы данных для моего приложения. Таблица базы данных имеет тип innodb. Я вижу некоторые ошибки в моем файле журнала типа (1205) Lock wait timeout exceeded; try restarting transaction с несколькими потоками, в которых приложение зависает.

Кроме того, у меня есть класс, который содержит обратные вызовы обработчика сеанса и сделал его одноэлементным классом:

 session_set_save_handler(
        array($this, "db_open"),
        array($this, "db_close"),
        array($this, "db_read"),
        array($this, "db_write"),
        array($this, "db_destroy"),
        array($this, "db_gc")
        );

В методе "db_open" я установил для autocommit значение false.

В методе "db_read" я делаю SELECT FOR UPDATE в строке с идентификатором сеанса. Итак, во время выполнения нагрузочного тестирования моего приложения. у меня также есть много вызовов ajax, делающих обновления базы данных, я заметил lock wait timeout ошибки.

В методе "db_write" я делаю явный "коммит" или "откат".

Как мне решить эту проблему? Есть предложения?

  1. Должен ли я отлавливать эти ошибки и выдавать явный «коммит» в читать или писать методы, чтобы снять блокировки и повторить попытку?
  2. Должен ли я указать более высокое значение времени ожидания для "innodb_lock_wait_timeout"

Спасибо

Ответы [ 2 ]

0 голосов
/ 17 марта 2015

Основная проблема в вашем случае заключается в том, что вы делаете коммит в методе SessionHandlerInterface :: write. Но метод записи не всегда выполняется, например, при использовании session_destroy вместо этого он вызывает метод SessionHandlerInterface :: destroy. Таким образом, ваша транзакция не зафиксирована и остается открытой и, таким образом, блокирует другие параллельные запросы к тому же сеансу дольше, чем необходимо. Правильный подход - зафиксировать транзакцию в SessionHandlerInterface :: close, которая всегда выполняется в конце.

Кстати, в Symfony есть реализация обработчика сеансов PHP для баз данных, которая поддерживает различные стратегии блокировки и поддерживает большинство систем баз данных, таких как MySQL, Postgresql, Oracle и Mssql: https://github.com/symfony/symfony/blob/2.7/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/PdoSessionHandler.php

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

  1. Перехватите ошибку и повторите транзакцию в надежде, что к тому времени блокировка будет снята. При использовании Doctrine взгляните на https://github.com/doctrine/dbal/pull/718, чтобы найти идеи и обсуждения по реализации.
  2. Увеличьте время ожидания, например, innodb_lock_wait_timeout, который может помочь в зависимости от загрузки базы данных.
  3. Игнорируйте тупик или тайм-аут, перехватывая исключение и информируя пользователя о том, что он должен обновить страницу, например, чтобы возобновить сеанс. Обычно тайм-аут блокировки происходит, когда один и тот же сеанс запрашивается много раз параллельно, то есть много параллельных запросов одного и того же пользователя. Это нестандартное поведение пользователя, которое чаще всего вызывается скриптами или тестированием производительности. Поэтому игнорирование ошибки на вашей стороне и просто отображение ошибки для пользователя может быть разумным подходом.
0 голосов
/ 15 марта 2012

У вас есть невыпущенные блокировки, потому что некоторые транзакции по какой-то причине не завершены.

Установить большее значение переменной времени ожидания блокировки InnoDB: innodb_lock_wait_timeout=300.

Затем перезапустите MySQL Server.

Если это не поможет, возможно, ваша таблица повреждена, обратитесь к этой статье: Восстановление таблицы Innodb. Повреждение

...