来源:https://log.zvz.im/2016/02/27/PHP-session/

https://ma.ttias.be/php-session-locking-prevent-sessions-blocking-in-requests/

PHP session 的锁机制也许不是每个人都很清楚,如果你不注意,就会造成程序运行慢的问题。 如果你能了解其背后的机制,且能预判这种机制给你的PHP程序所带来的影响,并且避免它。那么 session 阻塞问题就根本算不上什么问题。

当你调用 session_start() 时,都发生了什么

我们使用一个基本的 PHP 配置为例:当你开始一次 PHP 会话时,PHP会在 session.save_path 路径下创建一个普通的文件,默认路径为 /var/lib/php/session 或者 /tmp。所有的 session 数据都保存在这个地方。

如果你的用户还没有一个 session cookie ,那么 PHP 将产生一个新的 ID,并设置到用户机器的 cookie 中。如果是一个已访问过的用户,那么他会将 cookie 发送给你的 web 服务器,PHP 则会解析它,并且从 session.save_path 路径下加载到相应的 session 数据。
简而言之,这就是 session_start() 的所做的工作。

会话锁与并发

接下来我们举一个稍微完整一点的例子,来我们说明PHP初始化session后,各个场景下所发生的事情。

Timing PHP Code Linux/Server
0ms session_start(); 创建文件锁:/var/lib/php/session/sess_$identifier
15ms SQL查询,for循环,第三方API调用 持有session文件锁
350ms PHP脚本执行结束 session文件锁被移除

当你调用session_start()(或者PHP的session.auto_start被设置为true时,该方法会被自动调用),操作系统会锁住session文件。大多数文件锁的实现都是flock,在Linux上,它也用于防止定时任务的重复执行或者其它文件锁定工作。
在Linux机器上,一个session文件锁看起来就像这样子。

1
2
$ fuser /var/lib/php/session/sess_cdmtgg3noi8fb6j2vqkaai9ff5
/var/lib/php/session/sess_cdmtgg3noi8fb6j2vqkaai9ff5: 2768 2769 2770

fuser报告了3个进程的PID,这些进程要么正持有此文件锁,或者正在等待此文件锁的释放。

1
2
3
$ lsof /var/lib/php/session/sess_cdmtgg3noi8fb6j2vqkaai9ff5
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
php-fpm 2769 http_demo 5uW REG 253,1 0 655415 sess_cdmtgg3noi8fb6j2vqkaai9ff5

lsof可以告知你当前持有文件锁的PID以及指令。
该session的文件锁会保持到脚本执行结束或者被主动移除(后面会讲到)。这是一个读写锁:任何对session读取都必须等到锁被释放之后。
锁本身并不是问题。它保护session文件中的数据,防止多个同时写入损毁数据或者覆盖之前的数据。
但是当第二个并发的PHP执行想要获取同一个PHP会话的时候,就会造成问题了。

Timing script 1 Linux/Server script 2
0ms session_start(); script1锁定(flock)文件/var/lib/php/session/sess_$identifier session_start();被调用,但是被锁阻塞。PHP等待锁被移除。
15ms SQL查询,for循环,第三方API调用 文件锁保持不变。 脚本仍然在等待,啥都不做。
350ms script1执行结束。 script1持有的文件锁被移除。 script2仍然在等待。
360ms   script2得到新的文件锁。 script2现在可以执行它的SQL查询,for循环…
700ms   script2持有的文件锁被移除。 script2执行结束。

解释一下上面的表格:

  • 当2个PHP文件同时想要开始一个会话时,只有一个能赢且获得锁。另一个则需要等待。
  • 当它等待的时候,不会做任何事情:session_start()阻塞了之后动作的执行。
  • 一旦第一个脚本的锁被移除,第二个脚本在获得锁的同时就可以向后继续执行了。

在绝大多数场景下,这都使得PHP对于同一个用户来说,表现得像是一系列同步脚本:一个执行完成后执行下一个,没有平行的请求。即使你使用AJAX调用这些PHP脚本也无济于事。
所以,刚才两个脚本没能同时在350ms左右的时间执行完毕,第一个脚本350ms执行完毕,而第一个脚本则消耗两倍的时长执行了700ms,因为它得等第一个脚本先执行完。

