【问题标题】:session_start() takes VERY LONG TIMEsession_start() 需要很长时间
【发布时间】:2012-11-26 03:53:05
【问题描述】:

我的网站运行缓慢(我不知道为什么)。它是基于Zend Application的,我曾经做过大约几十个这样的网站,所以我确信我的代码是可以的。

我在服务器上安装了 xdebugger,试图分析它,你猜怎么着? php::session_start() 耗时 48.675 秒。四十八秒半!这太不可思议了!这可能是什么原因?很普通的操作,为什么能执行这么久?如何解决这种行为,要编辑哪些配置?在谷歌上搜索,但没有找到好的答案(几乎到处都有问题,但没有答案)。之前谢谢!

【问题讨论】:

  • 48 秒开始一个会话??这太疯狂了!会话中有什么数据和多少数据?您是否使用任何会话处理库?将会话保存到不寻常的位置?数据库(检查您的索引)?非本地文件系统?它运行在什么样的服务器上?您在 php.ini 或 .htaccess 中有任何会话配置吗?

标签: php performance zend-framework session profiling


【解决方案1】:

session_start(会话存储在文件中)在 PHP 中被阻塞,因此如果您尝试为同一个浏览器会话(AJAX 或多个浏览器选项卡/窗口)启动多个服务器会话,则会出现此问题。每个session_start 都会等到其他会话关闭。

请看这里:http://konrness.com/php5/how-to-prevent-blocking-php-requests/

尝试从文件更改为会话的数据库存储。

【讨论】:

  • 谢谢,就是这样。 session_write_close 帮助了我。
  • 谢谢,长池也有同样的问题,所以如果脚本正在等待,在等待/睡眠之前 session_write_close,唤醒时再次 session_start。
  • 是的,就我而言,是 SSE 造成了很长时间。
【解决方案2】:

我的猜测是垃圾收集例程,它在本机 session_start() 函数内部运行。也许您已经做了一些保留许多旧会话文件的操作,例如更改了最长生命周期?或者,也许您已经决定将它们存储在数据库中是个好主意,但忘记创建合适的索引?本机 GC 例程 stat() 会检查每个会话文件是否过期。如果构建了很多文件,这将非常耗时。

编辑:为了帮助您仅用于调试,通过临时设置session.gc-probability来禁用垃圾收集:

session.gc-probability = 0

确保设置不变,我不知道 zend 框架在这里可能会做什么。

附:在不知道原因的情况下很难提出修复建议。我的回答旨在指导您找出原因。

