Key-value and the cache
In short: A key-value store such as Redis is an in-memory dictionary that returns a value by its key very fast, which makes it a good cache, session store and counter.
Problem
The page that asks the same thing thousands of times
The shop's home page shows the three bestselling products. This is the query:
It joins three tables and adds up all the order rows. With 2000 visits a minute the database runs it 2000 times, while the answer changes once every few hours. The cart and the login also ask the database the same thing on every page. You need a place where the ready answer is fetched in a fraction of a millisecond.
The idea
A key and a value
A key-value database is a dictionary: every value has a key, and you get it by the key. There are no tables, columns or queries by value. That is why it is so fast: Redis keeps everything in memory and answers in microseconds.
The field is a simulator with the Redis commands. Keys are usually written with colons, so that it is clear what is in them:
GET on a key that is not there gives (nil). The value is text, even when it is a number: the database does not know that 7.90 is a price.
Try it
A view counter
INCR increments the number in a key by 1 and returns the new one. If the key is not there, it starts from 0. Two pages that count at the same time do not get in each other's way: every command in Redis runs to the end before the next one.
Product 7 was viewed three times and product 9 once. Count the views in the keys views:product:7 and views:product:9.
Solution
INCR views:product:7
INCR views:product:7
INCR views:product:7
INCR views:product:9
Four times INCR, three for 7 and one for 9. The check reads both keys at once with MGET and sees "3" and "1". With GET and then SET with the new number, two pages at once would lose a view. INCR is one operation.
The idea
A cache that expires
A cache is a copy of an answer that is expensive to compute. The program first looks in the cache. If there is nothing there, it asks the database and puts the answer in the cache for a set time (EX in seconds). That time is the TTL (time to live), and TTL shows how much is left:
After 60 seconds the key disappears on its own, and the next visit asks the database again. So the cache is never more than a minute stale, and the database runs the query once a minute, not 2000 times.
More precisely, if you are curious
SLEEP is a command of the simulator only: it moves its clock forward, so that you do not really wait. In the real Redis time just passes. The simulator sorts the answer of SMEMBERS and KEYS alphabetically, while Redis promises no order at all. And KEYS is not run on a real server with millions of keys: it goes through all of them and blocks every other command. There you use SCAN.
See it
The stale copy
The price of the coffee drops from 14.90 € to 12.90 €. The database is updated, but the cache still keeps the old page for another 10 minutes:
This is the most common mistake with a cache: the copy differs from the truth. So the program that changes the product in the database immediately deletes its copy in the cache (DEL). The next visit finds nothing, asks the database and stores the new price. The TTL stays as a safety net, in case you forget some time.
Try it
A new price for the cheese
The cache holds the pages of the white cheese (product 7) and the butter (product 9). The price of the cheese just changed in the database. Make the next visit to the cheese get the new price, without touching the butter.
Solution
DEL cache:product:7
DEL cache:product:7. FLUSHALL would also remove the old price, but everything else too: the whole site heads to the database at once. And writing the new price straight into the cache means two sources of truth: if the write to the database fails, the cache lies.
The idea
Not only text
A value in Redis can also be a structure. Each has its own commands:
- hash (
HSET,HGET,HINCRBY,HGETALL): fields under one key, like a cart with a product and a quantity; - list (
LPUSH,LRANGE,LTRIM): ordered values, like the recently viewed; - sorted set (
ZADD,ZINCRBY,ZREVRANGE): members with a score, ordered by it, like a leaderboard.
The cart of customer 42 (product 9, butter, two pieces, and product 5, milk, one), which expires after a week without activity:
The last three viewed products. LPUSH puts in front, and LTRIM cuts everything after the third:
The pieces sold in October, ranked:
With equal scores Redis orders the members alphabetically (here in reverse, because the leaderboard goes from the top down).
Exercise
A session, a cart and a leaderboard
1. Customer 42 logs in. The site gives them the session number 9f2c and keeps in Redis who they are.
Store the customer's number (42) in the key session:9f2c so that the session expires on its own after 30 minutes.
Solution
SET session:9f2c 42 EX 1800
SET session:9f2c 42 EX 1800: 30 minutes are 1800 seconds. A session without expiry stays forever if the customer just closes the window, and the memory of Redis fills up with dead sessions.
2. The cart of customer 7 is in the hash cart:7: the field is the product number, and the value is the quantity.
Customer 7 puts 2 packs of butter (product 9) and 1 milk (product 5) in the cart. Then they add one more butter and remove the milk.
Solution
HSET cart:7 9 2
HSET cart:7 5 1
HINCRBY cart:7 9 1
HDEL cart:7 5
HSET cart:7 9 2, HSET cart:7 5 1, HINCRBY cart:7 9 1 and HDEL cart:7 5. One field is left: product 9 with 3 pieces. HINCRBY changes the quantity in place, without reading the old one.
3. The key rating holds the average rating of every product.
Show the three products with the highest rating, together with the ratings, from the highest down.
Solution
ZREVRANGE rating 0 2 WITHSCORES
ZREVRANGE rating 0 2 WITHSCORES: from the first (0) to the third (2), in reverse order, with the scores. ZRANGE gives the lowest ones, and without WITHSCORES the ratings are not shown.
The idea
When key-value, when not
A key-value database is good when you always know the key: a cache, sessions, carts, counters, rate limiting ("at most 5 login attempts a minute"), job queues. It is not good when you ask by value ("all products under 5 €"), when the data is connected ("the customer's orders with their products") and when losing it is not acceptable. That is why it usually sits next to the SQL database, not instead of it: the database is the truth, and Redis is its fast copy.
More precisely, if you are curious
Besides Redis (and Valkey, which is compatible with it), key-value databases are Memcached (a cache only, without structures), DynamoDB in AWS (with a key and a sort key, on disk, distributed) and etcd (for cluster settings, with a focus on consistency). Redis keeps the data on disk in two ways: a snapshot every few minutes (RDB) or a log of every command (AOF).
Test
Check yourself
Related lessons
Section titled "Related lessons"- Builds on: Many tables, and a table joined to itself · UPDATE: changing rows · Connecting to the database from code · OLTP and OLAP
- Continues in: Document databases · Replication, sharding and CAP · How to choose a database
- Tasks on this topic: Kinds of databases