0% completed
1.4 Atomicity, Real Features, and Encodings
On This Page
Atomicity from Serial Execution
Building Real Features
Leaderboard
Sliding Window Rate Limiter
Job Queue and Task Distribution
Session Store
Activity Feed and Timeline
Unique Counts and Analytics
Distributed Lock
Autocomplete and Search
Internal Encodings and Memory
Limits and Misuse
Using This in an Interview
FAQs
Key Takeaways
Sorted sets, hashes, lists, sets, streams and the other types make Redis a data structure server: the server understands each value and changes it in place. The previous lesson showed that model on a season leaderboard, leaderboard:season7, and toured each structure with the problem it solves.
This lesson builds on that tour. It explains why each command is atomic and how transactions, scripts and functions extend that guarantee to several steps. It then maps common product features to one structure each, shows the compact encodings Redis chooses for small values, and lists the limits of the model.
Atomicity from Serial Execution
Redis executes commands on a single thread, one at a time, so each command runs to completion with no other client interleaved. That is why INCR, LPUSH, SADD and ZINCRBY are safe under heavy concurrency without any locking in the application.
Some operations span several commands. For these, MULTI and EXEC form a transaction: Redis queues the commands and runs them as one uninterrupted block. If one command in the block fails at run time, the others still run, because Redis transactions do not roll back. WATCH adds optimistic locking: if a watched key changes before EXEC, the transaction aborts and the client retries.
A Lua script goes further, because it runs atomically on the server and can read, branch and write across several keys without network round trips between steps. Since Redis 7.0, functions offer the same atomic execution as named libraries that the server stores and replicates. A script was long the standard way to write "increment the counter, and set its expiry if this is a new window" as one step. Since Redis 8.8, the INCREX command does that bounded increment with expiry in a single command.
Because everything runs in sequence, a long command blocks everyone, so structures should stay a reasonable size and large operations should be split into steps. This is the price of the simple execution model.
Building Real Features
Each feature below maps a requirement to one structure. The chapters on caching, coordination and real-time patterns develop each one in detail.
Leaderboard
The leaderboard is a sorted set keyed by game and season, such as leaderboard:season7, with player IDs as members. Updating a score is ZINCRBY, the top N is ZRANGE with REV, and a player's rank is ZREVRANK. ZINCRBY and ZREVRANK run in O(log N), and the top-N query costs O(log N + M), where M is the number of entries returned, so all three scale to millions of players.
Sliding Window Rate Limiter
Each client gets a sorted set whose members are request IDs, scored by the request timestamp. On each request, the limiter removes entries older than the window and counts the remaining entries. If the count is under the limit, it adds the new request and allows it; otherwise it rejects it. All of these steps run inside one script, so they are atomic. A simpler fixed window uses a counter with an expiry, and since Redis 8.8 INCREX implements that counter in one command.
Job Queue and Task Distribution
A list works when producers push jobs and workers wait with blocking pops. A stream with a consumer group works when you need acknowledgment, retries and a view of pending work. Delayed jobs use a sorted set scored by the time each job should run, with a poller that moves due jobs to the work queue.
Session Store
Each session is a hash under a key that has an expiry, such as session:abc123. Reads and single-field updates are fast, and expiry removes stale sessions automatically.
Activity Feed and Timeline
Each user has a list or sorted set that holds the IDs of recent items, trimmed to a fixed length. Writing a post pushes its ID to the feeds of the author's followers, and reading a feed is a range query.
Unique Counts and Analytics
A HyperLogLog counts approximate unique visitors, a bitmap records exact daily activity per user ID, and a set gives exact membership when the number of members is modest.
Distributed Lock
A lock is a string written with SET and the NX and PX options, holding a random token that identifies its owner. The owner releases it only if the token still matches: since Redis 8.4, DELEX key IFEQ token does this in one command, and earlier versions use a short Lua script that compares and deletes. A simple lock like this has limits when servers fail, which the Distributed Locks lesson covers.
Autocomplete and Search
A sorted set with lexicographic ranges completes prefixes, and the Query Engine handles full-text and vector queries.
Internal Encodings and Memory
Redis chooses a compact internal form for each value automatically. Small hashes, lists, sets and sorted sets use a listpack, a single contiguous block of memory that stores the elements one after another. Sets that hold only integers use an intset, a sorted array of integers. For example, a hash stays a listpack while it has at most 512 fields and no value longer than 64 bytes, and a sorted set stays a listpack up to 128 members.
When a collection grows past such a threshold, Redis converts it to a full structure. Hashes and sets become hash tables. A large list becomes a quicklist, a linked list of listpack nodes of at most 8 KB each. A large sorted set combines a skip list, which keeps members in score order for range queries, with a hash table that finds a member's score directly.
Several practical rules follow. Many small objects stored as hashes can cost far less memory than one key per field. Memory is the main resource, so measure it with MEMORY USAGE and watch for big keys. Choose a structure by access pattern first and then check its memory cost. Expiry, eviction policies and sensible key design matter because everything lives in RAM, as the next lesson explains.
Limits and Misuse
- Memory bound. The dataset must fit in RAM, and replication and forking can need extra room.
- Not a relational database. There are no joins or ad hoc queries across structures. You design keys and structures for the queries you know you will need, and you often store the same data in several shapes.
- Big keys. A set with ten million members or a list with millions of entries makes some commands slow and blocks the server. Break large structures into smaller ones, and iterate with commands such as SSCAN and HSCAN.
- Durability is a choice. Snapshots and the append-only file provide persistence with trade-offs, and Redis is not a direct replacement for a durable system of record without careful configuration.
- Wrong structure, wrong cost. A queue stored in a string that you rewrite, or a leaderboard in a list that you sort in the application, loses the benefit of the model.
- Cluster constraints. In Redis Cluster, a multi-key operation requires all its keys to be in the same hash slot, so structure design must account for sharding.
Using This in an Interview
Say that each command is atomic because Redis runs commands one at a time, and that MULTI and EXEC, Lua scripts and functions extend this to several steps. Then add that each feature maps to one structure, which Redis stores in a compact encoding while it is small.
Are multi-step operations atomic? Single commands always are; for several steps, use MULTI and EXEC, a Lua script or a function, remembering that transactions do not roll back.
What are the limits? The dataset must fit in memory, big keys block the single thread, and data must be shaped around known access patterns.
FAQs
Are Redis operations really atomic?
Each individual command is, because commands run one at a time. A group of commands runs without interruption inside MULTI and EXEC, a Lua script or a function, but a transaction does not undo earlier commands if a later one fails. A read followed by a write sent as separate commands is not atomic unless you use one of those mechanisms.
Can Redis replace my database?
Sometimes it can, for data that fits in memory and is accessed by key, with persistence configured to match. More often it works beside a primary database and holds frequently read data, counters, queues and precomputed views. The decision depends on durability needs, data size and how flexible your queries must be.
Key Takeaways
- Atomicity comes from serial execution. Single commands are atomic, and transactions, Lua scripts and functions extend that to several steps, without rollback.
- Each feature maps to one structure. A leaderboard is a sorted set, a session is a hash with an expiry, a lock is a string written with NX and PX, and a delayed job queue is a sorted set scored by run time.
- Compact encodings save space. Listpacks, intsets, HyperLogLog and bitmaps make small objects and large-scale counting cheap.
- Design for known queries. Redis has no joins, so shape keys and structures around the questions your application asks, and keep every key a reasonable size.
Reading Progress
0%
On This Page
Atomicity from Serial Execution
Building Real Features
Leaderboard
Sliding Window Rate Limiter
Job Queue and Task Distribution
Session Store
Activity Feed and Timeline
Unique Counts and Analytics
Distributed Lock
Autocomplete and Search
Internal Encodings and Memory
Limits and Misuse
Using This in an Interview
FAQs
Key Takeaways