文档章节

PHP session并发操作及session读写锁

BearCatYN
 BearCatYN
发布于 2015/07/29 20:03
字数 974
阅读 270
收藏 0

今天我来谈谈所有的PHPer都熟悉的session。

Case

1.示例代码中分别以files,redis储存会话数据

2./session/setUserFile和/session/setUserRedis设置user_name,user_id两个key,并sleep了3s

3./session/setLoginFile和/session/setLoginRedis设置last_time一个key

4./session/indexFile和/session/indexRedis模板中两个ajax请求,/session/setUserFile和/session/setUserRedis立即执行,/session/setLoginFile和/session/setLoginRedis延迟300ms,是为了模拟同一个用户,同时在两个页面(请求)修改会话数据

Files执行结果

请求:/session/indexfile

第一次访问:


第二次访问:


请求:/session/getsessionfile

array(3) { [“user_name”]=> string(10) “xudianyang” [“user_id”]=> string(3) “123” [“last_time”]=> int(1419411695) }

以文件保存会话数据结论:/session/setUserFile执行时间为3.1s,/session/setLoginFile执行时间为2.81s(如果加上ajax延迟刚好两个请求的响应时间都是3.1s)

Redis执行结果

请求:/session/indexredis

第一次访问:


第二次访问:

请求:/session/getsessionredis

array(2) { [“user_name”]=> string(10) “xudianyang” [“user_id”]=> string(3) “123” }

为什么?

手册中有这样的描述:

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是有锁的,为防止并发的写会话数据。php自带的的文件保存会话数据是加了一个互斥锁(session_start()的时候),从而解释了上面呈现的两个请求响应时间相同。但是以redis保存会话数据时,第二个ajax虽然没有阻塞,但是会话数据并没有写入到redis,那我们追溯一下源码就有答案了。

php-5.4.14源码

默认的files的save_handler

php-5.4.14/ext/session/mod_files.c


redis的save_handler

phpredis中的redis_session.c中并无实现session读写锁的机制,那上述如何解释呢?其实如果我们将session的源码大致浏览一下,就可以解释了。因为会话在session_start之后,将会话数据就会读取到$_SESSION(在扩展内部全局变量PS(http_session_vars))中,在PHP_RSHUTDOWN_FUNCTION(session)(请求释放)时,通过php_session_flush(TSRMLS_C)将会话数据写入储存介质,也就是说会话的数据并不是实时写入到储存介质的。

这就解释了,为什么redis保存会话数据时,/session/setLoginRedis看似未将last_time写入到redis,实质是写了,但是当/session/setUserRedis请求释放时(由于最开始会话数据中并无last_time这个key),要将所有的会话数据写入到储存介质,从而覆盖了/session/setLoginRedis请求写的值,到此解释了上述的问题。

测试代码

Session.php

<?php

final class SessionController extends \Yaf\Controller_Abstract
{
    public function setUserFileAction()
    {
        session_start();
        $_SESSION['user_name'] = 'xudianyang';
        $_SESSION['user_id']   = '123';

        sleep(3);
        echo json_encode($_SESSION);
        return false;
    }

    public function setLoginFileAction()
    {
        session_start();
        $_SESSION['last_time'] = time();

        echo json_encode($_SESSION);
        return false;
    }

    public function indexFileAction()
    {
        // Auto Rend View
    }

    public function getSessionFileAction()
    {
        session_start();
        var_dump($_SESSION);

        return false;
    }

    public function setUserRedisAction()
    {
        $session = \Core\Factory::session();
        $session->set('user_name', 'xudianyang');
        $session->set('user_id', '123');

        sleep(3);
        echo json_encode($_SESSION);
        return false;
    }

    public function setLoginRedisAction()
    {
        $session = \Core\Factory::session();
        $session->set('last_time', time());

        echo json_encode($_SESSION);
        return false;
    }

    public function indexRedisAction()
    {
        // Auto Rend View
    }

    public function getSessionRedisAction()
    {
        $session = \Core\Factory::session();
        var_dump($_SESSION);

        return false;
    }
}
<!DOCTYPE html>
<html>
<head>
  <title>测试session并发锁问题</title>
  <meta charset="utf-8">
  <script type="text/javascript" src="/assets/js/jquery-1.10.2.min.js"></script>
  <script type="text/javascript">
      $.ajax({
          url: "/session/setUserFile",
          type: "get",
          dataType: "json",
          success: function(response){
              console.info(response.last_time);
          }
      });
      setTimeout(function(){
          $.ajax({
              url: "/session/setLoginFile",
              type: "get",
              dataType: "json",
              success: function(response){
                  console.info(response.last_time);
              }
          });
      }, 300);
  </script>