可选的session处理器:redis,memcache,mysql

如果你在寻求一个快速的解决方案,觉得“我只需要把session保存在memcached里”,那么你会失望的。默认的memcached配置使用了与之前描述相同的、安全的逻辑:只要有一个PHP使用了sessions那它们就会阻塞。
如果你正在使用PHP的memcached扩展,你可以将memcached.sess_locking设置为“off”,来避免session锁。该配置项的默认值是“on”,与普通的session处理器一样会阻塞。
如果你在使用redis,那么你是幸运的,因为redis的session处理器还没有支持锁功能。用redis作为session存储后端,是没有锁的。
如果你在使用MySQL作为session后端存储(比如Drupal),你会有一个自己的实现:没有一个PHP扩展实现了使用MySQL作为session存储的功能。在你的PHP代码中会有一个函数session_set_save_handler()申明了负责session数据读取和写入的类或者方法。也就是说你的代码实现决定了session是否会产生阻塞。

PHP session锁:想要解决的问题

我对于session锁行为的看法看起过于负面了,但实际上我只是提醒你注意它的行为方式。其实锁的存在也它好的一面。
想象以下没有“session锁”的场景,当两个脚本同时处理同一个session数据时,可能引发错误:

Timing script 1 script 2
0ms session_start();session数据被读入到$_SESSION变量中 session_start();session数据被读入到$_SESSION变量中
15ms 脚本1写入session数据:$_SESSION['payment_id'] = 1; 脚本2写入session数据:$_SESSION['payment_id'] = 5;
350ms sleep(1); 脚本结束,保存session数据
450ms 脚本结束,保存session数据

session中的数据值应该是多少?
应当是脚本1的所保存的值。因为脚本2所保存的值被脚本1最后所保存的值覆盖了。

这是一个非常尴尬,而且又很难排查的并发问题。session锁可以防止这种情况发生。
绝大多数情况下,这是写session数据时才会碰到的问题。如果你有一个PHP脚本只是读取session数据(大多数ajax请求都是),你可以安全地对数据进行多次读取。
另一方面,如果你有一个长时间运行的脚本,它读取了session数据并且还会修改session数据,而另一个脚本开始执行并且读取到了旧的过时数据 — 这也可能使你的应用出错。

关闭PHP的会话锁:PHP 5.x 和 PHP 7

PHP中有一个方法叫做session_write_close()。它的功能如其名:写入session数据,关闭session文件,从而解除了session锁。你在PHP代码中,可以这样使用。

1
2
3
4
5
6
7
8
9
10
<?php
// This works in PHP 5.x and PHP 7
session_start();
$_SESSION['something'] = 'foo';
$_SESSION['yolo'] = 'swag';
session_write_close();
// Do the rest of your PHP execution below

上面的示例代码先开启了session(将session数据读到$_SESSION中),然后写入数据再解除锁。接下来,它就再也不能写入这个session文件了。如果接下来该脚本还在继续操作$_SESSION变量,那么这些变化都不会被保存下来。
从PHP 7开始,在调用session_start()的时候你可设置额外的选项。

1
2
3
4
5
<?php
session_start([
'read_and_close' => true
]);
?>

以上语法等同于:

1
2
3
4
5
<?php
session_start();
session_write_close();
?>

它先读取了session数据,然后立刻释放了锁,这样就不会阻塞其它脚本了。

