这个问题很多做PHP开发朋友应该都有遇到过,一个启用了session_start 页面,由于执行时间过长。导致用户访问另外一个很简单的启用session_start页面一直阻塞着。 直到第一个页面执行完了。第二个页面才可以读取。这个就是,我们常说的,session阻塞机制。
也就是说session是有锁的,为防止并发的写会话数据,php自带的的文件保存会话数据是加了一个互斥锁(在session_start()的时候)。
程序执行session_start(),此时当前程序就开始持有锁。
程序结束,此时程序自动释放Session的锁。
如果同一个客户端同时并发发送多个请求(如ajax在页面同时发送多个请求),且脚本执行时间较长,就会导致session文件阻塞,影响性能。因为对于每个请求,PHP执行session_start(),就会取得文件独占锁,只有在该请求处理结束后,才会释放独占锁。这样,同时多个请求就会引起阻塞。
session默认以文件保存,当一个用户访问session_start页面后,这个时候,就会默认创建一个包含session_id文件名,并且这个时候,会对文件进行锁定。如果这个用户点击链接,又访问一个该站session_start网页。这是,由于session_id一样,这个页面也有读取锁定该用户存放session文件。 由于,第一个页面没有执行完,它一直锁定了该文件。 第2个页面就不能获取锁,一直处于等待状态。
这样一个看似小的问题,实际上,如果网站上面有大量用户访问,会导致session读取文件一直阻塞等待着。用户浏览器一直跟服务器保持连接,会消耗很多服务器资源。web服务器活跃连接数也会增大,可能很快就会耗费完连接资源,出现拒绝服务器。
用memcache保存用户session,相比读取文件有很大速度提升。而且可以做到多服务器共享session。确实很方便,这个时候,我们发现不会出现用文件保存session锁定清理。memcached读取时候,是共享的,不会出现等待。但是,我们会发现,memcached连接数,还是会保持着。并且,连接数会增加,如果这个时候,你设置的memcached连接数过小,你会发现,很快memcached就挂死了。 这也是,做memcache接管session时候,经常遇到问题。 有时候,web服务器很多,session(memcache)很少。发现memcache莫名其妙死掉,可能跟这个有关系。太多反映很慢的页面(启动session),会导致占用了大量memcached连接数。
其实,通过file或者session,如果处理耗时页面,都会带来服务器资源很大消耗。其实我们一般写入session或者读取时候,如果自己能够控制。用完了,就关闭掉文件锁,或者mem连接。就会自动释放资源,其实,php里面的:session_write_close,session_commit 函数就能做到改功能。我们看下下面代码执行过程:
<?php ini_set('session.save_path', './tmp/'); function open($save_path, $session_name) { echo __FUNCTION__,"<br/>"; return (true); } function close(){ echo __FUNCTION__,"<br/>"; return (true); } function read($id){ echo __FUNCTION__,"<br/>"; } function write($id, $sess_data){ echo __FUNCTION__,"<br/>"; return (true); } function destroy($id){ echo __FUNCTION__,"<br />"; return(true); } function gc($maxlifetime){ echo __FUNCTION__,"<br />"; return true; } session_set_save_handler("open", "close", "read", "write", "destroy", "gc"); register_shutdown_function('test'); function test() { echo __FUNCTION__,"<br />"; } session_start(); echo 'aaaaa',"<br />"; /* 结论->session_start开启后, 会自动执行open,read函数。 等页面执行完后,会执行shutdown函数.最后后会把session写入进去,然后执行close关闭文件 从session_start 到页面结束,会一直锁定文件或者保持连接的。 */
然后通过1个实例子来理解 , 先执行 session2.php 代码, 20秒内执行 session3.php
session2.php 文件
<?php //执行 session2.php和session3.php, /* 因为 session2.php 脚本一直没有结束,结束后 session才会执行写入和关闭操作 (看session1.php 结论), session一直被锁定了 因此执行session3.php 时会发现 一直处于阻塞状态。 */ session_start(); $_SESSION['uname'] = 'lily';//当有session写入时会阻塞 sleep(20); /* //如果用以下代码 就没问题了, 立即提交 写入关闭 session_start(); $_SESSION['uname'] = 'lemon'; session_commit(); sleep(20); */
session3.php 文件
<?php session_start(); echo $_SESSION['uname'];
总结:
//可以用 session_commit (别名 session_write_close) 立即执行写入关闭 /* session_start(); $_SESSION['aaaa'] = 1000; session_commit(); //执行commit后,直接会调用,wirte,close操作 */ /* 1.只读取session页面,建议打开后,就直接commit,这是$_SESSION变量已经生成了。 2.有对session进行写入页面,建议修改完$_SESSION后,直接调用commit 3.多次打开并且写入,这个不建议使用,比较打开文件,写入都是耗费时间的。如果能一次搞定的,就不要做多次了。 除非,中间执行很耗时的业务。 后记:其实,使用完session,随手commit也不是坏事,养成习惯后。可以节省性能,减少服务器开销。是个不错选择!欢迎大家交流! * /
最后更新于 2019年4月3日