Speeding up Existing Apps with a Redis Cache

Share this article

We’ve gone through the basics of Redis in PHP before, but it’s time to cover a real life use case. In this tutorial, we’ll add it to an already deployed application to give the app the appearance of speed.

art6ver1-01

You can easily follow along by cloning the 0.6 release of the app.

The Problem

Before applying a solution, we need to have a clear definition of the problem.

The application in question, when executing a query, runs off to Diffbot’s API and makes it query the dataset. The subset is then returned and displayed. This can take up to 5 or so seconds, depending on the busyness of Diffbot’s servers. While the situation will undoubtedly improve as they expand their computational capacity, it would be nice if a query executed once were remembered and reused for 24 hours, seeing as the collection is only refreshed that often anyway.

“But what good is caching a single query?” you might wonder. It’s not like most people will search for one and the same thing often.

Well… as a matter of fact, not only has research shown that they will often search for one and the same thing (React is trending? Sudden influx of “react” queries), they will also very reliably search for prolific authors (or themselves). Considering the fact that implementing this cache costs us literally nothing (and actually reduces costs by reducing strain on the servers), adding it in is an easy win, even if it weren’t used as often as one would hope. There is no reason not to add it – it can only benefit us.

With the problem clearly defined, let’s handle the prerequisites.

Installing

First things first, we need to install Redis into both the development and production environment (note that if you’re using Homestead for local development, Redis is already installed, albeit v 3.0.1 at the time of writing).

We can do this via the operating system’s package manager like so:

sudo apt-get install redis-server

This is the simplest and recommended approach, but we can also install it from scratch and manually configure it. As per instructions on their website, this is done via:

sudo apt-get install gcc make build-essential tcl
wget http://download.redis.io/releases/redis-3.0.2.tar.gz
tar xzf redis-3.0.2.tar.gz
cd redis-3.0.2
make
make test
sudo make install

If you run into a fatal error mentioning jemalloc.h after running make just run make distclean and then make again. The make test command is optional, but helpful.

Note: If you’re reading this and version 3.0.2 is no longer newest, just adapt the commands to the newest version number.

To prevent some common warnings (on Ubuntu at least), we also preventatively run the following commands:

sudo sh -c 'echo "vm.overcommit_memory=1" >> /etc/sysctl.conf'
sudo sh -c 'echo "net.core.somaxconn=65535" >> /etc/sysctl.conf'
sudo sh -c 'echo "never" > /sys/kernel/mm/transparent_hugepage/enabled'

We also make sure the last command is added to /etc/rc.local just above exit 0, so that it’s re-issued on every server restart. Finally, we can reboot the server with sudo reboot and check if everything is fine by running Redis with sudo redis-server.

Finally, we need to make sure Redis starts after a server restart, so we follow their official instructions to get that accomplished.

Predis

We covered the basics of Predis before, and we’ll be using it in this case, too. Let’s install it with:

composer require predis/predis

Going further, the assumption is that we’ve absorbed the knowledge from the aforementioned introduction to Predis.

Since that post was published, minor differences were introduced (like a transition to namespaces) but the API we need is more or less the same.

Implementation

To make use of Redis in our app, we need to follow this procedure:

  • see if results for the current query exist in the cache
  • if yes, grab them
  • if no, fetch them, store them, forward them to the rest of the app

Thus, the implementation is extremely simple: under the “form submitted” check (the one that looks for the “search” param), we instantiate the Predis client, calculate the md5 hash of the executed search query, and then check if the results for it are already cached. If false, the previous flow continues, only, rather than end in:

$result = ...
$info = ...

we serialize and save the results directly into the cache. Then, immediately outside the block, we grab them from the cache and the app’s flow continues as usual. The changed part of the index.php file therefore looks like this:

// Check if the search form was submitted
if (isset($queryParams['search'])) {

    $redis = new Client();
    $hash = md5($_SERVER['QUERY_STRING']);
    if (!$redis->get($hash . '-results')) {

        $diffbot = new Diffbot(DIFFBOT_TOKEN);

        // Building the search string
        $searchHelper = new SearchHelper();
        $string = (isset($queryParams['q']) && !empty($queryParams['q']))
            ? $queryParams['q']
            : $searchHelper->stringFromParams($queryParams);

        // Basics
        $search = $diffbot
            ->search($string)
            ->setCol('sp_search')
            ->setStart(($queryParams['page'] - 1) * $resultsPerPage)
            ->setNum($resultsPerPage);

        $redis->set($hash . '-results', serialize($search->call()));
        $redis->expire($hash . '-results', 86400);
        $redis->set($hash . '-info', serialize($search->call(true)));
        $redis->expire($hash . '-info', 86400);
    }

    $results = unserialize($redis->get($hash . '-results'));
    $info = unserialize($redis->get($hash . '-info'));

After testing, we can see that it works like a charm – a query executed once is instant if we refresh the page, or execute another query and then come back to the previous one. Finally, we can add, commit, and push to deploy:

git add -A
git commit -m "Added Redis cache [deploy:production]"
git push origin master

That’s it! Our app’s newest version is now live and Redis is serving the cached data. Test it here!

Note: If you’re wondering how we got from development mode to production deployment in a single commit, you should read this.

Fine Tuning

For an additional performance boost, Predis recommends the installation of phpiredis, a PHP extension to “to lower the overhead of serializing and parsing the Redis protocol“. Seeing as we’re in full control of the server, why not?

cd ~
git clone https://github.com/redis/hiredis
cd hiredis
make
sudo make install
cd ~
git clone https://github.com/nrk/phpiredis
cd phpiredis
phpize && ./configure --enable-phpiredis
make
sudo make install

sudo touch /etc/php5/mods-available/phpiredis.ini
sudo sh -c 'echo "extension=phpiredis.so" > /etc/php5/mods-available/phpiredis.ini'
sudo php5enmod phpiredis
sudo service php5-fpm restart

This installed the prerequisites and enabled the extension. Now all we have to do is configure the Predis client to use the phpiredis connection. We need to replace:

$redis = new Client();

with

$redis = new Client('tcp://127.0.0.1', [
        'connections' => [
            'tcp'  => 'Predis\Connection\PhpiredisStreamConnection',
            'unix' => 'Predis\Connection\PhpiredisSocketConnection',
        ],
    ]);

That’s it! Our Redis installation is now even faster!

Conclusion

In this tutorial, we used Redis in combination with the Predis library to give an already deployed app the appearance of speed. We leverage the available RAM of our DigitalOcean droplet to save the results of a query once per day, and then return those results from cache rather than doing the round-trip to their origin. This does mean the results aren’t always fresh, but as per this post, the results aren’t refreshed more often than that anyway.

Hopefully, this tutorial showed you how simple it is to add an in-memory caching layer to your application, and will be of use when you need to shorten those loading times and reduce those server costs.

Any additional advice? Tips? Comments? Leave them below!

Frequently Asked Questions (FAQs) about Speeding Up Existing Apps with a Redis Cache

How does Redis improve the performance of my existing application?

Redis, which stands for Remote Dictionary Server, is an open-source, in-memory data structure store that can be used as a database, cache, and message broker. It improves the performance of your existing application by storing data in memory, reducing the time it takes to access data compared to traditional disk-based databases. This results in faster response times and improved user experience. Redis also supports various data structures such as strings, hashes, lists, sets, and more, providing flexibility in how you can store and manage data.

What are the prerequisites for using Redis with PHP?

To use Redis with PHP, you need to have PHP and Redis installed on your server. You also need to install a PHP extension for Redis, such as phpredis or Predis. These extensions provide an API for communicating with the Redis server from your PHP application. You can install these extensions using the package manager for PHP, known as Composer.

How do I set up a Redis server as a session handler for PHP?

Setting up a Redis server as a session handler for PHP involves configuring your PHP.ini file to use Redis for sessions. You need to specify the session.save_handler as Redis and provide the session.save_path, which is the connection string to your Redis server. This allows PHP to store session data in Redis, which can improve the performance of your application, especially if it relies heavily on session data.

How can I use Redis with PHP?

To use Redis with PHP, you first need to connect to the Redis server using the Redis extension’s connect or pconnect method. Once connected, you can use various methods provided by the extension to interact with Redis, such as get, set, lpush, rpush, and more. These methods correspond to the commands available in Redis and allow you to manipulate the data stored in Redis.

What is phpredis and how do I use it?

Phpredis is a PHP extension for Redis that provides an API for communicating with the Redis server. It supports all Redis commands and features, including transactions, pub/sub, and scripting. To use phpredis, you need to install it on your server and load it in your PHP application. You can then create a new instance of the Redis class and use its methods to interact with Redis.

How does Redis caching work?

Redis caching works by storing data in memory, which can be accessed much faster than data stored on disk. When a request is made for data, Redis first checks if the data is in its cache. If it is, Redis returns the data immediately, reducing the time it takes to retrieve the data. If the data is not in the cache, Redis retrieves the data from the database, stores it in the cache for future requests, and then returns the data.

How can I manage data in Redis?

You can manage data in Redis using the commands provided by Redis. These commands allow you to manipulate various data structures supported by Redis, such as strings, hashes, lists, sets, and more. For example, you can use the SET command to store a string, the HSET command to store a hash, the LPUSH command to add an element to a list, and so on.

How can I monitor the performance of Redis?

Redis provides several tools for monitoring its performance, including the INFO command, the MONITOR command, and the Slow Log. The INFO command provides information about the Redis server, including its memory usage, number of connections, and more. The MONITOR command allows you to see all the commands being sent to the Redis server in real time. The Slow Log records all commands that take longer than a specified amount of time to execute.

How can I secure my Redis server?

You can secure your Redis server by configuring a password, limiting connections to trusted clients, and running Redis in a protected environment. You can set a password using the requirepass directive in the Redis configuration file. This requires clients to authenticate before they can execute commands. You can also use the bind directive to limit connections to certain IP addresses.

How can I troubleshoot issues with Redis?

Troubleshooting issues with Redis involves checking the logs, monitoring the server, and understanding the error messages. The Redis logs provide information about the server’s operation and any errors that occur. The INFO and MONITOR commands can help you understand what’s happening on the server. If you encounter an error, the error message usually provides clues about the problem. You can also consult the Redis documentation or seek help from the Redis community.

Bruno SkvorcBruno Skvorc
View Author

Bruno is a blockchain developer and technical educator at the Web3 Foundation, the foundation that's building the next generation of the free people's internet. He runs two newsletters you should subscribe to if you're interested in Web3.0: Dot Leap covers ecosystem and tech development of Web3, and NFT Review covers the evolution of the non-fungible token (digital collectibles) ecosystem inside this emerging new web. His current passion project is RMRK.app, the most advanced NFT system in the world, which allows NFTs to own other NFTs, NFTs to react to emotion, NFTs to be governed democratically, and NFTs to be multiple things at once.

BrunoScacheOOPHPoptimizationperformanceperformance-toolsPHPredisspeed
Share this article
Read Next
Get the freshest news and resources for developers, designers and digital creators in your inbox each week