Drupal 7 Memcache, PECL Memcache, Memcached, and sockets on Ubuntu 12.04

Sunday, July 15, 2012 - 11:59
  • Druplicon
  • Ubuntu circle of friends logo.

I have this setup running with 3 sites on a VPS and it seems to run without problems, and quite snappily at that. I’m using the Drupal Memcache module (aka. Memcache API and Integration), the PECL Memcache extension, Drupal 7 and Memcached itself of course (The daemon, not the PECL Memcached extension) all running on Ubuntu Server 12.04.

By default Drupal’s cache bins are kept in tables in the database, and here we will set up Drupal to use Memcached to store the cache bins instead. You can see what cache bins your Drupal site is using by looking for the database tables which begin with ‘cache‘. We will not store cache_form in Memcached as it should be stored in persistent storage to prevent losing any forms that your sites users may be half way with filling in. (Cache form is not just a cache). I also avoid keeping cache_update in Memcached. cache_update is always stored in the database.

Just to clear up any confusion there is the Drupal Memcache module, the PECL Memcache extension (which we will be using), the PECL Memcached extension (which isn’t used in this setup) and the Memcached daemon, which is what the extensions are used to connect to. I would have preferred to use the PECL Memcached extension, as it supports sockets since version 2.0.0b1, unfortunately the Drupal Memcache module does not work with it as of yet. Additionally the Drupal Memcache admin module (which comes with the Drupal Memcache module) spewed forth errors upon viewing it’s stats page while using PECL Memcached, but was fine with PECL Memcache (apart from a more minor problem which is fixed below). I might do a post at a later date about using the PECL Memcached extension as I did get it working, eventually, by (hacking|patching) Memcache. (A modified version of the patch was committed, it is currently only available in dev releases as of 28/05/2013.)

I used sockets because A) It might (possibly should) be faster than going through the network stack, and B) when the Memcached daemon is told to use a socket, it disables it’s networking capabilities.

Final note, if you aren’t running as root, you either need to su to get a root shell, or put sudo in front of most of the commands. For editing config files I generally use nano, and you can get it display the line you are on by starting it with the -c switch like so: nano -c /file/to/edit

Onwards!

Install & configure the Memcache daemon

You install the Memcached package (the daemon) with:

  1. apt-get install memcached

It is necessary to edit /etc/init.d/memcached in order to automatically create a directory for the Memcached processes to place their sockets. I added this at roughly line 44:

  1. # Make /var/run/memcached directory
  2. if [ ! -d "/var/run/memcached" ]; then
  3. mkdir /var/run/memcached
  4. chown memcache /var/run/memcached
  5. fi

Bear in mind though, that it is possible for the above changes to be overwritten by an update to the Memcached Ubuntu package. You will generally get a warning if an update is about to overwrite your changes though.

As you can see from the usage notes in the /etc/init.d/memcached file you need to copy /etc/memcached.conf to a new file and edit it to create the configuration for a Memcached daemon:

  1. # The naming convention is memcached_myserver.conf, this will
  2. # create the config for a Memcached instance called myserver
  3. # You can name what you please
  4. cp /etc/memcached.conf /etc/memcached_myserver.conf

Now to edit /etc/memcached_myserver.conf to our liking:

  1. # The memory limit is on line 23, it is measured in MB, it is probably safe to leave it at 64MB.
  2. # You can monitor it and increase it later if necessary.
  3. # Worth noting is that the daemon will not start out using this
  4. # much memory for storage, but will only grow to use this much space.
  5. -m 64
  6. # Comment out the default port on line 26 like so:
  7. # -p 11211
  8. #Comment out the IP address option on line 35 like so:
  9. # -l 127.0.0.1
  10. # At the bottom of the file add the location of your socket by specifying the -s switch:
  11. -s /var/run/memcached/memcached_myserver.sock
  12. # The name of the socket isn't important as long as you remember it for later.
  13. # I just stick to the config file convention, replacing .conf with .sock as I then always
  14. # know which socket belongs to which instance.
  15.  
  16. # Finally add the -a switch to specify the octal file mode of the socket when it is created:
  17. -a 0766
  18. # You may not want the socket to be world writable, I have not set it up like that just yet.

