Recent Posts

Sponsors
![]() |
Memcached for PHP SessionsDawn Rossi, 10-14-2008 |
Sessions are a great way to store data pertaining to a single end-user browsing through your site, before the user logged in.
Each user is assigned a unique ID and that ID serves as the key to a dataset, where you can store the user name, email address, referral, affiliate_id etc.
Since sessions are accessed on every page load, it is important to optimize the amount of time it takes to read/write sessions.
PHP sessions can typically be stored as either files or database records.
Files are fast, but don't allow you to scale beyond a single server.
Database records support scaling but are "expensive" to read/write.
As part of this post, I will walk you through the process of using Memcached for PHP sessions.
Memcached is a very efficient in-memory database that supports scaling to multiple machines, expiration and built-in garbage collection. We're going to hit Memcached first on all session queries. If we don't have a match, we read it from the database (once) and store the information into Memcached for later fast retrieval.
Step 1: Install Memcached
Step 2: We're going to use PHP function overrides feature and define new session handling functions. Save dbsession.php in your includes directory and replace all references to "payments" with the name of your global database.
Step 3: Create the sessions table under your global database:
CREATE TABLE sessions (
session_expire int(11) unsigned NOT NULL DEFAULT '0',
session_value longtext,
session_key char(32) NOT NULL DEFAULT '',
PRIMARY KEY (session_key)
)
Step 4: Save common_cache.php under your includes directory. This file is used by dbsession.php and serves as the wrapper class for accessing the Memcached server. Update the array at the top of common_cache.php with the ip addresses of your Memcached servers. You can have multiple Memcached servers and as long as they're all aware of each other, everything will scale gracefully.
Step 5: Include dbsession.php at the top of your php files (or add it to one of your global includes that is included everywhere). Make sure you include dbsession.php before the first call to session_start
All it takes is a single include at the top of your php files and voila - all sessions are now handled by Memcached first, then reverting to the database if no match found.
require_once("dbsession.php");
Each user is assigned a unique ID and that ID serves as the key to a dataset, where you can store the user name, email address, referral, affiliate_id etc.
Since sessions are accessed on every page load, it is important to optimize the amount of time it takes to read/write sessions.
PHP sessions can typically be stored as either files or database records.
Files are fast, but don't allow you to scale beyond a single server.
Database records support scaling but are "expensive" to read/write.
As part of this post, I will walk you through the process of using Memcached for PHP sessions.
Memcached is a very efficient in-memory database that supports scaling to multiple machines, expiration and built-in garbage collection. We're going to hit Memcached first on all session queries. If we don't have a match, we read it from the database (once) and store the information into Memcached for later fast retrieval.
Step 1: Install Memcached
Step 2: We're going to use PHP function overrides feature and define new session handling functions. Save dbsession.php in your includes directory and replace all references to "payments" with the name of your global database.
Step 3: Create the sessions table under your global database:
CREATE TABLE sessions (
session_expire int(11) unsigned NOT NULL DEFAULT '0',
session_value longtext,
session_key char(32) NOT NULL DEFAULT '',
PRIMARY KEY (session_key)
)
Step 4: Save common_cache.php under your includes directory. This file is used by dbsession.php and serves as the wrapper class for accessing the Memcached server. Update the array at the top of common_cache.php with the ip addresses of your Memcached servers. You can have multiple Memcached servers and as long as they're all aware of each other, everything will scale gracefully.
Step 5: Include dbsession.php at the top of your php files (or add it to one of your global includes that is included everywhere). Make sure you include dbsession.php before the first call to session_start
All it takes is a single include at the top of your php files and voila - all sessions are now handled by Memcached first, then reverting to the database if no match found.
require_once("dbsession.php");
![]() |
James, 10-20-2008 |
This article confuses me. Why on earth try re-inventing the wheel and writing your own session handler for memcache when you can use the one that the PECL memcache module ships with?
if(MEMCACHE_HAVE_SESSION) {
ini_set('session.save_handler','memcache');
ini_set('session.save_path', <session save path info>);
} else {
// fall back to a db session
}
Unfortunately I don't think that the author has a firm grasp of memcached and/or sessions.
if(MEMCACHE_HAVE_SESSION) {
ini_set('session.save_handler','memcache');
ini_set('session.save_path', <session save path info>);
} else {
// fall back to a db session
}
Unfortunately I don't think that the author has a firm grasp of memcached and/or sessions.
![]() |
George, 10-24-2008 |
James,
Your approach would fall back to a db session upon a memcache miss. It doesn't handle reading data from the db, and updating memcache to retain the speed advantage of using memcache. Dawn's approach is designed to use memcache in all instances and update the memcache contents from db in the case of a cache miss. The session handler in the PECL memcache does not do this.
Your approach would fall back to a db session upon a memcache miss. It doesn't handle reading data from the db, and updating memcache to retain the speed advantage of using memcache. Dawn's approach is designed to use memcache in all instances and update the memcache contents from db in the case of a cache miss. The session handler in the PECL memcache does not do this.
![]() |
Dawn Rossi, 10-31-2008 |
Thank you George,
You got it!
We have been implementing Memcached for PHP sessions for a few years now. I'd like to think we know a thing or two about how to get the most out of Memcached.
You got it!
We have been implementing Memcached for PHP sessions for a few years now. I'd like to think we know a thing or two about how to get the most out of Memcached.
![]() |
Sung, 11-18-2008 |
One more thing.
Whenever a client JUST reads session variable, sessionWrite() is called to update session expiration time. Thus, it seems that this rarely utilizes memcached.
Whenever a client JUST reads session variable, sessionWrite() is called to update session expiration time. Thus, it seems that this rarely utilizes memcached.
![]() |
Caner, 12-17-2008 |
Can we use this method on a multi-server architecture?
![]() |
John, 04-17-2009 |
We are using this exact same approach for our session handling using memcached. it's working brilliantly. However, we are receiving the occasional random logout. As if memcached returned true but didn't actually have the sessionid stored. Has this ever happened to you? Here is an example of our session read() function:
function read($sessionID) {
$db =& $this->_db;
$cache =& $this->_cache;
if($cache->get(CACHE_SESSION . $sessionID) !== false) {
return $cache->get(CACHE_SESSION . $sessionID);
} else {
$sql = "SELECT value FROM session WHERE sessionid = ? AND expiration > ?";
return $db->getOne($sql, array($sessionID, time()));
}
}
function read($sessionID) {
$db =& $this->_db;
$cache =& $this->_cache;
if($cache->get(CACHE_SESSION . $sessionID) !== false) {
return $cache->get(CACHE_SESSION . $sessionID);
} else {
$sql = "SELECT value FROM session WHERE sessionid = ? AND expiration > ?";
return $db->getOne($sql, array($sessionID, time()));
}
}
![]() |
Phil, 05-03-2009 |
Interesting article, however wouldn't the fact you are still doing at least a write to the database every time kinda defeat the object of using memcache? That database write will always be the bottleneck for this script. The PECL memcache package supports writing PHP sessions to multiple memcache servers which is probably best for a redundant solution.
![]() |
Mike Peters, 06-09-2009 |
Caner -
Absolutely. This approach is specifically designed to support multi server architecture.
John -
We're not seeing any issues with this method. You may want to check your code and make sure you are calling session set save handler in all places. Otherwise, your web server will be reverting to 'files' session handling. That could possibly explain what you're seeing.
Phil -
Memcached is first checked for the session value. If Memcached has it, no db access is ever initiated.
Absolutely. This approach is specifically designed to support multi server architecture.
John -
We're not seeing any issues with this method. You may want to check your code and make sure you are calling session set save handler in all places. Otherwise, your web server will be reverting to 'files' session handling. That could possibly explain what you're seeing.
Phil -
Memcached is first checked for the session value. If Memcached has it, no db access is ever initiated.
![]() |
Mike, 06-23-2009 |
Do you think we can have an example of using this ?
As in a test login form maybe with the test table for logins/etc
As in a test login form maybe with the test table for logins/etc
![]() |
Anton, 07-18-2009 |
A short treaty on high-performance session caching.
Using memcached for sessions is naive (faster is not always better) and prone to huge issues with random log-outs. In-fact, as your site become more critical and has higher traffic then although it is counter intuitive you need to abandon RAM based caching solutions as users will quickly overwhelm the available storage. Database sessions provide a reasonably robust solution for badly designed server farms that do not use consistent server forwarding for a given user. Consistently forwarding a given user to a given server using file sessions is more efficient, with the only issue being the potential loss of the server and hence a random logout when another working server is selected - but that is a 'blue-moon' issue. However, this issue is tiny in comparison to the loss of the session server database storage which would take down the entire site.
There is also the option of using MCache sessions which access a session server. Distributed session servers are the best option for high-traffic multi-server installations and require only simple server forwarding rules. Distributed = robust, and caching is multi-level moving from RAM to disc and eventually to oblivion (GC) as usage of the session data ages through lack of access.
In the end each solution has a number of points of failure. Memcached is better used for less critical and far more efficient caching of data that can easily be recreated without user intervention. An extra moment to re-cache a lost page or query result is of no consequence to a user; having to log in again because session data was lost is highly annoying and leads to users giving up on the site (Google 'evony login' for a high profile example)
Overall experience in server farms using from ten to over five hundred servers has led us to this recommendation.
1. Make sure your load balancing rules send the same user to the same server consistently. This results is very efficient use of both database and memory caching. This allows for efficient file caching of sessions using the power of disc caches.
2. Even if you can do (1) above then there is still merit in using some form of persistent and properly garbage collected session caching. memcache and memcached both will create random log-outs for users and should be avoided for reasons given in the narrative.
Database session handling is robust across multiple servers, but its speed (or lack thereof) is the reason for this entire discussion.
Here mcache (which does *not* stand for memory cache) provides a session server with all the benefits of both memory caching for recent sessions and disc backup for swapped out session data.
Of course, the overhead in setting up mcache is higher - you may actually have to read a manual rather than using someone else's example from the interweb ;-)
Using memcached for sessions is naive (faster is not always better) and prone to huge issues with random log-outs. In-fact, as your site become more critical and has higher traffic then although it is counter intuitive you need to abandon RAM based caching solutions as users will quickly overwhelm the available storage. Database sessions provide a reasonably robust solution for badly designed server farms that do not use consistent server forwarding for a given user. Consistently forwarding a given user to a given server using file sessions is more efficient, with the only issue being the potential loss of the server and hence a random logout when another working server is selected - but that is a 'blue-moon' issue. However, this issue is tiny in comparison to the loss of the session server database storage which would take down the entire site.
There is also the option of using MCache sessions which access a session server. Distributed session servers are the best option for high-traffic multi-server installations and require only simple server forwarding rules. Distributed = robust, and caching is multi-level moving from RAM to disc and eventually to oblivion (GC) as usage of the session data ages through lack of access.
In the end each solution has a number of points of failure. Memcached is better used for less critical and far more efficient caching of data that can easily be recreated without user intervention. An extra moment to re-cache a lost page or query result is of no consequence to a user; having to log in again because session data was lost is highly annoying and leads to users giving up on the site (Google 'evony login' for a high profile example)
Overall experience in server farms using from ten to over five hundred servers has led us to this recommendation.
1. Make sure your load balancing rules send the same user to the same server consistently. This results is very efficient use of both database and memory caching. This allows for efficient file caching of sessions using the power of disc caches.
2. Even if you can do (1) above then there is still merit in using some form of persistent and properly garbage collected session caching. memcache and memcached both will create random log-outs for users and should be avoided for reasons given in the narrative.
Database session handling is robust across multiple servers, but its speed (or lack thereof) is the reason for this entire discussion.
Here mcache (which does *not* stand for memory cache) provides a session server with all the benefits of both memory caching for recent sessions and disc backup for swapped out session data.
Of course, the overhead in setting up mcache is higher - you may actually have to read a manual rather than using someone else's example from the interweb ;-)
![]() |
Anton, 07-18-2009 |
One typo above - I meant to say '...abandon RAM ONLY based caching solutions...'
I realised the original text read as if I was bashing Dawn's solution, which was certainly not the intent!
I realised the original text read as if I was bashing Dawn's solution, which was certainly not the intent!
![]() |
Richard Ford, 09-16-2009 |
My issue is that on a cluster I have enabled memcached for sessions (was already using it for db query caching) and it works fine. This is done changing /etc/php.ini
The problem comes with other support sites on the same cluster that don't have memcache support.
So how can one set a per virtual host memcache or file or db session use?
I would have thought a php override in the virtual host .conf fragment - but that does not work either...
:-(
The problem comes with other support sites on the same cluster that don't have memcache support.
So how can one set a per virtual host memcache or file or db session use?
I would have thought a php override in the virtual host .conf fragment - but that does not work either...
:-(
![]() |
Jane, 09-22-2009 |
James, you jerk! how dare you be so idiotic and assume that Dawn doesn't know what she is talking about because she has tits. I think tits are realted to greater brain capacity.
![]() |
Martin R. Ehmsen, 08-13-2010 |
John, you have a race condition in your code:
if($cache->get(CACHE_SESSION . $sessionID) !== false) {
return $cache->get(CACHE_SESSION . $sessionID);
From the time you get a positive response from memcached in your if-statement, to when you look it up again in the body of your if-statement, the session can get expired from memcached.
This would probably only happen very few times, depending on the amount you store in memcached and the number of visitors to your site.
To avoid the race condition you should probably do something like:
if(($val = $cache->get(CACHE_SESSION . $sessionID)) !== false) {
return $val;
Regards,
Martin R. Ehmsen
if($cache->get(CACHE_SESSION . $sessionID) !== false) {
return $cache->get(CACHE_SESSION . $sessionID);
From the time you get a positive response from memcached in your if-statement, to when you look it up again in the body of your if-statement, the session can get expired from memcached.
This would probably only happen very few times, depending on the amount you store in memcached and the number of visitors to your site.
To avoid the race condition you should probably do something like:
if(($val = $cache->get(CACHE_SESSION . $sessionID)) !== false) {
return $val;
Regards,
Martin R. Ehmsen
![]() |
codef0rmer, 10-13-2010 |
I used your script on x server pointing memcache path on z and stored a session. It works on x server.
But when i use common_cache file on y server pointing memcache path on z. When i tried to print a session array. It does not work.
Session does not get forwarded from server x to y.
Have you tried using it over multiple servers?
But when i use common_cache file on y server pointing memcache path on z. When i tried to print a session array. It does not work.
Session does not get forwarded from server x to y.
Have you tried using it over multiple servers?
![]() |
Steve Veltkamp, 12-19-2010 |
One note - Step 4 says to put common_cache.db under includes directory. The dbsession.php in line 4 though looks for it in the Document Root - which in my case caused everything to fail till I changed it to require_once("common_cache.php");
![]() |
Mike Peters, 02-11-2011 |
Looking for a more robust session caching solution? Be sure to check out our Cassandra for PHP Sessions post.
![]() |
Ben, 09-09-2011 |
I have one question regarding to race condition.
Can this script handle race condition?
What if there are multiple Ajax requests happened simutaneously? Problem might happened if all requests need to update the session storage and return back the session state data.
Data might lost if a request read the data before another one, but write back to the storage later than another one to read.
I am raising this question because I am studing how to use Memcached as session handler, but scared of this nightmare happened again.
Can this script handle race condition?
What if there are multiple Ajax requests happened simutaneously? Problem might happened if all requests need to update the session storage and return back the session state data.
Data might lost if a request read the data before another one, but write back to the storage later than another one to read.
I am raising this question because I am studing how to use Memcached as session handler, but scared of this nightmare happened again.
![]() |
Dan, 10-07-2011 |
Just as a thumbs up - The payments. in the dbsession file is not really needed. Thanks for the great post! By the way, I am gonna modify your script to use pear cache_lite (shared host), and also make it so that there is only one session per user at most (regardless of the number of logins). Any ideas in this regard (the max one session id per user)?
![]() |
Alexander, 12-10-2011 |
Some simple and very lightweight option is to use SCache. It's not a full-scale monster like Cassandra, but still capable of operating as central storage for multiserver installations. It provides persistent storage so there's no need to store data redundantly to database.
![]() |
Matt, 02-19-2012 |
Hi,
This is a great script.
Are you willing to give some examples how to use it in your code using the sessionvars ?
Like... loggedin or not.. name and so on ?
Would be great to see!
Cheers,
Matt
This is a great script.
Are you willing to give some examples how to use it in your code using the sessionvars ?
Like... loggedin or not.. name and so on ?
Would be great to see!
Cheers,
Matt
|

Subscribe Now to receive new posts via Email as soon as they come out.
Comments
Post your comments