In a previous post I briefly went through getting a basic instance of Redis going and how at a code level you to store and retrieve data. In this article I’m going to try to be a little more practical about things and actually use it for something. Specifically, we’re going to look at replacing Drupal’s database-oriented caching/locking system with Redis’s in-memory caching system.
I’m working off a fresh install of redis and Drupal but I’m going to side-step the installation procedure for each since you would usually do this to an already functioning website you wanted to integrate with your existing redis infrastructure.
For reference this is the system I’m using but the instructions found here should be widely applicable:
- Platform: Ubuntu 16.04 (Xenial)
- PHP Version 7.0 configured as FastCGI (PHP-FPM)
- MariaDB 10.0.31
- Drupal 7 (but the steps here should be identical for Drupal 8).
- When Redis Caching Makes Sense
- Getting PHP Ready
- Redis Drupal Module
- Troubleshooting Issues
- Further Reading
When Redis Caching Makes Sense
Considering database management systems often contain some of the performance-oriented behaviors (such as keeping data in-memory) you would get from Redis you may wonder: What use Redis does have for an application that’s already going to have a full DBMS?
Indeed, if it serves no purpose it’s just another thing that can break and make troubleshooting more convoluted. The primary reason you would use Redis for the Drupal cache to be moved to be more local to the application.
You might prefer a cache local to your application for several reasons but mainly:
- You’re interested in keeping network out of the loop and want Drupal to pull the cache entries from something running on the local system rather than making another database query.
- The server the database runs on is shared amongst several application. Moving to a small private Redis instance keeps the data handy without contending with other users during peak load.
Getting PHP Ready
Before we move onto modifying Drupal itself, we need to make sure PHP can even communicate with Redis. To do that we need to install the
redis PECL module.
Since compilation of the extension via
pecl requires access to PHP development libraries and headers I’ll also need to make sure the
php-dev package is installed first.
Then we do the actual install of the PECL extension:
root@2287b3975d3a:/# pecl install redis downloading redis-3.1.4.tgz ... Starting to download redis-3.1.4.tgz (199,559 bytes) ..................done: 199,559 bytes 20 source files, building running: phpize Configuring for: PHP Api Version: 20151012 Zend Module Api No: 20151012 Zend Extension Api No: 320151012 enable igbinary serializer support? [no] : building in /tmp/pear/temp/pear-build-defaultuserPAA1mj/redis-3.1.4 running: /tmp/pear/temp/redis/configure --with-php-config=/usr/bin/php-config --enable-redis-igbinary=no checking for grep that handles long lines and -e... /bin/grep checking for egrep... /bin/grep -E [...snip...]
Now we need to enable the extension in the appropriate php.ini file (or inclusion thereof). Knowing the proper .ini file to write to varies according to the platform and how you’re using PHP so in general you’ll have to figure that out on your own. My install though uses PHP-FPM on Ubuntu though:
root@2287b3975d3a:/# echo "extension=redis.so" >> /etc/php/7.0/fpm/conf.d/90-redis.ini
We’ll now need to refresh PHP for this to take effect, so I issue a
kill -SIGUSR1 (where
masterPID is the pid of the FPM master process). This will cause FPM to re-read it’s configuration and do a graceful/non-disruptive restart.
At this point, if you look at the output of
phpinfo() (also available from the Drupal status section) you should see the Redis extension having been loaded.
Redis Drupal Module
Now that PHP can talk to Redis, we need to install the
redis module so that we have access to its Drupal 7 caching backend.
Installing the module with
drush is pretty straight forward:
root@ad4c3eee12b1:/var/www/html# drush en -y redis redis was not found. [warning] The following projects provide some or all of the extensions not found: [ok] redis Would you like to download them? (y/n): y Project redis (7.x-3.15) downloaded to /var/www/html/sites/all/modules/redis. [success] The following extensions will be enabled: redis Do you really want to continue? (y/n): y redis was enabled successfully. [ok] Now that it's installed and enabled, we need to update the relevant
settings.phpfor the website to switch the caching backend over to Redis. To configure my install I appended the following: $conf['lock_inc'] = 'sites/all/modules/redis/redis.lock.inc'; $conf['path_inc'] = 'sites/all/modules/redis/redis.path.inc'; $conf['redis_client_interface'] = 'PhpRedis'; $conf['redis_client_host'] = '9184.108.40.206'; $conf['cache_backends'] = 'sites/all/modules/redis/redis.autoload.inc'; $conf['cache_default_class'] = 'Redis_Cache'; $conf['cache_prefix'] = 'drupal7';
Note: your exact path to the
redis module may vary. The above is just where my
drush command happened to install mine. Other installs may place it underneath
Let’s explain each line we’re adding so you understand what we’re actually doing here:
redismodule’s implementation moves all locking mechanisms from the database and into Redis. Quicker locks mean that contentious operations are finished sooner and thus get out of each others’ way.
- We’re overriding the default
path_incimplementation so that Drupal will now cache URL path mappings in Redis. By default, once Drupal figures out what user-supplied URL results in what “system URL” it stores this result in the database. If the value is already a system URL, the value saved is just an exclamation point.
redis_client_interfaceis used to specify what PHP client we want to use to communicate with Redis. The available choices are Predis (a purely PHP implementation bundled with the
redismodule) or phpredis (a compiled C extension to PHP). For performance reasons you’re going to want phpredis wherever possible which is why we installed it above.
redis_client_hostthis merely sets the hostname/IP address of the Redis server. If left unspecified it defaults to
localhost. A related (optional) configuration item is
redis_client_portwhich does the same for the port of the remote Redis service.
cache_backendsthis loads the Redis functions during the Drupal bootstrap process.
cache_default_classSets the actual cache handler class.
cache_prefixSets a unique prefix for this Drupal instance to use when storing values to Redis. This is useful for cases where the Redis instance may be local but shared amongst several installs. Ultimately it’s optional if this redis instance only ever serves as a cache for this one Drupal website.
Only issue with this setup I’ve ran into is with clearing the cache from the command line. At the time of this writing, if you issue a
drush cc all (to clear all caches) on a Redis-backend cache you will receive an error from
drush and an incomplete purge of cached items. To get around this, use the
redis-cli command to connect to the server and issue commands directly to clear the cache for you. One possibility is simply a
FLUSHDB command to clear all keys in the database out (assuming they’re all easily replaceable) or something similar to
redis-cli --raw keys "$PREFIX" | xargs redis-cli del to delete anything that begins with the prefix you gave above.
This is a known issue with a fix available but not yet merged into the
redis module yet.