1.Strings, hashes, lists, sets, and sorted sets
Redis is not a key-value store where every value is a blob — the value has a *type*, and the type decides which commands are available and what they cost. A **string** holds bytes and supports atomic `INCR`. A **hash** holds field-value pairs, so you can read or write one field without fetching the whole object. A **list** is an ordered sequence with cheap pushes and pops at either end, which is what makes it a queue. A **set** holds unique members with O(1) membership tests and set algebra. A **sorted set** gives every member a numeric score and keeps them ordered by it, which is how you build a leaderboard or a sliding-window rate limiter. Picking the right type is most of what "using Redis well" means.
Key features
- Strings hold bytes up to 512 MB and support atomic `INCR`/`INCRBY`/`DECR` — the counter primitive, with no read-modify-write race
- Hashes store field-value pairs under one key: `HSET`/`HGET`/`HGETALL`/`HINCRBY`, so one field updates without transferring the object
- Lists are doubly-linked: `LPUSH`/`RPOP` are O(1) at the ends, `BRPOP` blocks until an element arrives — that pair is a work queue
- `LRANGE key 0 -1` reads the whole list and is O(n); a list is a queue, not something to scan repeatedly
- Sets give O(1) `SADD`/`SISMEMBER` plus `SINTER`/`SUNION`/`SDIFF` — membership and set algebra without moving data to Python
- Sorted sets attach a float score to each member and keep them ordered: `ZADD`, `ZREVRANGE` for leaderboards, `ZRANGEBYSCORE` for time windows and delayed work
The five types, and the Django-shaped problem each one solves
- One Redis key
- String and Hash — a value, or an object
- INCR is atomic — a counter with no read-modify-write race
- HSET writes one field — no read, no lost update from a concurrent writer
- JSON-in-a-string loses both — every field change becomes read-modify-write
- List and Set — ordered, or unique
- LPUSH / BRPOP — O(1) at both ends, and BRPOP blocks — a work queue
- SISMEMBER is O(1) — membership without moving the collection to Python
- LRANGE 0 -1 is O(n) — a list is a queue, not something to scan repeatedly
- Sorted set — a score per member, kept in order
- ZREVRANGE 0 9 — a top-ten leaderboard, already sorted
- score = a timestamp — ZRANGEBYSCORE gives a sliding window or a delayed queue
- ZADD + ZREMRANGEBYSCORE + ZCARD — an exact sliding-window rate limiter, no scan
Command
r = redis.Redis.from_url(settings.REDIS_URL)
r.hset("user:42", mapping={"plan": "pro", "seats": 5})
r.zadd("leaderboard", {"ana": 1450})The value has a type, and the type decides both the commands and the cost. `INCR` on a string and `HSET` on a hash field are atomic single writes — JSON in a string turns every change into a lossy read-modify-write. Lists are O(1) at the ends and O(n) to scan, so they are queues. Sets give O(1) membership. Sorted sets are the one to reach for more often: score by time and you get sliding windows, delayed work, and leaderboards without a scan. And never `KEYS` in production — Redis is single-threaded, so it blocks everyone.