» Enhance PHP session management
In PHP, sessions can keep track of authenticated in users. They are an essential building block in today's websites with big communities and a lot of user activity. Without sessions, everyone would be an anonymous visitor.
In system terms, PHP sessions are little files, stored on the server's disk. But on high traffic sites, the disk I/O involved, and not being able to share sessions between multiple webservers make this default system far from ideal. This is how to enhance PHP session management in terms of performance and shareability.
Session sharing in web clusters
If you have multiple webservers all serving the same site, sessions should be shared among those servers, and not reside on each server's individual disk. Because once a user gets load-balanced to a different server, the session cannot be found, effectively logging the user out.
A common way around this is to use custom session handlers. Writing a class that overrules default behavior and stores sessions in a MySQL database.
Sessions in Database
All webservers connect to the same database and so, as soon as www01 registers a session (insert in a sessions table), www02 can read it. All servers can now see all sessions: problem solved?
Yes, and no. This sure is functional and tackles the shareability issue. But databases seem to be the biggest bottlenecks of web clusters these days. They are the hardest to scale, and so in high traffic environments you don't want to (ab)use them for session management if you don't have to. We have to tackle the 'performance' issue.
Database memory
Memory is about 30 times faster than disk storage. So storing our sessions in memory somehow, could deliver great performance.
MySQL query caching
One form of using database memory is the standard MySQL query caching. But MySQL query caching isn't very effective because it invalidates all cache related a table, if only one record in that table is changed.
Of course the session table is changed all the time, so the session cache is purged all the time, rendering it quite useless for our purposes.
Heap tables / Memory tables
We're really closing in to our goal now. Storing the sessions in a heap/memory table (a table that lives in your database server's RAM) speeds up things greatly. Many demanding sites have opted for this solution.
In my eyes however, it's still not optimal. Because it still requires a lot of additional queries that your database server(s) shouldn't necessarily have to process.
One other possible solution is using Memcache. And you will find it's easier to setup and has a smaller footprint than most alternatives. For one thing, because you will not have to code custom session handler classes in PHP. Memcache session support comes native.
Memcache
Memcache is a little program originally written by Live Journal. It's quite straight forward: It reserves some memory, opens a socket and just stays there.
We can connect to the socket and store variables in it, and later retrieve them later on. The storage of the variables is done in RAM. So it's lighting fast ;)
Memcache is used for caching a lot things: function results, entire html blocks, database query results. But now we're going to use it to store our site's user sessions.
Architecture
From system point of view, Memcache looks a lot like MySQL. You have a:
- Server
Where information is stored. Should be running at all times. - Client module
Interface to save & get information from the server.
It's integrated in our programming language.
There is one important difference though. If the Memcache server is shut down, the information in it is lost. So remember to use memcache as a cache only. Don't store information in it, that can't be retrieved in some other way. For sessions this is a risk I'm willing to take. Worst case scenario is that my users will be logged out. If you cannot live with this, you could combine database & memcache session handlers. Database will be the safe storage, memcache will be in front of it for performance. If it crashes, you will only lose performance, and not the data.
Installing a Memcache server
For session sharing, use a centralized server. If you only have one webserver, it still makes sense to use Memcache from performance point of view. Just limit it's maximum allowed memory size to 64MB (depending on your server & wishes), and use the localhost (127.0.0.1) to connect to it.
If you don't have a Memcache server already, you can install it very easily with package management. I use Ubuntu so in my case that would translate to:
aptitude install memcached
Adjust the settings in /etc/memcached/memcached.conf. In my case the defaults were OK, I only increased the max allowed memory, and allowed more hosts to connect to it.
Now let's spawn the Memcache Daemon (yes that's what the 'd' stands for):
/etc/init.d/memcached start
Done, we're ready to use it... But how?
Installing a Memcache 'client'
You could just open a socket and go talk to Memcache, but that would eventually cause headaches. So there is a standard PHP module we can use that does a lot of work for us, and allows us to talk object oriented to it. This works much like installing a MySQL module. If it's in your distro's package management, good for your, let's:
aptitude install php5-memcache
If not, no problem. Make sure you have pecl available and:
pear install pecl/memcache
(I used 'pear' and not directly pecl to circumvent the bug that caused a Fatal error: Allowed memory size of 8388608 bytes exhausted).
And please choose:
Enable memcache session handler support? [yes] : yes
You must enable PHP to use the memcache.so module we now have. So please add a
extension=memcache.so
to your php.ini (usually located at /etc/php5/apache2/php.ini)
Great, we have all the prerequisites to start Memcaching!
Sessions in Memcache
PHP allows you to overrule the default session handler in two ways:
- session_set_save_handler(). By programming your own session handlers, allowing you to virtually use any type of storage, as long as you can read/write to it from PHP. This example uses a MySQL database. We could also use this method to connect to Memcache.
- session.save_handler. By specifying one of the default handlers in the php.ini file using the session.save_handler & session.save_path directives.
Option 1 allows greater flexibiliy. And it even allows you to create a combined database/memcache mechanism. Resulting in a fallback-on-database in case memcache goes offline and loses all of it's sessions (effictively logging out all users).
Option 2 is very easy to implement, doesn't require changing your existing code, and is the one I'm going to show you today.
session.save_handler
Assuming that you have one webserver, and installed the Memache server on that same machine, the hostname will be 127.0.0.1. If you have it on a different server, you will know what IP to substitute it with.
session.save_handler = memcache
session.save_path = "tcp://127.0.0.1:11211"
Done! Huh? what just happened?
Well, because we enabled Memcache session handler support, all work is done for us. PHP will now know not to use the default files handler so save session files in /var/lib/php5 but uses memcache running at 127.0.0.1 instead.
Don't forget to restart your webserver to activate your changes to the php.ini
/etc/init.d/apache2 restart
The Catch
Update - As Manuel de Ruiter says in the comments, the following is no longer true thanks to some updates
As with anything too cool, there is a catch: Locking. The standard PHP Session module locks the whole session until the request finishes. Memcache is build for speed and as a result, does not support this kind of locking. This could lead to problems when using frames or ajax. Some actions may request a session-variable before it's actually saved.
Further reading
What we have just done with Memcache is the low-hanging fruit. We've enabled RAM sessions with minimum effort and without changing even one line of your existing code.
But now that you have memcache running, you might want to use it for storing other often-used, rarely-changed variables as well. Feel free to expiriment and review the documentation. You will learn that Memcache can enhance your serverside performance dramatically.
Stay up to date
You can track my blog
articles and
comments. You may also find my
bookmarks interesting. Or
Follow me on Twitter
Like this Article?
|
Your money is no good here, but you can boost morale by spreading the word! : ) |
RelatedArticles like this one» Survive heavy traffic with your webserver |
tags: php, performance, mysql, PECL, disk IO, memcache
category: Howto - Webserver
read: 56,905 times






