I have seen many places on the net that tell me that PHP Sessions are slow. I am running a fairly large site and I am wondering if instead of storing info using the sessions I would be better making my own set of session functions using MySQL to store and retreive the data from. Would this make things faster? or would I just be transferring the slowness from the server to the mysql server?

    2 months later

    I came across a similar thought, and have always had problems with PHP sessions (probably due to stupidity though). I found a class to handle mysql sessions on the net and following the basic principles used there I created my own which uses PEAR DB database access (as I use this in my site).

    Maybe you will find it usefull, maybe not.

    First the SQL:

    CREATE TABLE `sessions` (
        `id_session` varchar(32) NOT NULL default '',
        `moment` bigint(20) NOT NULL default '0',
        `name` varchar(255) NOT NULL default '',
        `data` text NOT NULL) TYPE=MyISAM;

    Now the class

    <?php
    // remove 'extends config class' if not using this as a child of a global class.  As you see the $db handler is always passed by reference.
    
    class session extends config_class {
    
     var $session_limit = 300; //I have this in my config class, it is the session timeout in seconds, this should work fine for you here
    
    //constructor function, db by reference
    function session (&$db) { 
        session_start(); 
        $id_session=$this->id(); 
    	//now delete any old sessions
    	$deletesql = "DELETE FROM sessions WHERE moment<(UNIX_TIMESTAMP()-$this->session_limit)";
    	$deleteresult = $db->query($deletesql);
    	//now see if there is already an open session				
    	$momentsql = "SELECT moment FROM sessions WHERE (id_session='$id_session') ORDER BY moment DESC";
    	$momentrow = $db->getRow($momentsql);
    	//see if there was a result
        if (!$momentrow) {
    		//make a new session
    		$insertsql = "INSERT INTO sessions VALUES('$id_session', UNIX_TIMESTAMP(), '', '')";
    		$insertresult = $db->query($insertsql);
    	}
        else {
    		//keep current session alive
    		$updatesql = "UPDATE sessions SET moment=UNIX_TIMESTAMP() WHERE (moment='$momentrow[moment]')AND(id_session='$id_session')";
    		$updateresult = $db->query($updatesql);
    	}
    } 
    
    //register a session variable e.g to save $id name will be 'id' dan data is 'abc123etc'
    function register($name, $data, &$db) { 
        $id_session=$this->id();
    	$data = serialize($data);
        $checkquery = "SELECT id_session, name FROM sessions WHERE (id_session='$id_session')AND(name='')";
    	$checkresult = $db->query($checkquery);
    	$numresults = $checkresult->numRows();
    	//now if there is a result (empty session starter) we may as well just use that row
        if (count($numresults)==1) { 
    		$updatequery = "UPDATE sessions SET name='$name', data='$data' WHERE id_session='$id_session'";
    		$updateresult = $db->query($updatequery);
        } 
        else {
    		//check if the variable is already registered if registered then update it
    		if ($this->is_registered($name, $db)) {
    			$updatequery = "UPDATE sessions SET data='$data' WHERE (id_session='$id_session') AND(name='$name')";
    			$updateresult = $db->query($updatequery);
            } 
            else {
    			//create new 
    			$insertquery = "INSERT INTO sessions VALUES($id_session, UNIX_TIMESTAMP(), '$name', '$data'";
    			$insertresult = $db->query($insertquery);
            } 
        } 
    	$checkresult->free();
    } 
    
    //unregister variable, no return
    function unregister($name, &$db) { 
        $id_session=$this->id();
    	//check if this is the last variable registered, we don't want to end the session here
    	$checkquery = "SELECT count(id_session) FROM sessions WHERE (id_session='$id_session')";
    	$checkresult = $db->getOne($checkquery);
    	if ($checkresult==1){
    	//so we need to update then
    		$updatequery = "UPDATE sessions SET name='', data='', moment=UNIX_TIMESTAMP() WHERE (id_session='$id_session') AND (name='$name')";
    		$updateresult = $db->query($updatequery);
    	}
        else {
    	//get rid of it, we have more left
    		$deletequery = "DELETE FROM session WHERE (id_session='$id_session')AND(name='$name')";
    		$deleteresult = $db->query($deletequery);
    	}
    } 
    
    
    // check if a variable is registered
    function is_registered($name, &$db) { 
        $id_session=$this->id(); 
    	//check if it is registered, if so return 1, if not return null
    	$checkquery = "SELECT id_session, name FROM sessions WHERE (id_session='$id_session')AND(name='$name')";
    	$checkresult = $db->query($checkquery);
    	$numresults = $checkresult->numRows();
    	$checkresult->free();
    	if ($numresults==1) return 1;
    	else return;
    } 
    
    //Return a value
    function get($name, &$db) { 
        $id_session=$this->id();
    	$selectquery = "SELECT data FROM sessions WHERE ((id_session='$id_session')AND(name='$name'))";
    	$data = $db->getOne($selectquery); 
        return unserialize($data); 
    } 
    
    //Get the session_id from php
    function id() { 
        return(@session_id()); 
    } 
    
    //end the session, no return
    function finish(&$db) { 
        $id_session = $this->id(); 
    	$endquery = "DELETE FROM session WHERE id_session='$id_session'";
    	$end = $db->query($endquery);
    	    @session_unset();
    			@session_destroy();
    }
    
    //Return number of active sessions at this time, lifetime decides how long until a user is not seen as online
    // this cant be longer than the session timeout, default is 5 mins
    function nb_users(&$db, $lifetime=300) {
    		$usersquery = "SELECT id_session FROM sessions WHERE moment>(UNIX_TIMESTAMP()-$this->session_limit) GROUP BY id_session";
    		$usersresult = $db->query($usersquery);
    		$numusers = $usersresult->numRows();
    		$usersresult->free();
    		return $numusers;
    	}
    
    } 
    ?>
    

      Just how much traffic are you expecting?

        Me? It's a busy commercial site.

          One of my sites gets approx 250,000 unique visitors a month, each of which views about 25 - 30 pages. It uses sessions. I've never had any problems.

            I use sessions on all of the sites I build and I've never noticed any "slow" problems.

              We may have to move to load ballanced servers a little later, so sessions are a problem, if they use the DB they are not.

                Ok, that was poor and slow, this is better, again using pearDB:

                CREATE TABLE sessions ( 
                   ses_id varchar(32) NOT NULL default '', 
                   ses_time int(11) NOT NULL default '0', 
                   ses_start int(11) NOT NULL default '0', 
                   ses_value text NOT NULL, 
                   PRIMARY KEY  (ses_id) 
                ) TYPE=MyISAM;
                <?php
                //set sessions handler on php
                session_set_save_handler (array(&$session, '_open'), 
                                          array(&$session, '_close'), 
                                          array(&$session, '_read'), 
                                          array(&$session, '_write'), 
                                          array(&$session, '_destroy'), 
                                          array(&$session, '_gc')); 
                
                
                class session
                { 
                
                            var $session_limit = 300;
                
                function session(&$db){
                //set db handler
                $this->db = $db;
                }
                /* Open session, if you have your own db connection 
                   code, put it in here! */ 
                function _open($path, $name) { 
                    if (isset($this->db)) return TRUE; 
                	else return FALSE;
                } 
                
                /* Close session */ 
                function _close() { 
                    /* This is used for a manual call of the 
                       session gc function */ 
                    $this->_gc(0); 
                    return TRUE; 
                } 
                
                /* Read session data from database */ 
                function _read($ses_id) {
                    $session_sql = "SELECT * FROM sessions WHERE ses_id = '$ses_id'"; 
                	$session_res = $this->db->getRow($session_sql); 
                    if (!$session_res) { 
                        return ''; 
                    } 
                
                   // $session_num = $session_res->numRows(); 
                    if ($session_res) { 
                       // $session_row = $session_res->fetchRow(); 
                        $ses_data = $session_res["ses_value"];
                		//$session_res->free() 
                        return $ses_data; 
                    } else { 
                        return ''; 
                    } 
                } 
                
                /* Write new data to database */ 
                function _write($ses_id, $data) { 
                    $session_sql = "UPDATE sessions SET ses_time='" . time() 
                                 . "', ses_value='$data' WHERE ses_id='$ses_id'"; 
                    $session_res = $this->db->query($session_sql); 
                	if (!$session_res) { 
                        return FALSE; 
                    } 
                    if ($this->db->affectedRows() <> 0) { 
                        return TRUE; 
                    } 
                
                    $session_sql = "INSERT INTO sessions (ses_id, ses_time, ses_start, ses_value)" 
                                 . " VALUES ('$ses_id', '" . time() 
                                 . "', '" . time() . "', '$data')"; 
                    $session_res = $this->db->query($session_sql); 
                    if (!$session_res) {     
                        return FALSE; 
                    }         else { 
                        return TRUE; 
                    } 
                } 
                
                /* Destroy session record in database */ 
                function _destroy($ses_id) { 
                    $session_sql = "DELETE FROM sessions WHERE ses_id = '$ses_id'"; 
                    $session_res = $this->db->query($session_sql); 
                    if (!$session_res) { 
                        return FALSE; 
                    }         else { 
                        return TRUE; 
                    } 
                } 
                
                /* Garbage collection, deletes old sessions */ 
                function _gc($life) { 
                    $ses_life = $this->session_limit; 
                    $session_sql = "DELETE FROM sessions WHERE ses_time < (UNIX_TIMESTAMP()-$ses_life)"; 
                    $session_res = $this->db->query($session_sql); 
                
                
                    if (!$session_res) { 
                        return FALSE; 
                    }         else { 
                        return TRUE; 
                    } 
                } 
                    /* Users online*/ 
                function users() { 
                    $users_sql = "SELECT COUNT(ses_id) FROM sessions"; 
                    $users_res = $this->db->getOne($users_sql); 
                
                    if (!$session_res) { 
                        return NULL; 
                    }         else { 
                        return $users_res; 
                    } 
                } 
                } 
                ?>
                

                To use:

                require ("sessionclass.php");
                $session = new session($db);
                session_start();

                  Originally posted by JF1980

                  require ("sessionclass.php");
                  $session = new session($db);
                  session_start();

                  [/B]

                  You wrote all that nice code and then used session_start() anyway? What was the point? You've not overridden it in that script...

                    Originally posted by onion2k
                    You wrote all that nice code and then used session_start() anyway? What was the point? You've not overridden it in that script...

                    Yes he did, by using 'session_set_save_handler'.

                    Also as to the problem of running sessions behind a load balancer, db sessions are one way to go, but so is having an area on the file system shared between your servers. This also allows you to share config files and such to guarantee that each server is identical. And if a change SHOULD arrise allows you to quickly change it across all the nodes.

                      Originally posted by onion2k
                      You wrote all that nice code and then used session_start() anyway? What was the point? You've not overridden it in that script...

                      As Sgarissta said, the 'session_set_save_handler' lets you change the default way that sessions are handled, once you have intergrated your code to this then you can use sessions as usual but behind the scenes things run in the way you want.

                      Good idea about a shared file area Sgarissta, I might use that to keep hold of config files, I would still prefer to keep sessions in the DB though.

                      Justin.

                        Originally posted by Sgarissta
                        but so is having an area on the file system shared between your servers.

                        But be warned that the internal traffic caused by your servers all jumping on the shared file area could itself become a bottleneck.

                          Now I have a real problem. It all works fine on 3 different PHP/Apache installs I have on my local network, but when I upload it to the main testing server we use on the net, I get this error:

                          Fatal error: Failed to initialize session module in /*****/application.common/common.header.php on line 18

                          This line is the session_start(); command.

                          I have set all of the ini vars to the same as my local system. Grrrr, this server is PHP 4.2.2 which may be a problem but I can't see why!

                          Any ideas peeps?

                            Write a Reply...