php数据加锁redis php redis锁( 三 )


为什么不推荐使用 PHP 自带的 files 型 Session 处理器
在 PHP 中,默认的 Session 处理器是 files , 处理器可以用户自己实现(参见:自定义会话管理器) 。我知道的成熟的 Session 处理器还有很多:Redis、Memcached、MongoDB……
为什么不推荐使用 PHP 自带的 files 类型处理器,PHP 官方手册中给出过这样一段 Note:
无论是通过调用函数 session_start() 手动开启会话,还是使用配置项 session.auto_start 自动开启会话, 对于基于文件的会话数据保存(PHP 的默认行为)而言,在会话开始的时候都会给会话数据文件加锁, 直到 PHP 脚本执行完毕或者显式调用 session_write_close() 来保存会话数据 。在此期间,其他脚本不可以访问同一个会话数据文件 。
上述引用参见:Session 的基本用法
为了证明这段话,我们创建一下 2 个文件: 文件:session1.php
?php
session_start();
sleep(5);
var_dump($_SESSION);
?
文件:session2.php
?php
session_start();
var_dump($_SESSION);
?
在同一个浏览器中,先访问,然后在当前浏览器新的标签页立刻访问。实验发现,session1.php 等了 5 秒钟才有输出,而 session2.php 也等到了将近 5 秒才有输出 。而单独访问 session2.php 是秒开的 。在一个浏览器中访问 session1.php , 然后立刻在另外一个浏览器中访问 session2.php 。结果是 session1.php 等待 5 秒钟有输出,而 session2.php 是秒开的 。
分析一下造成这个现象的原因:上面例子中,默认使用 Cookie 来传递 session_id,而且 Cookie 的作用域是相同 。这样,在同一个浏览器中访问这 2 个地址 , 提交给服务器的 session_id 就是相同的(这样才能标记访问者,这是我们期望的效果) 。当访问 session1.php 时,PHP 根据提交的 session_id,在服务器保存 Session 文件的路径(默认为 /tmp , 通过 php.ini 中的 session.save_path 或者函数 session_save_path() 来修改)中找到了对应的 Session 文件,并对其加锁 。如果不显式调用 session_write_close(),那么直到当前 PHP 脚本执行完毕才会释放文件锁 。如果在脚本中有比较耗时的操作(比如例子中的 sleep(5)),那么另一个持有相同 session_id 的请求由于文件被锁,所以只能被迫等待,于是就发生了请求阻塞的情况 。
既然如此,在使用完 Session 后,立刻显示调用 session_write_close() 是不是就解决问题了哩?比如上面例子中,在 sleep(5) 前面调用 session_write_close() 。
确实,这样 session2.php 就不会被 session1.php 所阻塞 。但是,显示调用了 session_write_close() 就意味着将数据写到文件中并结束当前会话 。那么,在后面代码中要使用 Session 时,必须重新调用 session_start() 。
例如:
?php
session_start();
$_SESSION['name'] = 'Jing';
var_dump($_SESSION);
session_write_close();
sleep(5);
session_start();
$_SESSION['name'] = 'Mr.Jing';
var_dump($_SESSION);
?
官方给出的方案:
对于大量使用 Ajax 或者并发请求的网站而言,这可能是一个严重的问题 。解决这个问题最简单的做法是如果修改了会话中的变量,那么应该尽快调用 session_write_close() 来保存会话数据并释放文件锁 。还有一种选择就是使用支持并发操作的会话保存管理器来替代文件会话保存管理器 。
我推荐的方式是使用 Redis 作为 Session 的处理器 。
拓展阅读:
为什么不能用 memcached 存储 Session
如何使用 Redis 作为 PHP Session handler
Session 数据是什么时候被删除的
这是一道经常被面试官问起的问题 。
先看看官方手册中的说明:

推荐阅读