</head>
<body>
同时发起2两个ajax请求
</body>
</html>
<!DOCTYPE html>
<html>
<head>
  <title>测试session并发锁问题</title>
  <meta charset="utf-8">
  <script type="text/javascript" src="/assets/js/jquery-1.10.2.min.js"></script>
  <script type="text/javascript">
      $.ajax({
          url: "/session/setUserRedis",
          type: "get",
          dataType: "json",
          success: function(response){
              console.info(response.last_time);
          }
      });
      setTimeout(function(){
          $.ajax({
              url: "/session/setLoginRedis",
              type: "get",
              dataType: "json",
              success: function(response){
                  console.info(response.last_time);
              }
          });
      }, 300);
  </script>
</head>
<body>
同时发起2两个ajax请求
</body>
</html>


本文转载自:http://www.phpboy.net/2014-12/47-php-session-concurent.html

共有 人打赏支持
BearCatYN
粉丝 26
博文 158
码字总数 11947
作品 0
朝阳
程序员
私信 提问
Article-No.05 大型网站系统与Java中间件实践

1、阿姆达尔定律:S(N) = 1/(1-P) + P/N P:程序中并行部分的程序在单核上执行时间的占比 N:处理器的个数(总核心数) S(N):程序在N个处理器(总核心数)相对于单个处理器(单核)中的速度...

蓝汀华韶
2015/07/16
0
0
PHP中Session可能会引起并发问题

在进行Web应用程序开发的时候,人们经常会用Session存储数据。但可能有人不知道,在PHP中,Session使用不当可能会引起并发问题。印度医疗行业软件解决方案提供商Plus91 Technologies高级工程...

蜗牛奔跑
2015/06/29
0
0
swoole 中的锁及其应用

date: 2017-12-25 01:42:00 title: swoole 中的锁及其应用 时间不早了, 尽量言简意赅一些. 怎么快速学习锁: 锁是什么 -> 锁的概念 为什么要用锁 -> 锁的使用场景 怎么使用锁 -> show me the...

daydaygo
2017/12/26
0
0
读《大型架构技术演进的思考》的一点儿感悟

一、网站简单整体架构:网站需要储存数据、要有数据库,网站要支持客户端访问进行业务操作,要有web服务器,web服务器中包含程序代码,数据缓存。 二、网站要记录用户的状态 记录session信息...

wow863597
2016/07/14
46
0
大数据量下高并发同步的讲解(不看,保证你后悔)

对于我们开发的网站,如果网站的访问量非常大的话,那么我们就需要考虑相关的并发访问问题了。而并发问题是绝大部分的程序员头疼的问题, 但话又说回来了,既然逃避不掉,那我们就坦然面对吧...

天天顺利
2015/10/15
200
0

没有更多内容

加载失败,请刷新页面

加载更多

C++ vector和list的区别

1.vector数据结构 vector和数组类似,拥有一段连续的内存空间,并且起始地址不变。 因此能高效的进行随机存取,时间复杂度为o(1); 但因为内存空间是连续的,所以在进行插入和删除操作时,会造...

shzwork
今天
4
0
Spring之invokeBeanFactoryPostProcessors详解

Spring的refresh的invokeBeanFactoryPostProcessors,就是调用所有注册的、原始的BeanFactoryPostProcessor。 相关源码 public static void invokeBeanFactoryPostProcessors(Configu......

cregu
昨天
4
0
ibmcom/db2express-c_docker官方使用文档

(DEPRECIATED) Please check DB2 Developer-C Edition for the replacement. What is IBM DB2 Express-C ? ``IBM DB2 Express-C``` is the no-charge community edition of DB2 server, a si......

BG2KNT
昨天
4
0
Ubuntu 18.04.2 LTS nvidia-docker2 : 依赖: docker-ce (= 5:18.09.0~3-0~ubuntu-bionic)

平台:Ubuntu 18.04.2 LTS nvidia-docker2 版本:2.0.3 错误描述:在安装nvidia-docker2的时候报dpkg依赖错误 nvidia-docker2 : 依赖: docker-ce (= 5:18.09.0~3-0~ubuntu-bionic) 先看一下依......

Pulsar-V
昨天
4
0
学习笔记1-goland结构体(struct)

写在前面:若有侵权,请发邮件by.su@qq.com告知。 转载者告知:如果本文被转载,但凡涉及到侵权相关事宜,转载者需负责。请知悉! 本文永久更新地址:https://my.oschina.net/bysu/blog/3036...

不最醉不龟归
昨天
4
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部