Redis: From Caching to In-Memory Data Structures
Nihal Gupta
Redis is one of the most popular data stores used for caching. But Redis is not just a cache. It is an in-memory data structure store that can be used for caching, sessions, queues, counters, leaderboards, rate limiting and many other use cases.
In this article, we’ll understand Redis from the beginning.
We will cover:
- what is Redis
- what does in-memory mean
- caching
- cache invalidation
- Redis key-value structure
- Redis data types
- basic Redis commands
- a simple caching example
What is Redis?
Redis is an in-memory data structure store. In-memory means the data is primarily stored in RAM which has much faster to read/write speeds compared to a disk
This fast access is one of the main reasons redis is so useful for caching. It also supports persistence, so in-memory doesn’t mean that data can never be written to disk.
What is caching?
Caching means storing frequently accessed data in a faster storage layer so that we don’t have to perform the operation again.
Suppose our application has:
Client -> Backend -> Database
Every time a user requests some data, our backend queries the db. This can become expensive when the same data is requested over 1000 times
Instead, we can use redis,
Client -> Backend -> Redis — (if cache miss) -> Database
If the data exists in redis, we can directly return it
Suppose we have a blog website. We have a route: GET /blogs
This route returns all the blogs, suppose the first user hits this route, there is no data in redis yet So the request hits redis , data is not found so it goes to db.
suppose the database takes 800ms to return the result, now we store this result in redis
blogs → [all blog data]
The next user requests /blogs, this time data is found in redis We don’t need to query the database, redis takes only 20ms to return the data
This is the basic idea behind caching.
Cache hit and cache miss
There are two important terms.
Cache hit : The requested data exists in Redis.
Request -> redis -> data found -> return response
Cache miss : The requested data doesn’t exist in Redis.
Request -> redis -> data not found -> db -> store in redis -> return response
The next request can then be served from Redis.
Cache invalidation
Now we have a problem, suppose our blog data is:
Database: blogs = [A, B, C]
and Redis also contains: blogs = [A, B, C]
Now a new blog is added, the database becomes: blogs = [A, B, C, D]
But Redis still contains: blogs = [A, B, C]
We somehow need to remove or update the old value, this is called cache invalidation
TTL — Time To Live
One simple way to handle cache invalidation is to give cached data an expiry time. for eg,
blogs → [A, B, C]
TTL → 24 hours
After 24 hours, redis automatically removes the key, when the next user requests /blogs:
Now the latest data is cached again, TTL is not the only way to handle invalidation
Redis stores data in key-value pairs
Now let’s understand redis itself, Redis stores data using:
KEY → VALUE
For eg: “user:1” → “Nihal”
Here, “user:1” is the key and “Nihal” is the value We use the key to access the value.
Naming convention for keys
You can technically name keys however you want. But it is common to use ‘:’ to separate different parts of the key.
For eg ,
user:1 user:2
user:2:email user:2:profile
product:123
product:123:reviews
This makes keys easier to understand and organize.
For example: user:123:email
clearly tells us: resource → user id → 123 field → email
Data types
The value associated with a key doesn’t have to be just a string, redis supports several data types
Some commonly used ones are:
- String
- List
- Set
- Hash
- Sorted Set
- Stream
- Geospatial
Each data type is useful for different problems, lets look at some of them
1. String
String is the simplest Redis data type.
For eg, user:1 → “Nihal”
To store it: SET user:1 “Nihal”
To retrieve it: GET user:1
we can also store numbers as strings and use redis commands to increment them, for eg,
SET views 100
INCR views
Now, views = 101
Strings are commonly used for caching simple values, counters, flags, etc.
# Store a value
SET user:1 "Nihal"
# Get a value
GET user:1
# Store only if the key doesn't already exist
SET user:1 "Nihal" NX
# Set a value with expiry of 60 seconds
SET user:1 "Nihal" EX 60
# Check whether a key exists
EXISTS user:1
# Delete a key
DEL user:1
# Get multiple values at once
MGET user:1 user:2 user:3
# Increment a numeric value
SET views 100
INCR views
# Decrement a numeric value
DECR views
2. List
A Redis List is an ordered collection, for eg,
tasks task-3 task-2 task-1
Add values from the left:
LPUSH tasks “task-1”
LPUSH tasks “task-2”
LPUSH tasks “task-3”
Get the values:
LRANGE tasks 0 -1
We can also remove values:
LPOP tasks RPOP tasks
This makes Lists useful for simple queue-like use cases.
# Add elements from the left
LPUSH tasks "task-1"
LPUSH tasks "task-2"
LPUSH tasks "task-3"
# Add elements from the right
RPUSH tasks "task-4"
# Get elements from index 0 to the last element
LRANGE tasks 0 -1
# Get the length of the list
LLEN tasks
# Remove from the left
LPOP tasks
# Remove from the right
RPOP tasks
Queue vs Stack using Redis List
We can build a queue using: LPUSH + RPOP
Items enter from the left and leave from the right.
This gives us FIFO: First In → First Out
For a stack, we can use: LPUSH + LPOP
The same side is used for both operations.
This gives us LIFO: Last In → First Out
here is an example of how redis is used in code :
This article only covers the basics. Once you understand these concepts, the next things worth learning are -
- TTL and expiration
- eviction policies
- RDB and AOF persistence
- replication
- Redis Sentinel
- Redis Cluster
- transactions
- distributed locks
- Pub/Sub
- Streams
- rate limiting
- memory management
Try adding Redis to one of your projects and implement a cache, a rate limiter, or a leaderboard, if you want to explore more about redis , the official documentation is the best place to start