有没有办法在使用默认会话处理程序时禁用PHP中的会话锁定?
[编辑:]或者至少有一种方法可以在调用后重启会话session_write_close()
?session_start()
如果已经将任何输出发送到浏览器,则不起作用.
你不想禁用它...如果你这样做,你可能会遇到各种奇怪的问题,你登录一个窗口,登录另一个窗口,然后结束一个不一致的状态......锁定是有原因的......
相反,如果您知道不打算在该请求中写入会话,请尽早关闭会话.一旦你开始它,你将能够从它读取整个请求(除非你重新启动它,或做一些其他特殊的事情),即使你打电话后session_write_close
.那么你要做的是检查请求是否是写请求,如果不是,请在打开后立即关闭它.请注意,如果您稍后尝试写入会话(对于Captcha或CSRF保护或其他内容),这可能会产生一些不利影响,因此请谨慎使用......
但是,我不会试图绕过它,而是努力缩短请求长度(以减少锁争用),或者对那些根本不需要会话的请求进行无cookie请求...
我有一个超过2分钟的报告页面(超过80个AJAX请求).我通过删除会话锁定将其降低到30秒以下.是的,天堂禁止你删除文件锁定,因为那时你有竞争条件.如果你不了解竞争条件而且你不知道你的会话会产生什么影响......那就不要删除文件锁定.但是,如果你知道你的会话中有什么数据,并且知道什么样的竞争条件,那么就会觉得没有数据可能会受到种族条件的不利影响而导致错误...你知道你的环境比其他人都这样做,所以去吧.
MySQL,REDIS和Memcache
此外,请注意,如果您切换到MySQL进行会话管理,那么有99%的可能性,恕我直言,您没有锁定从您阅读的时间到写入时间的行.因此,使用MySQL,您仍然具有相同的竞争条件(如果您决定锁定行,则会出现锁定问题).
根据我能找到的信息,使用PHP Redis的人正在使用一个容易出现竞争条件的非锁定应用程序......按照以下主题...他们引用速度作为他们喜欢的原因之一"特征":
https://github.com/phpredis/phpredis/issues/37
Memcached在版本3.0.4之前不支持会话锁定......因此它最初也容易出现竞争条件.
显然,随着这些选项的成功,竞争条件不是程序员面临的最大问题.
最终问题是
所有并发请求总是会受到竞争条件的限制,除非你进行文件锁定,此时它们不再是并发请求.
关于Sessions和Locking vs. Concurrency以及竞争条件的重要之处在于了解您的应用程序,了解竞争条件是否会破坏您的应用程序......并设计适合您应用程序的解决方案.如果您只是将userId存储在会话中并在所有后续请求中读取它,您可能不需要担心竞争条件.如果您存储精心设计的数据历史记录,如果事情发生故障或数据可能会丢失,则会中断数据,然后在读取和写入之间锁定文件并尽可能快地在读取后写入以限制时间文件被锁定.
最佳选择
但是,无会话API对并发请求要好得多.但是,如果您没有时间重构这样的API ......那么请继续阅读.
Stop-Gap解决方案,用于继续使用PHP会话文件并停止锁定
要继续使用PHP会话的默认方式,要停止锁定,并且基本上能够快速解决复杂问题,您可以实现PHP网站的SessionHandler示例实现.
我在生产环境中为每分钟有数万个连接的站点运行以下代码,我还没有遇到任何竞争条件问题,但我也没有存储竞争条件可能会破坏的数据.正如我所说,这段代码从2分钟到30秒内得到了一份报告......而且需要花费几分钟的时间来实施.没有要创建的MySQL模式,也没有要安装的Memcache或Redis.
对于一封信,这是PHP文档(http://php.net/manual/en/class.sessionhandlerinterface.php)上提供的示例实现,它在读取时不会锁定会话文件.
savePath = $savePath; if (!is_dir($this->savePath)) { mkdir($this->savePath, 0777); } return true; } public function close() { return true; } public function read($id) { return (string)@file_get_contents("$this->savePath/sess_$id"); } public function write($id, $data) { return file_put_contents("$this->savePath/sess_$id", $data) === false ? false : true; } public function destroy($id) { $file = "$this->savePath/sess_$id"; if (file_exists($file)) { unlink($file); } return true; } public function gc($maxlifetime) { foreach (glob("$this->savePath/sess_*") as $file) { if (filemtime($file) + $maxlifetime < time() && file_exists($file)) { unlink($file); } } return true; } }
在PHP 5.4+中,使用它就像在启动会话之前设置处理程序一样简单:
$handler = new MySessionHandler(); session_set_save_handler($handler, true); session_start();
对于较低版本的PHP,您仍然可以通过函数调用来完成...请参阅PHP文档.