If you only want to use one Memcached instance for all your Drupal cache bins you can move on, if not, you will need to repeat the above process create a configuration file for each of the Memcached instances you wish to use. You can assign multiple cache bins to one instance, or as some people like to do, you can create a Memcached instance for each cache bin, the advantage of the latter is that you can look at the statistics for each bin on it’s own. Remember to give each instance a unique socket (Just place them in the /var/run/memcached directory to avoid problems with file permissions). You can also have multiple Drupal sites use the same Memcached instance.

Now start up Memcached and see if it is running:

  1. # Start all Memcached instances with:
  2. service memcached start
  3. # Alternatively start instances by name, for memcached_myserver.conf:
  4. service memcached start myserver
  5. # Stopping works the same way:
  6. service memcached stop # For all the instances
  7. service memcached stop myserver # For one instance
  8.  
  9. # Check the Memcached log for errors, though it might not exist yet if nothing has been written to it.
  10. tail /var/log/memcached.log
  11.  
  12. # Finally check if the instance is running:
  13. echo "stats" | nc -U /var/run/memcached/memcached_myserver.sock
  14. # Should return something like:
  15. # STAT pid 4668
  16. # STAT uptime 456843
  17. # STAT time 1342432534
  18. # ...

Install & configure PECL Memcache

This is easy, just do:

  1. pecl install memcache

Once that is installed you need to create /etc/php5/conf.d/memcache.ini and add the following:

  1. # This enables the extension
  2. extension=memcache.so

The other configuration directives are listed in the PHP manual. Some are not useful to a single server running Memcached on sockets, as they pertain to a distributed setup. Currently using pecl to install the memcache extension will get you version 2.2.6, if you are feeling a little more daring you can install version 3, which is still in beta, like this:

  1. # If you haven't already installed PECL memcache
  2. pecl install memcache-beta
  3. # If you wish to "upgrade" to a beta release
  4. pecl upgrade memcache-beta

This does allow you to use more of the configuration directives listed in the PHP Manual, though Drupal Memcache does not support the binary protocol. You will also need to restart Apache if you are using mod_php, or mod_fcgid. If you are using PHP-FPM you will need to restart your PHP process. (Shameless plug on using Apache 2.2 and PHP-FPM over here).

If you are interested in using the PECL memcache's memcache.php for statistics reporting, see this post. memcache.php does a much better job than the memache_admin module discussed below. This is apart from the hit and miss statistics the memcache_admin module can provide on each page.

Install & configure the Drupal Memcache Module

The Drupal Memcache module is known as “Memcache API and Integration” on it’s project page. Download it, then we need to patch it with a patch from this issue. We need to patch it so that the memcache_admin statistics display correctly, so it won't break anything other than the statistics if you upgrade and the patch is not committed. I used the second patch as it contained the least cruft. To patch it just do this:

  1. # We need to change to the memcache_admin folder for patch no. 2 as it doesnt apply otherwise
  2. cd /path/to/memcache/memcache_admin
  3. wget http://drupal.org/files/patch-memcache-memcache_admin-socket-support.patch
  4. git apply -v patch-memcache-memcache_admin-socket-support.patch

If you don’t have Git installed you could make the changes by hand, it’s a pretty small patch.

Now, enable the memcache and memcache_admin modules. Check your status page to see if everything is alright, if it is you can continue to edit your settings.php.

  1. # Adds memcache as a cache backend
  2. $conf['cache_backends'][] = 'sites/all/modules/memcache/memcache.inc';
  3. # Makes it so that memcache is the default caching backend
  4. $conf['cache_default_class'] = 'MemCacheDrupal';
  5. # Keep forms in persistent storage, as per discussed at the beginning
  6. $conf['cache_class_cache_form'] = 'DrupalDatabaseCache';
  7.  
  8. # Specify the memcache servers you wish to use and assign them to a cluster
  9. # Cluster = group of memcache servers, in our case, it's probably just one server per cluster.
  10. $conf['memcache_servers'] = array('unix:///var/run/memcached/memcached_myserver.sock' => 'default');
  11. # This assigns all cache bins to the 'default' cluster from above
  12. $conf['memcache_bins'] = array('cache' => 'default');
  13.  
  14. #Alternatively, if you were using more Memcached server instances it would look like this:
  15. $conf['memcache_servers'] = array(
  16. 'unix:///var/run/memcached/memcached_myserver.sock' => 'default',
  17. 'unix:///var/run/memcached/memcached_myserver2.sock' => 'mycluster',
  18. );
  19. # And then you could assign different bins to the second cluster
  20. $conf['memcache_bins'] = array(
  21. 'cache' => 'default',
  22. 'cache_bootstrap' => 'mycluster',
  23. 'cache_block' => 'mycluster',
  24. );
  25.  
  26. # If you wanted multiple Drupal installations to share one Memcache instance use the prefix like so:
  27. $conf['memcache_key_prefix'] = 'site_specific_prefix';

