memcached for PHP sessions tutorial

Sessions are a simple mechanism for web technologies like PHP to create a unique ID for each visitor which can then be used to persist data between page requests. Upon their first visit, a cookie is dropped in the user’s browser with the generated ID which the server then uses to store data about this session until the cookie expires or is deleted.

This post explorer the use of memcached as the PHP session handler, and explains the pro’s and con’s of its use compared to the default.

What is the memcached daemon?

Memcached describes itself as

Free & open source, high-performance, distributed memory object caching system.

https://memcached.org/

The salient points of this are that it is distributed which enables us to scale as required and that it is in-memory which provides for the high-performance

Comparison with ‘files’

Out of the box, most Linux distributions (and we will be using Debian for the basis of this tutorial) use files to manage sessions at the server end. This is fairly obvious as every server has a filesystem! If you inspect the path defined in php.ini by the session.save_path directive you will see the current session files.

But filesystem access is significantly slower than memory, particularly if your visitor numbers are high. But what if your site is being served from a farm of servers? Although most reverse proxies can be configured to pin each session to just one backend server, this can cause issues if that server needs to be taken offline – the user loses their session and is no longer logged in.

Either of these two scenarios are where memcached can have significant benefits over files.

Memcache vs memcached

There is inevitably some confusion over this; memcached is a daemon which you need to install to provide the caching. The most confusing aspect is that there are two separate PHP modules to interface – memcache and memcached. Either of these modules will work, but I shall use memcache for this tutorial.

Installation

I shall assume that you already have a single webserver serving php pages. As every with Debian, installation with apt takes care of all dependencies

apt-get install memcached php-memcache libmemcached-tools

Configuration

Memcached

The default configuration file /etc/memcached.conf provided is sufficient for a single server installation.

#memcached default config file
#2003 - Jay Bonci jaybonci@debian.org
#This configuration file is read by the start-memcached script provided as
#part of the Debian GNU/Linux distribution.

#Run memcached as a daemon. This command is implied, and is not needed for the
#daemon to run. See the README.Debian that comes with this package for more
#information.
-d

#Log memcached's output to /var/log/memcached
logfile /var/log/memcached.log

#Be verbose
# -v
 
#Be even more verbose (print client commands as well)
# -vv
 
#Start with a cap of 64 megs of memory. It's reasonable, and the daemon default
#Note that the daemon will grow to this size, but does not start out holding this much
#memory
-m 64
 
#Default connection port is 11211
-p 11211
 
#Run the daemon as root. The start-memcached will default to running as root if no
#-u command is present in this config file
-u memcache
 
#Specify which IP address to listen on. The default is to listen on all IP addresses
#This parameter is one of the only security measures that memcached has, so make sure
#it's listening on a firewalled interface.
-l 127.0.0.1
 
#Limit the number of simultaneous incoming connections. The daemon default is 1024
-c 1024
 
#Lock down all paged memory. Consult with the README and homepage before you do this
-k
 
#Return error when memory is exhausted (rather than removing items)
-M
 
#Maximize core file limit
-r

The listen address will only allow connections from localhost. In a later post I shall explore sharing sessions in a farm situation.

php memcache

Likewise the default memcache config file /etc/php/7.3/apache2/conf.d/20-memcache.ini )assuming you are using Apache as the front end) is sufficient

; uncomment the next line to enable the module
 extension=memcache.so

 [memcache]
 ; Whether to transparently failover to other servers on errors.
 ;
 ; memcache.allow_failover="1"

 ; Defines how many servers to try when setting and getting data. Used
 ; only in conjunction with memcache.allow_failover.
 ;
 ; memcache.max_failover_attempts="20"

 ; Data will be transferred in chunks of this size, setting the value
 ; lower requires more network writes. Try increasing this value to
 ; 32768 if noticing otherwise inexplicable slowdowns.
 ;
 ; memcache.chunk_size="8192"

 ; The default TCP port number to use when connecting to the memcached
 ; server if no other port is specified.
 ;
 ; memcache.default_port="11211"

 ; Controls which strategy to use when mapping keys to servers. Set
 ; this value to consistent to enable consistent hashing which allows
 ; servers to be added or removed from the pool without causing keys to
 ; be remapped. Setting this value to standard results in the old
 ; strategy being used.
 ;
 ; memcache.hash_strategy="consistent"

 ; Controls which hash function to apply when mapping keys to servers,
 ; crc32 uses the standard CRC32 hash while fnv uses FNV-1a
 ;
 ; memcache.hash_function="crc32"

 ; Use memcache as a session handler by setting this value to memcache.
 ;
 ; session.save_handler="memcache"

 ; Defines a comma separated of server urls to use for session storage,
 ; for example session.save_path="tcp://host1:11211,tcp://host2:11211".
 ;
 ; Each url may contain parameters which are applied to that server,
 ; they are the same as for the Memcache::addServer() method. For
 ; example: session.save_path="tcp://host1:11211?persistent=1&weight=1&timeout=1&retry_interval=15"
 ;
 ; session.save_path="tcp://localhost:11211"

 ; Additional parameters
 ; memcache.protocol=ascii
 ; memcache.redundancy=1
 ; memcache.session_redundancy=2
 ; memcache.compress_threshold=20000
 ; memcache.lock_timeout=15

Again, these defaults only need changing if sessions are being shared between servers, or if you have changed the memcached defaults.

php

All that remains is to update the session directives in /etc/php/7.3/apache2/php.ini as follows.

session.save_handler = memcache
session.save_path="tcp://localhost:11211"

remember to restart Apache after the changes

Testing

The libmemcached-tools package we installed has a number of useful tools to inspect the memcached server. You can use the following command to dump the data currently stored.

memcdump --servers=127.0.0.1:11211