tagcloud
#18. Kevin on 12 August 2010
If memcache is dead, sessions won't work and it's totally in order to get some errors.
Wether they should be shown to your customers is a second; but you can control that in your php.ini
#17. Jeff on 15 July 2010
I'm using memcache to store user sessions, it works well most of time , but it will throw exceptions when the connection to cache server is failed . have you come across this problem ? how to solve this kind of problem ?
The exceptions looks like below,
... [more]
Error #8 Memcache::get() [<a href='memcache.get'>memcache.get</a>]: Server 10.10.24.9 (tcp 11212) failed with: Connection timed out (110)
Error #8 Memcache::get() [<a href='memcache.get'>memcache.get</a>]: Server 10.10.24.9 (tcp 11212) failed with: Failed reading line from stream (0)
Thanks,
Jeff
#16. Kevin on 29 June 2010
#15. Zeeshan Zakaria on 17 June 2010
You have wonderful articles here and I am now going to tweak my server to enhance its performance.
There are three things however which I wanted to ask:
... [more]
1. Regarding memcache, my CentOS server has a file in /etc/php.d/ and it is memcache.ini. There are many other ini files here too. The memcache.ini is as follows:
; Enable memcache extension module
extension=memcache.so
; Options for the memcache module
; Whether to transparently failover to other servers on errors
;memcache.allow_failover=1
; Defines how many servers to try when setting and getting data.
;memcache.max_failover_attempts=20
; Data will be transferred in chunks of this size
;memcache.chunk_size=8192
; The default TCP port number to use when connecting to the memcached server
;memcache.default_port=11211
; Hash function {crc32, fnv}
;memcache.hash_function=crc32
; Hash strategy {standard, consistent}
;memcache.hash_strategy=standard
; Options to use the memcache session handler
; Use memcache as a session handler
session.save_handler=memcache
; Defines a comma separated of server urls to use for session storage
session.save_path="tcp://localhost:11211?persistent=1&weight=1&timeout=1&retry_interval=15"
Now there is also a file /etc/php.ini with session.save_handler = files and a path for the files, but these files are never created. This mean s that somehow the settings in the memcache.ini take precedence over the settings in php.ini file, or maybe php.ini file is never read and only the *.ini files in php.d folder are read. Do you have any comments on what is happening here?
2. For a page where the data doesn't change frequently, I am caching it using simple PHP techniques, i.e. doing ob_start() and ob_get_contents(). Is it fine or the method you explained is faster?
3. The main page of my website which is updated as the users post comments, I tried a tricky way to cache it and update the cache file only when a new comment is added, and so far it didn't work right, however I plan to try it again later sometime. But I have noticed that if a lot of visitors come to my website at once, e.g. 100 users in a few minutes, I see in the log that this many users hit the server, but don't see that this many sessions were initiated or pages were served. I think this is because that apache and mysql gets overloaded with requests and can't serve all the users. I don't have a super server, just an average Dell server. Do you think I am right here?
Thanks again for the wonderful knowledge you shared here.
#14. Kevin on 21 February 2010
#13. Leenix on 26 January 2010
Found a nice description here;
http://phpslacker.com/2009/03/02/php-session-clustering-with-memcache/
#12. Kevin on 29 July 2009
#11. Manuel de Ruiter on 29 July 2009
By the way, you got a dead link in your article, namely: " Feel free to expiriment and [u]review the documentation.[/u]".
#10. Kevin on 22 May 2009
You can make it locking:
http://www.slideshare.net/folke/netlog-what-we-learned-about-scalability-high-availability-430211 (slide 59)
... [more] But this would require writing your own sessions hander (instead of using the .ini default memcache handler) and sacrificing some performance.
#9. Artur Ejsmont on 19 May 2009
What you have to worry about is sequence of session updates. Data stored in session by one request could be overridden by another request.
Having your own handler you could get data just before saving again to minimize that risk of dirty update.
#8. Kevin on 05 September 2008
Concerning memcache: Verbose output would be very helpful. Viewing the phpinfo(); output and determining whether there is memcache support can be a good test. Others tests may be to try and open a telnet connections from the 'malfunctioning' webserver and access the memcache port directly. Let me know when you have more details
#7. martin on 01 September 2008
im still cant solve my problem. since there isnt much articles about memcache as detail as yours, i got stuck and decide to pending this memcache project for a while.
so...can you give me a clue? my webserver simply cant save its session in main memcache webserver. thanx in advance kevin.
#6. Kevin on 27 August 2008
#5. martin on 11 August 2008
i already done these steps:
aptitude install php5-memcache
pear install pecl/memcache
... [more] Enable memcache session handler support? [yes] : yes
extension=memcache.so
session.save_handler = memcache
session.save_path = "tcp://172.19.3.78:11211" (memcache server ip)
/etc/init.d/apache2 restart
any idea about what i've missed to do this time?
#4. Yaa101 on 06 August 2008
The first message is because it can't find the memcache demon, checkout if it is running, further checkout what port it assigns itself in the runlevel startup script.
The 2 messages after are caused by the first message, if you solve the first, these will go away too.
#3. martin on 01 August 2008
Notice: session_start() [function.session-start]: Server 172.19.3.78 (tcp 11211) failed with: Connection refused (111) in /var/www/phpPgAdmin/libraries/lib.inc.php on line 56
... [more] Warning: session_start() [function.session-start]: Cannot send session cookie - headers already sent by (output started at /var/www/phpPgAdmin/libraries/lib.inc.php:56) in /var/www/phpPgAdmin/libraries/lib.inc.php on line 56
Warning: session_start() [function.session-start]: Cannot send session cache limiter - headers already sent (output started at /var/www/phpPgAdmin/libraries/lib.inc.php:56) in /var/www/phpPgAdmin/libraries/lib.inc.php on line 56
could you help me figure what's wrong?
thanks alot before
#2. Craig on 31 July 2008
#1. Craig on 30 July 2008
As you may be aware, when two requests from the same session id request access to the file based session, php queues the second request until the first request has closed the session. This prevents process race conditions from corrupting session information.
For example, in a non-locking session (such as using a custom database handler), if request A started executing first, then request B started executing. They both got the session value for Authenticated which equaled False.
... [more]
Request A changed the value to True, but request B left it set to false.
If Request A finished first it would write the Authenticated equals true to the session. When request B finished it would then write Authenticated equals false back to the session (overwriting any changes made by request A). Authenticated would then equal false, when you were expecting it to equal true.
File based sessions would lock the session when request A was using it, preventing request b from executing until it had finished. Then request B would start, get the session request A wrote, where Authenticated is equal to True. Any your web app would execute as expected.
So do you know If PHP locks sessions when you tell it to use memcache sessions instead of file sessions?