如何处理“;锁定等待超时”;使用设置保存处理程序的基于PHP数据库的会话中的错误


How to handle "lock wait timeout" errors in PHP database-based sessions using set save handler?

我有一个自定义会话.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"方法中,我在具有会话id的行上执行SELECT FOR UPDATE。因此,在对我的应用程序执行负载测试时。它也有很多进行数据库更新的ajax调用,我注意到了lock wait timeout错误。

在"db_write"方法中,我进行了显式的"提交"或"回滚"。

我该如何解决此问题?有什么建议吗?

  1. 我应该发现这些错误并在读取或写入方法以释放锁定并重试
  2. 我应该指定一个更高的超时值吗用于"innodb_lock_wait_timeout"

感谢

您有未释放的锁,因为某些事务由于某种原因没有完成。

设置InnoDB锁等待超时变量的较大值:innodb_lock_wait_timeout=300

然后重新启动MySQL Server。

如果没有帮助,也许您的表已损坏,请参阅本文:恢复Innodb表损坏

您的案例中的主要问题是在SessionHandlerInterface::write方法中执行提交。但是write方法并不总是执行的,例如,当使用session_demort时,它会调用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. 捕获错误并重试该事务,希望在那时释放锁。使用条令时,请查看https://github.com/doctrine/dbal/pull/718以供一些实施思路和讨论
  2. 增加超时,例如innodb_lock_wait_timeout,这可能会根据数据库负载而有所帮助
  3. 忽略死锁或超时,方法是捕获异常并通知用户他必须刷新页面,例如恢复会话。通常,锁定超时发生在多次并行请求同一会话时,即同一用户的多次并行请求。这是一种非标准的用户行为,更可能是由脚本或性能测试引起的。因此,忽略您一侧的错误,只向用户显示错误可能是一种合理的方法