PHP session并发及session读写锁分析

PHP这门程序设计语言简单得令人发指,那是因为PHP的作者们太神通。今天我来谈谈所有的phper都熟悉的session(会话)。

需要说明的是:

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,是为了模拟同一个用户,同时在两个页面(请求)修改会话数据

执行结果表象:

请求:/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)

请求:/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 YafController_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 = CoreFactory::session();
        $session->set('user_name', 'xudianyang');
        $session->set('user_id', '123');

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

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

        echo json_encode($_SESSION);
        return false;
    }

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

    public function getSessionRedisAction()
    {
        $session = CoreFactory::session();
        var_dump($_SESSION);

        return false;
    }
}

indexfile.phtml

<!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>

indexredis.phtml

<!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>

以上是小编为您精心准备的的内容,在的博客、问答、公众号、人物、课程等栏目也有的相关内容,欢迎继续使用右上角搜索按钮进行搜索string
, 文件
, 数据
, session
, 代码
时间
php session 并发、session并发、asp.net session 并发、session 并发问题、高并发 session,以便于您获取更多的相关知识。

时间: 2024-08-02 04:44:31

PHP session并发及session读写锁分析的相关文章

php的session读写锁例子

先看一个例子,功能: 1.点击页面中一个按钮,ajax执行php,php中用session记录执行到哪一步. 2.使用ajax轮询另一个php,获取session中数据,输出执行到哪一步. session.html 调用php执行,并输出执行到第几步 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">

《Java并发编程的艺术》-Java并发包中的读写锁及其实现分析

作者:魏鹏  本文是<Java并发编程的艺术>的样章 1. 前言 在Java并发包中常用的锁(如:ReentrantLock),基本上都是排他锁,这些锁在同一时刻只允许一个线程进行访问,而读写锁在同一时刻可以允许多个读线程访问,但是在写线程访问时,所有的读线程和其他写线程均被阻塞.读写锁维护了一对锁,一个读锁和一个写锁,通过分离读锁和写锁,使得并发性相比一般的排他锁有了很大提升. 除了保证写操作对读操作的可见性以及并发性的提升之外,读写锁能够简化读写交互场景的编程方式.假设在程序中定义一个共享

Java 并发包中的读写锁及其实现分析

1. 前言 在Java并发包中常用的锁(如:ReentrantLock),基本上都是排他锁,这些锁在同一时刻只允许一个线程进行访问,而读写锁在同一时 刻可以允许多个读线程访问,但是在写线程访问时,所有的读线程和其他写线程均被阻塞.读写锁维护了一对锁,一个读锁和一个写锁,通过分离读锁和写锁,使得 并发性相比一般的排他锁有了很大提升. 除了保证写操作对读操作的可见性以及并发性的提升之外,读写锁能够简化读写交互场景的编程方式.假设在程序中定义一个共享的数据结构用作缓存,它大部分时间提供读服务(例如:查

GO语言并发编程之互斥锁、读写锁详解_Golang

在本节,我们对Go语言所提供的与锁有关的API进行说明.这包括了互斥锁和读写锁.我们在第6章描述过互斥锁,但却没有提到过读写锁.这两种锁对于传统的并发程序来说都是非常常用和重要的. 一.互斥锁 互斥锁是传统的并发程序对共享资源进行访问控制的主要手段.它由标准库代码包sync中的Mutex结构体类型代表.sync.Mutex类型(确切地说,是*sync.Mutex类型)只有两个公开方法--Lock和Unlock.顾名思义,前者被用于锁定当前的互斥量,而后者则被用来对当前的互斥量进行解锁. 类型sy

nginx中session ticket重用Session提高https性能分析

原创文章:来自nginx中session ticket重用Session提高https性能分析 https会话建立初次使用session ticket的SSL握手流程如下: Client Server ClientHello (empty SessionTicket extension)--------> ServerHello (empty SessionTicket extension) Certificate* ServerKeyExchange* CertificateRequest*

C#使用读写锁三行代码简单解决多线程并发的问题_C#教程

在开发程序的过程中,难免少不了写入错误日志这个关键功能.实现这个功能,可以选择使用第三方日志插件,也可以选择使用数据库,还可以自己写个简单的方法把错误信息记录到日志文件. 选择最后一种方法实现的时候,若对文件操作与线程同步不熟悉,问题就有可能出现了,因为同一个文件并不允许多个线程同时写入,否则会提示"文件正在由另一进程使用,因此该进程无法访问此文件". 这是文件的并发写入问题,就需要用到线程同步.而微软也给线程同步提供了一些相关的类可以达到这样的目的,本文使用到的 System.Thr

Java并发编程之显示锁ReentrantLock和ReadWriteLock读写锁_java

在Java5.0之前,只有synchronized(内置锁)和volatile. Java5.0后引入了显示锁ReentrantLock. ReentrantLock概况 ReentrantLock是可重入的锁,它不同于内置锁, 它在每次使用都需要显示的加锁和解锁, 而且提供了更高级的特性:公平锁, 定时锁, 有条件锁, 可轮询锁, 可中断锁. 可以有效避免死锁的活跃性问题.ReentrantLock实现了 Lock接口: 复制代码 代码如下:   public interface Lock {

C#解决SQlite并发异常问题的方法(使用读写锁)_C#教程

本文实例讲述了C#解决SQlite并发异常问题的方法.分享给大家供大家参考,具体如下: 使用C#访问sqlite时,常会遇到多线程并发导致SQLITE数据库损坏的问题. SQLite是文件级别的数据库,其锁也是文件级别的:多个线程可以同时读,但是同时只能有一个线程写.Android提供了SqliteOpenHelper类,加入Java的锁机制以便调用.但在C#中未提供类似功能. 作者利用读写锁(ReaderWriterLock),达到了多线程安全访问的目标. using System; usin

.NET Session丢失解决方案与问题原因分析

.NET Session丢失解决方案与问题原因分析 判断Session是否过期: 通过BasePage或IHtttpMoudle实现 由于Asp.net程序是默认配置,所以Web.Config文件中关于Session的设定如下: < sessionState mode='InProc' stateConnectionString='tcpip=127.0.0.1:42424' sqlConnectionString='data source=127.0.0.1;Trusted_Connectio