If you're looking to improve the performance of your Laravel web application, then implementing a cache layer could be a good option. As a very high-level overview, using a cache allows data to be stored and then quickly retrieved. Common use cases include replacing slow or repetitive database queries, or even entire HTTP responses.
Although Laravel comes with a great, easy to use solution for server-side caching, there is still a lot to consider when implementing a cache layer in your application. What should be cached? How long should it be cached for? Where should we cache it?
The answer to all of those questions is usually "it depends on your circumstances", however we're going to tackle the last one. The Laravel cache service offers multiple cache "drivers", allowing different back-end storage engines to be used. When choosing which one is best to use, it is again likely to depend on your circumstances.
Do you need a cache that is quick and easy to setup / maintain? Where should the cache store be hosted? Should it be available to multiple servers? Is performance the ultimate priority? Performance is what I'll be focusing on.
Cache drivers: APC vs Database vs File vs Memcached vs Redis
The cache drivers we will be focusing on are those that come with the default Laravel installation:
- APC (APCu)
- Database (this cache driver uses Laravel's database engine, which is again driver based - we'll be using two - MySQL & SQLite)
- File
- Memcached
- Redis
Laravel also comes with two other drivers that I won't be testing:
- Array (data is stored in memory for the current PHP process (e.g. a HTTP request, running an Artisan command) so wouldn't be suitable for most people, it's mainly used for tests).
- DynamoDB (an AWS proprietary NoSQL key-value based database, which you're only likely to use if you host all of your infrastructure on AWS)
Testing Laravel cache drivers
I'll be testing the read and write speed of each cache driver with 5 different types of commonly cached data:
- Integer - a single number
- Stats - a JSON encoded string of 100 numbers
- Paragraph - a text string of 150 words
- Article - a text string of 2500 words
- Webpage - an entire HTML webpage (I've used the webpage for the most popular BBC news article at the time of writing)
I've written an Artisan command (available on GitHub) in a default Laravel 6 installation to make 1,000 reads and 1,000 writes for each of the above data types. For each test, the time taken to complete the action will be recorded in microseconds. This will then be used to calculate an average time for both reading and writing for each data type, for each cache driver.
As for server environment, I'll be testing on the lowest spec VPS available from Linode(1GB RAM / 1 CPU) that has been freshly provisioned by Laravel Forge, using the default configuration that it provides for PHP 7.3. There will be minimal load on the server as there will be no inbound HTTP traffic and only the default services running.
Laravel cache driver speed tests
APC cache driver
Test | Read | Write |
---|---|---|
Integer | 0.770 | 2.416 |
Stats | 0.679 | 2.367 |
Paragraph | 0.631 | 2.128 |
Article | 0.676 | 2.433 |
Webpage | 0.694 | 2.120 |
Database (MySQL) cache driver
Test | Read | Write |
---|---|---|
Integer | 642.146 | 390.471 |
Stats | 714.820 | 412.997 |
Paragraph | 811.301 | 383.895 |
Article | 1151.277 | 412.056 |
Webpage | 3643.461 | 765.362 |
Database (SQLite) cache driver
Test | Read | Write |
---|---|---|
Integer | 1432.321 | 137.590 |
Stats | 1353.896 | 137.828 |
Paragraph | 1132.085 | 161.893 |
Article | 1194.738 | 173.334 |
Webpage | 2159.022 | 253.023 |
File cache driver
Test | Read | Write |
---|---|---|
Integer | 147.066 | 33.503 |
Stats | 132.957 | 35.460 |
Paragraph | 153.218 | 35.498 |
Article | 158.506 | 46.110 |
Webpage | 395.767 | 165.265 |
Memcached cache driver
Test | Read | Write |
---|---|---|
Integer | 63.500 | 35.403 |
Stats | 63.671 | 38.796 |
Paragraph | 63.031 | 37.054 |
Article | 138.603 | 74.496 |
Webpage | 1125.189 | 842.751 |
Redis cache driver
Test | Read | Write |
---|---|---|
Integer | 57.925 | 54.762 |
Stats | 55.013 | 54.390 |
Paragraph | 60.338 | 56.676 |
Article | 44035.899 | 78.775 |
Webpage | 1192.044 | 378.57 |
Performance observations
From looking at the above results, it's clear that the APC drivers is by far the fastest Laravel cache driver in our tests with speeds far better than any of the others. There was little difference in the timings between the smaller and larger datasets.
Next up, the two memory based cache stores Memcached and Redis both produced similar results - although the write speeds were slightly better in Memcached and the read speeds were slightly better in Redis. You may have noticed the rather high read speed for the "article" data in Redis. Having tried slightly different amounts of data and different servers in different data centres, this anomaly still occurred.
The Laravel file cache driver came out in third place, with speeds roughly double those achieved with Redis and Memcached.
The second slowest Laravel cache driver appears to be the database driver connected to a MySQL (5.7) database, with the speeds being roughly 5 times slower than those achieved with Redis and Memcached.
Finally, the slowest cache driver in our tests was the the database driver connected to SQLite (a flat file based database) - the speeds appear to be approximately 10 times slower than those achieved with Redis and Memcached.
So, which Laravel cache driver should I use?
As I mentioned in the start of this article, the answer is probably "it depends".
APC obviously gave the best results by far, however it can only be used by a single PHP process. So if you need you cached data to be accessed via HTTP requests and CLI, or even across multiple servers, you won't be able to use it. For simple, single server Laravel applications, it is probably your best choice.
If you don't mind a little extra server maintenance (having to install & maintain additional services), the memory based cache drivers Memcached and Redis are great options. A server provisioning tool like Laravel Forge with make the initial setup easier, and a fully managed hosted service will greatly reduce the amount of additional maintenance. If you'll be caching large amounts of data, many server providers offer high memory instances.
Whilst the the file based driver is incredibly easy to setup and offers OK speeds, you may run into problems with disk IO if your application will have high activity levels. It might be a good choice to start with, but you'll likely need to replace it with a different cache store if your application sees an increase in traffic.
Finally, we come to the database cache drivers. Again, they are a good option if you want a quick and easy solution as you're likely already running a database. If top-level performance is the priority, these won't be the answer for you as they tend to be the slowest, and will require other server resources - such as CPU usage for MySQL.
In conclusion
There is so much to consider when implementing a cache layer, and whilst better performance is likely to be your priority, you'll need to do further research, or even better, your own testing to get the best results.
You can find my test script here: https://github.com/gbuckingham89/laravel-cache-performance.
If you want to discuss Laravel caching in more detail, feel free to get in contact.