session_start()需要非常长的时间

时间:2012-12-07 22:32:16

标签: php performance zend-framework session profiling

Mys网站工作非常缓慢(我对其原因一无所知)。它基于Zend应用程序,我曾经制作了大约几十个这样的网站,所以我确信我的代码没问题。

我在服务器上安装了xdebugger,试图对其进行分析并猜测是什么? php :: session_start()花了48.675秒。四十八秒半!这太不可思议了!这可能是什么原因?这是常见的操作,为什么它可以执行SO多长时间?如何修复这种配置编辑的行为?通过谷歌搜索,但没有找到好的答案(几乎所有地方都有问题,但没有答案)。谢谢你!

xdebugger profiling results

9 个答案:

答案 0 :(得分:19)

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

修改:帮助您仅用于调试,暂时设置session.gc-probability禁用垃圾回收:

session.gc-probability = 0

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

P.S。在不知道原因的情况下很难提出修复建议。我的回答是为了引导您找出原因。

答案 1 :(得分:18)

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

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

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

答案 2 :(得分:6)

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

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);  

希望这有用!

答案 3 :(得分:2)

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

我这样做是为了将巨大的mysql转储上传到PHPMyAdmin并加载加载时间,或许(如上所述)很多会话文件堆积起来,因为PHP有​​空余。我认为默认值为128M。我把它翻了四倍。

答案 4 :(得分:0)

避免此问题的一种方法是让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();

END。

答案 5 :(得分:0)

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

答案 6 :(得分:0)

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

答案 7 :(得分:0)

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

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

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

答案 8 :(得分:0)

我的页面在许多 标签中打开并发会话,其中 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() 对我有用。