Now, if everything went as planned you can now visit your Drupal site as User 1 and see the Memcache hit and miss statistics on the bottom of each page, you can turn it on and off at drupal.example.com/admin/config/system/memcache, and the Memcache statistics overview page at drupal.example.com/admin/reports/memcache.

This post went on for much longer than I expected, I hope that someone finds it useful at least :S

If you made it this far, thanks for reading and drop your input below, especially if you think there is something wrong with the configuration.

Cheers :D

Author: Ryan Solomon
Ryan is a partner at desiDev, he likes open source software and QfG meeps.

Thanks for the feedback!

Thanks for the feedback!

I have clarified that part of the post, as I had left out a link to the issue, and an explanation. It's just so that the memcache_admin module reports statistics correctly on the status page. It shouldn't break anything other than the report if the patch, or a working alternative, is not committed to the memcache project.

Hi, thanks.

Hi, thanks.

Memcached doesn't have tables, it's a key => value store, so there aren't any columns or such, and you can't query by anything except for the key as far as I know.

I would just use cache_* drupal functions to use Memcache, as you would normally use the Drupal cache functions. I would then assign my cache bin (which is a table when using the database cache backend.) to be handled by Memcache in settings.php. This also means that if Memcached is not available, you can use any of the other cache backends to handle your data storage. If you do go this route, don't forget to create the database table in hook_install() lest someone tries to use it with the database cache.

Alternatively, if you don't want to use Memcache as a Drupal cache backend, you should be able to get example code from the Memcache module or some of the other modules which use Memcache on Drupal.org.

Excellent tutorial Ryan,

Excellent tutorial Ryan, thanks a lot from me too!

Just wondered if adding an opcode cacher like apc would add some extra performance,
especially on sites with multiple drupal instances?

I would go for apc since:
- installation and configuration seems fairly simple
- it comes with a nice webfrontend
- as rumors goes it will be included in php6 anyhow

Do you got any experience with apc?
Might be worse a comment in your tutorial.

And to round things up,
why not mentioning the standard mysql tuning with mysqltuner
(apt-get install mysqltuner) and/or tuning-primer.sh

And maybe another tuning hint,
in "LPCKVMSSPV2.1.pdf" there is a KVM benchmark showing
dramatical write improvment to harddisk when using deadline scheduler.

Thanks!

Thanks!

Yes, APC is a must when you can run it, I have covered it elsewhere, but I have assumed that people are aware of the performance benefits, I prefer to keep articles focused around a point as it means you don't have to sift through information you aren't interested in at the time. The related articles are Quick Setup: Apache Worker, FastCGI, PHP-FPM and APC, I think using PHP-FPM is important myself to keep Apache process size down, and it shares the APC cache between processes, which is only otherwise possible with mod_php as far as I am aware. There is also Install APC through PECL on Ubuntu 12.04.

I have yet to write anything about MySQL performance, as I don't think I am knowledgeable enough to put forward universal recommendations for that, MySQL tuning can be very case specific. For a development setup I use a configuration along the lines of my answer to a question on poor database performance on Drupal Answers.

I haven't looked deeply into scheduling, it would be worth looking at, at some point, but a large portion I have come across has been geared towards single purpose servers such as a DB server, which again I'm not that up to speed on, and some of the information almost seems conflicting depending on scenario and trade-offs you can make.

No need to explicitly keep

No need to explicitly keep cache_update in database as update module does this out of the box. See comments at _update_cache_set() in update module. Quoting: "Usage of the core cache API results in all sorts of potential problems that would result in attempting to fetch available update data all the time, including if a site has a "minimum cache lifetime" (which is both minimum and a maximum) defined, or if a site uses memcache or another pluggable cache system that assumes volatile caches."

Cheers.
jummonk