PHP Session可能会引起并发问题
在进行Web应用程序开发的时候,人们经常会用Session存储数据。但可能有人不知道,在PHP中,Session使用不当可能会引起并发问题。
如果同一个客户端并发发送多个请求,而每个请求都使用了Session,那么PHP Session锁的存在会导致服务器串行响应这些请求,而不是并行。这是因为在默认情况下,PHP使用文件存储Session数据。对于每一个新的 Session,PHP会创建一个文件,并持续向其中写入数据。所以,每次调用session_start()方法,就会打开Session文件,并取得 文件的独占锁。这样,如果服务器脚本正在处理一个请求,而客户端又发送了一个同样需要使用Session的请求,那么后一个请求会阻塞,直至前一个请求处 理完成释放了文件上的独占锁。不过,这只限于来自同一个客户端的多个请求,也就是说,来自一个客户端的请求并不会阻塞另一个客户端的请求。
如果脚本很短,这通常没有问题。但如果脚本运行时间比较长,那就可能会产生问题。在现代Web应用程序开发中,有一个非常常见的情况,就是使用 AJAX技术在同一个页面内发送多个请求获取数据。如果这些请求都需要使用Session,那么第一个请求到达服务器后会取得Session锁,其它请求 就必须等待,所有请求将串行处理,即使它们彼此之间并没有依赖关系。这将大大增加页面的响应时间。
有一个方法可以避免这个问题,就是在使用完Session以后立即调用session_write_close()方法关闭Session。这样 Session锁就会释放,即使当前脚本还在等在处理。需要注意的是,调用该方法后,当前脚本就不能进一步操作Session了。
需要特别指出的是,本文所陈述的问题和观点只适用于使用session_start()方法的PHP默认Session管理模式。比如,有用户就指出,如果将应用程序托管在AWS EC2上,并正确配置DynamoDB,Session锁定问题就不会出现。
void session_write_close ( void )
End the current session and store session data.
Session
data is usually stored after your script terminated without the need to
call session_write_close(), but as session data is locked to prevent
concurrent writes only one script may operate on a session at any time.
When using framesets together with sessions you will experience the
frames loading one by one due to this locking. You can reduce the time
needed to load all the frames by ending the session as soon as all
changes to session variables are done.
案例:
session_start()后 对应的session文件是被锁定的,直到当前脚本结束才会解锁。
在锁定期间,令一个进程访问相同session id 要等文件解锁后session_start()才会开始。
解决办法是在 session 设置好数据后调用 session_write_close() 将数据写入文件并且结束session
例如如下代码
PHP code
<?php
session_name("STEST");
session_id("TESTID");
session_start();
$_SESSION["count"]=0;
session_write_close();
sleep(10);
echo "success";
?>
如果不加入session_write_close();
并发3个进程访问该页面,第一个进程执行10秒 第二个执行20秒 第三个执行30秒。
加入session_write_close()后
并发3个进程会同时在10秒后执行完成