There’s no discussion that Redis is one of the most consolidated caching tools today. Whenever someone wants to introduce a cache layer in their systems, Redis will be one of the names on the table.
For a while, I only associated Redis with caching solutions. However, I was missing a lot that I could get from this tool. About three and a half years ago was the first time I saw a solution that used Redis as a queue mechanism. As someone that had recently come from Java, I was having a hard time finding a solution where I could schedule jobs in Node.js. Talking about that with a few other teams with more experience in Node.js, I’ve come across “BullJS”, a Redis-based queue for Node. This library introduced me to a few solutions based on Redis capabilities, like rate limiting, job priority, job delay & scheduler, among a few other things.
By seeing the capabilities offered by this library, I started to wonder what other things I could get from Redis. I will likely write two (or three) articles on this to save you the trouble of reading them all at once. First, let’s start with some concepts on Redis and a few relevant data structures.
Do you need a caching tool? Redis. Do you need a message broker? Redis. Do you need a database? Redis. Do you need a stream engine? Still, Redis.
I’m not saying Redis is the perfect tool for all of the above, but depending on your use case, it might be the right fit. It includes transactions, pub/sub, failover mechanisms, and more.
Redis Serialization Protocol (RESP) is what powers Redis behind the scenes. According to the docs, a simple to implement, fast to parse, and human-readable protocol, usually running over TCP (but not tied to it). It’s aimed to be a request/response model. However, it contains two main exceptions:
- It allows clients to batch multiple commands. Which Redis refers to as Pipelining.
- When using a pub/sub subscription, the protocol will change its semantics to behave as a push protocol. Meaning that the server will push messages to the client as they arrive.
One curiosity on the protocol is that Redis keys are binary-safe. This means you can use strings, numbers, and binary values. Just keep in mind that they’re also case-sensitive. One thing that struck me is that keys are allowed to be up to 512MB. I honestly have a hard time finding use cases for that.
Basic Commands & Data Structures
Iterates over a collection of elements. You must be careful with this command since it can return too many elements in production and degrade your performance.
Use with caution.
Returns all keys matching a giving pattern.
Returns a given key exists.
Returns the internal encoding for the Redis object in a given key. This is used a lot internally to check if an operation is valid. For instance, Redis checking if the value is a number before doing a INCRBY or DECRBY operation.
Rename a key. An error is returned if key doesn’t exist.
It follows the same idea as a delete command, however, it’s a non-blocking operation, unlinking the key & freeing the memory associated with it in a separated thread.
Removes key in a blocking operation
Expiring data / Time to Live (TTL)
When setting expiration periods in your keys, you can mainly use three functions: PEXPIRE, EXPIRE, EXPIREAT. They will set the time to live in milliseconds, seconds, and Unix timestamps, respectively.
If you’re curious about the TTL for your key, you can check that using the TTL or PTTL commands.
And if at some point you decided that the key/value pair you have are really important, you can make them persistent using the PERSIST command.
Mostly used to store objects, typically used for rate limiting and session stores. Data manipulation commands are as follows:
- HGET – retrieves the hash field value. If the value is also passed, it returns a boolean informing whether the value is present or not
- HEXISTS – checks if a field exists in the hash
- HINCRBY – increments key value;
- HINCRBYFLOAT – increments field float value
- HSET – set key value
- HSETNX – only set field value if the key doesn’t exist, if the key already exists, this command has no effect;
- HKEYS – return all keys in the hash;
- HVALS – return all values in the hash;
- HDEL – delete a field stored at a key;
An ordered collection of strings that enables you to add elements to the left & right, it can be used for stacks and queues. In lists, there’s no nesting.
Good use cases are activity streams and queues (producers, consumers).
- LLEN – list length
- LPUSH – add an element to the left
- RPUSH – add an element to the right
- LPOP – remove an element from the left
- RPOP – remove an element from the right
- LRANGE – retrieves elements from a list. It’s used as (key, start, stop), where the key is the list, and start and stop are the indexes. In case you want to retrieve all elements, one can use a -1 as the stop index.
If you want to use the list as a stack, you can limit yourself to using the LPUSH and LPOP commands. If you want to use it as a queue, you can use RPUSH and LPOP commands.
Sets are an unordered collection of strings, in which you can do difference, intersection, and union operations. The data cannot be nested.
This is interesting for use cases like tag clouds, and unique visitors.
- SADD – add an element to set
- SMEMBERS – returns all the members of a given set
- SISMEMBER – returns whether the element is present in the set
- SREM – removes an element from a given set
- SPOP – removes a random element from the given set
- SDIFF – returns the difference between two sets
- SDIFFSTORE – stores the diff between two sets into a new set
- SINTER – returns the intersection between two sets
- SINTERSTORE – stores the intersection between two sets into a new set
- SUNION – returns the union between two sets
- SUNIONSTORE – stores the union between two sets into a new set
Sorted sets contain a field called score, which will be used to sort your data. The elements in your sorted set cannot repeat, the score is not considered for the uniqueness of an element.
For instance, let’s say you want your elements sorted by time so you can have a time-series, you can ensure that by having the minute the element was added as the score, and this will enforce that your elements are always sorted by time.
Your elements will always be sorted, with efficient inserts.
- ZRANGE – returns the elements from start to stop, enabling you to ask in reverse order, also returning the scores.
- ZRANK – returns the rank of an element in the sorted set
- ZADD – adds the elements with the score to the sorted set
- ZCOUNT – how many elements the set has with scores between min and max parameters
- You will have lots of commands that look like the set commands, and a few extra ones due to the nature of the sorted sets.