【讨论】:

    【解决方案3】:

    我遇到了这个问题,很惊讶没有人发布这个具体的回复。可能不是,但值得检查。

    当页面正在处理时,PHP 会锁定会话文件,以便该页面可以独占访问它。想想看,sess_184c9aciqoc 文件不是数据库,所以同一个会话中的两个调用不能同时访问。因此,如果您有很多 ajax 调用,您可能会遇到“交通堵塞”。一旦你开始编写高级脚本,这是一个需要提防的问题。顺便说一句,这是一个存储时间戳数组的函数。我用它来确定会话开始是罪魁祸首:

    //time function for benchmarking
    if( function_exists('gmicrotime')){
        function gmicrotime($n=''){
            #version 1.1, 2007-05-09
            //store array of all calls
            global $mT;
            list($usec, $sec) = explode(' ',microtime());
            if(!isset($mT['_base_']))$mT['_base_']=$sec;
        $t=round((float)$usec + (float)(substr($sec,-4)),6);
        $mT['all'][]=$t;
        if($n){
            if(isset($mT['indexed'][$n])){
                //store repeated calls with same index.  If in a loop, add a $i if needed
                if(is_array($mT['indexed'][$n])){
                    $mT['indexed'][$n][]=$t;
                }else{
                    $mT['indexed'][$n]=array($mT['indexed'][$n],$t);
                }
            }else $mT['indexed'][$n]=$t;    
        }
        //return elapsed since last call (in the local array)
        $u=$mT['all'];
        if(count($u)>1){
            $mT['_total_']=$u[count($u)-1] - $u[0];
            return round(1000*($u[count($u)-1]-$u[count($u)-2]),6);
        }
    }
    gmicrotime('pageStart');
    }
    

    然后我调用如下:

    gmicrotime('beforeSessionStart');
    session_start();
    gmicrotime('afterSessionStart');
    
    do_something_slow();
    gmicrotime('afterSlowProcess');
    //etc..
    echo '<pre>';
    print_r($mT);  
    

    希望这有帮助!

    【讨论】:

    • 我的问题原来是旧的 sess_* 文件可能已被锁定。我清除了所有 sess_* 文件,这似乎可以解决问题。
    【解决方案4】:

    另一种方法可能是您在 PHP.ini 中设置了一个大的memory_limit

    我这样做是为了将大量的 mysql 转储上传到 PHPMyAdmin 并增加加载时间,也许(如上所述)现在 PHP 有空余空间堆积了很多会话文件。我认为默认是128M。我已经翻了两番。

    【讨论】:

      【解决方案5】:

      避免此问题的一种方法是让 PHP 将会话存储在数据库表中而不是文件中。

      首先,我会给你几个链接作为这个解决方案的真正功劳:

      http://www.tonymarston.net/php-mysql/session-handler.html

      http://shiflett.org/articles/storing-sessions-in-a-database

      http://culttt.com/2013/02/04/how-to-save-php-sessions-to-a-database/

      然后是我从这些读数中得出的代码实现:

      <?php
      
      class TLB_Sessions_in_Database
      {
          private $debug;
          private $dbc;
      
          function __construct()
          {
              $this->debug = false;
      
              session_set_save_handler(
                  array($this, '_open'),
                  array($this, '_close'),
                  array($this, '_read'),
                  array($this, '_write'),
                  array($this, '_destroy'),
                  array($this, '_clean')
              );
          }
      
          function _open()
          {
              if( $this->debug ) echo '_open:'.PHP_EOL;
      
              if( ($this->dbc = mysql_connect(DB_HOST, DB_USER, DB_PASSWORD)) !== false )
              {
                  $select_db = mysql_select_db(DB_NAME, $this->dbc);
                  $set_charset = mysql_set_charset(DB_CHARSET, $this->dbc);
      
                  if( $this->debug ) echo '- return: '.(( $select_db && $set_charset ) ? 'true' : 'false').PHP_EOL;
      
                  return( $select_db && $set_charset );
              }
              else
              {
                  if( $this->debug ) echo '- error: '.mysql_error($this->dbc).PHP_EOL;
              }
      
              return( false );
          }
      
          function _close()
          {
              if( $this->debug ) echo '_close:'.PHP_EOL;
      
              return( mysql_close($this->dbc) );
          }
      
          function _read($session_id)
          {
              if( $this->debug ) echo '_read:'.PHP_EOL;
      
              $session_id = mysql_real_escape_string($session_id);
      
              $sql = "SELECT `session_data` FROM `".DB_NAME."`.`php_sessions` WHERE `session_id` = '".$session_id."'";
      
              if( $this->debug ) echo '- query: '.$sql.PHP_EOL;
      
              if( ($result = mysql_query($sql, $this->dbc)) !== false )
              {
                  if( !in_array(mysql_num_rows($result), array(0, false), true) )
                  {
                      $record = mysql_fetch_assoc($result);
      
                      return( $record['session_data'] );
                  }
              }
              else
              {
                  if( $this->debug ) echo '- error: '.mysql_error($this->dbc).PHP_EOL;
              }
      
              return( '' );
          }
      
          function _write($session_id, $session_data)
          {
              if( $this->debug ) echo '_write:'.PHP_EOL;
      
              $session_id = mysql_real_escape_string($session_id);
              $session_data = mysql_real_escape_string($session_data);
      
              //$sql = "REPLACE INTO `php_sessions` (`session_id`, `last_updated`, `session_data`) VALUES ('".$session_id."', '".time()."', '".$session_data."')";
              $sql = "INSERT INTO `".DB_NAME."`.`php_sessions` (`session_id`, `date_created`, `session_data`) VALUES ('".$session_id."', NOW(), '".$session_data."') ON DUPLICATE KEY UPDATE `last_updated` = NOW(), `session_data` = '".$session_data."'";
      
              if( ($result = mysql_query($sql, $this->dbc)) === false )
              {
                  if( $this->debug ) echo '- error: '.mysql_error($this->dbc).PHP_EOL;
              }
      
              return( $result );
          }
      
          function _destroy($session_id)
          {
              if( $this->debug ) echo '_destroy:'.PHP_EOL;
      
              $session_id = mysql_real_escape_string($session_id);
      
              $sql = "DELETE FROM `".DB_NAME."`.`php_sessions` WHERE `session_id` = '".$session_id."'";
      
              if( ($result = mysql_query($sql, $this->dbc)) === false )
              {
                  if( $this->debug ) echo '- error: '.mysql_error($this->dbc).PHP_EOL;
              }
      
              return( $result );
          }
      
          function _clean($max)
          {
              if( $this->debug ) echo '_clean:'.PHP_EOL;
      
              $sql = 'DELETE FROM `'.DB_NAME.'`.`php_sessions` WHERE `last_updated` < DATE_SUB(NOW(), INTERVAL '.$max.' SECOND)';
      
              if( ($result = mysql_query($sql, $this->dbc)) === false )
              {
                  if( $this->debug ) echo '- error: '.mysql_error($this->dbc).PHP_EOL;
              }
      
              return( $result );
          }
      }
      
      new TLB_Sessions_in_Database();
      

      结束。

      【讨论】:

        【解决方案6】:

        如果您在同一页面上有多个并发 ajax 调用,这种情况可能会导致您的问题。

        【讨论】:

          【解决方案7】:

          在我的情况下,/etc/php.d/memcached.ini 中的内存缓存服务器设置不正确 Here 是有关 memcache 属性的信息,here 是如何在 memcache 中设置存储。

          【讨论】:

          • 这是一个糟糕的答案,因为它不能说明错误或正确的设置是什么。
          • @kikosoftware memcached 设置可能因您要实现的目标而异。您可以了解有关此here 的更多信息。在我的情况下,extension 缺少指向 elasticache 库的设置。要求您删除反对票
          • 感谢您添加该信息。我在您链接到的 PHP 手册页上没有看到 extension 设置。你的意思是memcached.serializer?目前我无法删除我的反对票:“您昨天最后一次对此答案投票。除非编辑此答案,否则您的投票现在已锁定。”。这很有道理。
          【解决方案8】:

          我刚遇到这个问题。 session_start 大约需要 5 秒。

          我的问题是我在它上面声明了一些变量。

          我将 session_start 移到顶部,现在需要几毫秒。

          【讨论】:

            【解决方案9】:

            我的页面在许多 标记中打开并发会话,其中 download_image.php 运行 session_start() 然后下载图像。

            在 download_image.php 中插入 session_write_close() 解决了我的问题。

            session_start();
            
            session_write_close(); 
            
            download_image();
            

            我已经尝试过 memcached 和 session_start(['read_and_close'=>true])。但只有 session_write_close() 对我有用。

            【讨论】:

              猜你喜欢
              • 2016-01-19
              • 2013-09-07
              • 2020-08-26
              • 2014-10-09
              • 2019-12-27
              • 2017-10-22
              • 2020-11-15
              • 2016-05-19
              • 2011-11-20
              相关资源
              最近更新 更多