I’m facing a cache invalidation problem in Redis, particularly with relational data.
For example, I have a User object:
{
"id": 1,
"name": "John"
}
And a UserClass object:
{
"user_id": 1,
"class_id": 3
}
I have an API that retrieves all users by class_id
, and I store this data in Redis using the key users:class:3, which contains an array of users. I also have an API that retrieves a user by ID using the key user:1.
The problem arises when someone updates, inserts, or deletes user data. I need to remove all corresponding user data from the cache. Removing the user by the ID key is straightforward, but to invalidate the cache for the user by class, I currently have to query the database to get all classes the user is associated with and then remove the corresponding keys in Redis one by one using class_id
.
This approach doesn’t seem optimal, especially if the user has other relationships beyond classes. Each time, I would need to query the database again to find the corresponding keys, leading to a lot of operations. Do you have any suggestions on how to solve this problem more efficiently?
2
Answers
There are various options i can think of:
Option-1:
Instead of querying the database to find all related keys, you can maintain sets in Redis that track these relationships. For example:
Option-2
In addition to storing the data, maintain a reverse mapping for cache invalidation. For example:
There are other options, too, depending on the application and environment. For example, the SCAN command can scan with a pattern match(users:class:*) to find and delete related keys; however, this would be highly non-performant. We can also use Redis pipelines or multi-exec commands to invalidate multiple keys in one go.
Not sure if you are using Redis client-side caching or not when referring to invalidation, but the data modeling problem can be approached as follows.
You want the ability to classify users by
class_id
, and retrieve data byclass_id
or using additional relations.Your approach is manual secondary indexing, which is inefficient due to the effort to maintain relationships, make scans, serialize and deserialize data in the string.
I suggest using the Redis Stack capabilities (which will also be available in the standard Redis Community Edition 8).
Then you would create an index as follows:
Insert data with:
And filter by class or attribute
These commands are available also in the Redis supported client libraries. You can read more about the search and query capability here. If you’d rather user JSON to model your data, you can do that as well.
This should help you model and search your data without any overhead or maintenance effort.