Ready to squeeze even more speed out of Redis?

Connecting to Redis via Unix sockets is usually faster than connecting via TCP/IP.

That’s because in general, Unix sockets have much less overhead. They’re essentially just files that Unix-based systems can read. The downside of that being that only local connections are possible.

At 10M+ pulls, Redis is the fourth most downloaded Docker image, and most systems are set up to connect to Redis via Docker’s own TCP bridge networking.

Docker’s own TCP bridge networking is all fine and dandy, but wouldn’t you like to squeeze out some more speed?

Granted, the increase is small, but it’s not insignificant. Here are some rough comparisons from my own tests:

time redis-benchmark -h 127.0.0.1 -p 6379

48.92 real 36.43 user 11.85 sys

time redis-benchmark -s /tmp/redis.sock

42.90 real 34.96 user 7.42 sys

A 14% speed difference is significant and we should take advantage of this even when using Docker.

But hang on a minute, aren’t Docker containers self-contained and separate? How can we use Unix sockets in that kind of environment?

The answer is that we need to take advantage of shareable Docker volumes.

Say we have a PHP container that runs our app code, with a Redis client built in, that connects to a Redis container.

We need to create a separate container, purely to be the base for a volume we’re going to share with PHP and Redis.

We’ll put the volume on /tmp/docker/ , and tell Redis to put the redis.sock file in that folder.

Wonderfully, this generic folder may also be used for any other files or data you want to share across containers.

We can use the busybox image for this volume container because this container won’t actually be doing anything other than holding the volume.

In the config for the PHP and Redis containers, we use the volumes_from option to extra the same volume from the busybox container.

So our docker-compose.yml file will look a bit like this:

services:

tmp:

image: busybox

command: chmod -R 777 /tmp/docker

volumes:

- /tmp/docker/ php:

image: php

// other app bootstrapping options

volumes_from:

- tmp redis:

image: redis

command: redis-server /etc/redis.conf

volumes:

- /redis/redis.conf:/etc/redis.conf

volumes_from:

- tmp

The important bit is the tmp service and the volumes_from options in the other containers. This is just an illustrative example and of course assumes that you’re correctly copying across redis.conf into the Redis container.

You do need to make sure that Redis is being started with redis.conf , because we need to explicitly tell Redis to create a socket file available for connections.

In your redis.conf file, add the following lines:

unixsocket /tmp/docker/redis.sock

unixsocketperm 777

This will tell Redis to create /tmp/docker/redis.sock upon startup and give it file permissions of 777. I’ve no doubt that ‘777’ will be controversial but it’s up to you — I find that in a contained Docker environment this is perfectly safe.

Then because of the Docker volume we set up, when we up these containers, /tmp/docker/ will be available to the PHP container too, and we can access it in our app code.

Then in your PHP application, tell it to connect to Redis via this Unix socket. If you’re using the popular Predis package, you would do this:

$client = new Predis\Client([

'scheme' => 'unix',

'path' => '/tmp/docker/redis.sock',

]);

If you’re using Laravel, you need to do a bit more work, because the default setup assumes you want to connect to Redis via TCP. So in your .env file, add the following:

REDIS_SCHEME=unix

REDIS_PATH=/tmp/docker/redis.sock

Then in config/database.php , edit the 'redis' section so it looks like the following:

'redis' => [ 'client' => 'predis', 'default' => [

// 'host' => env('REDIS_HOST', '127.0.0.1'),

// 'password' => env('REDIS_PASSWORD', null),

// 'port' => env('REDIS_PORT', 6379),

'scheme' => env('REDIS_SCHEME'),

'path' => env('REDIS_PATH'),

'database' => 0,

], ],

Live Results

I did the benchmark again, except this time within the setup of one of my apps that runs PHP and relies heavily on Redis.

I was a bit naughty and ran these on my live production server, but I’m of the frame of mind that this is a solid test of how my production environment holds up under load.

time --verbose docker-compose exec redis redis-benchmark -h 127.0.0.1 -p 6379

Command being timed: “docker-compose exec redis redis-benchmark -h 127.0.0.1 -p 6379”

User time (seconds): 0.35

System time (seconds): 0.09

Percent of CPU this job got: 0%

Elapsed (wall clock) time (h:mm:ss or m:ss): 1:21.64

Average shared text size (kbytes): 0

Average unshared data size (kbytes): 0

Average stack size (kbytes): 0

Average total size (kbytes): 0

Maximum resident set size (kbytes): 25344

Average resident set size (kbytes): 0

Major (requiring I/O) page faults: 31

Minor (reclaiming a frame) page faults: 14686

Voluntary context switches: 1229

Involuntary context switches: 293

Swaps: 0

File system inputs: 22640

File system outputs: 0

Socket messages sent: 0

Socket messages received: 0

Signals delivered: 0

Page size (bytes): 4096

Exit status: 0

time --verbose docker-compose exec redis redis-benchmark -s /tmp/docker/redis.sock

Command being timed: “docker-compose exec redis-server redis-benchmark -s /tmp/docker/redis.sock”

User time (seconds): 0.37

System time (seconds): 0.04

Percent of CPU this job got: 0%

Elapsed (wall clock) time (h:mm:ss or m:ss): 1:06.77

Average shared text size (kbytes): 0

Average unshared data size (kbytes): 0

Average stack size (kbytes): 0

Average total size (kbytes): 0

Maximum resident set size (kbytes): 25232

Average resident set size (kbytes): 0

Major (requiring I/O) page faults: 46

Minor (reclaiming a frame) page faults: 14708

Voluntary context switches: 1167

Involuntary context switches: 294

Swaps: 0

File system inputs: 21024

File system outputs: 0

Socket messages sent: 0

Socket messages received: 0

Signals delivered: 0

Page size (bytes): 4096

Exit status: 0

The TCP connection makes the benchmark 22% longer than the test using Unix sockets.

I hope this post helps you, if you have any comments don’t forget to hit that ‘Response’ button below, maybe even give me a clap if you feel like this post has helped you. 👍🏻⚡️💪🏻