PHP session锁:如何避免session阻塞PHP请求相关推荐

  1. PHP session锁、并发、覆盖问题解析

    这篇文章主要介绍了php session的锁和并发,与之相关的现象有请求阻塞.session数据丢失.session数据读不到的问题,感兴趣的小伙伴们可以参考一下 本文分享PHP的session在使用 ...

  2. oracle锁和kill session

    查看被锁定的对象 SELECT A.OWNER, A.OBJECT_NAME, B.XIDUSN, B.XIDSLOT, B.XIDSQN, B.SESSION_ID, B.ORACLE_USERNA ...

  3. php设置session共享,PHP网站session共享几种方案

    session共享问题原因于自己几个不同站之间需要实现session共享了,下面小编整理了一些session共享文章,希望对大家有帮助. 使用lvs或者nginx进行web的负载均衡时,一般都会遇到s ...

  4. spring session 退出登录 清理session

    2019独角兽企业重金招聘Python工程师标准>>> spring session 退出登录 清理session 博客分类: spring /*** Allows creating ...

  5. java session 作用范围_ssm项目session使用及其作用域问题

    这两天由于自己在前端用到ajax发起异步更新请求,发现ajax会暴露后端的接口地址,这个问题当然是避免不了的啦,前端都是明文.可怜于是就在百度.谷歌.QQ群里各种查询各种提问题,都说只能通过安全验证去 ...

  6. Spring Session - Cookie VS Session VS Token 以及 Session不一致问题的N种解决方案

    文章目录 Cookie VS Session VS Token History Cookie Session Token Session不一致问题 Session不一致解决方案 nginx sessi ...

  7. ajax得到session,Ajax如何使用Session

    在Ajax中有时会使用到Session,在aspx.cs文件这样获取: string name = Session["name"]; 但是在Ajax中就不能这样获取Session, ...

  8. Nhibernate中session的状态与session.connection.state状态的差别的解释

    在项目中用到了nhibernate,但是需要判断数据连接的状态,在NHIbernate的文档中找了半天,英文的中文的都看了一遍,都没找到相关的说明 .后来在Hibernate的论坛上才找到了一些说明. ...

  9. Cookie和Session-学习笔记03【Session快速入门、Session细节】

    Java后端 学习路线 笔记汇总表[黑马程序员] Cookie和Session-学习笔记01[Cookie_快速入门.Cookie_细节] Cookie和Session-学习笔记02[Cookie案例 ...

最新文章

  1. Ubuntu环境下docker的安装
  2. C# Levenshtein计算字符串的相似度
  3. opencv-python 使用掩模抠图
  4. Matlab 重命名
  5. eslint 无法格式化ts_VS Code Prettier + ESlint 格式化Vue代码及遇到问题
  6. 变动性算法源代码分析与使用示例(copy_backward、 transform、 replace_copy_if 等)
  7. 程序员版的「倚天屠龙」,看完泪奔!
  8. 腾讯往事:微信其实就是第四代 QQ 邮箱
  9. python stdev_Python stdev()函数的详细指南
  10. 个人计算机系统的不稳定原因,cpu使用率忽高忽低怎么办 电脑cpu使用率不稳定原因分析【详解】...
  11. Linux操作命令分类详解 - 压缩备份(四)
  12. 徐谓-科举制与中国独特的士大夫阶层
  13. oracle按顺序新增字段,Oracle 修改字段顺序的两种方法
  14. 2023南京信息工程大学计算机考研信息汇总
  15. 张俊芳电机学12章计算题以及答案
  16. 程序员转正述职报告_公司程序员试用期转正工作总结
  17. PHOTOSHOP给MM去斑的最简单方法
  18. ResRep Lossless CNN Pruning via Decoupling Remembering and Forgetting 论文学习
  19. VS2019安装失败
  20. idea出现decompiled .class file 解决方案

热门文章

  1. 中谷教育Python09~14笔记——流程控制
  2. 个人怎么给短视频配音?三个简单的小技巧,配音原来并不难
  3. 【运维常用命令】文件和目录操作命令-04-mkdir命令
  4. 谷歌Android 1.0放弃蓝牙和Gtalk
  5. 马上要填志愿了!大学的计算机各个专业的就业方向你都了解吗?
  6. MySQL中你可能忽略的COLLATION实例详解
  7. Unity官方案例-roll a ball
  8. Js下,自然数的高精度计算
  9. 关于进程igfxtray.exe和hkcmd.exe
  10. 千锋老师追忆Hadoop大数据