Choose Lists, Sets, Streams, or Sorted Sets from ordering, membership, replay, acknowledgment, and score semantics—not command convenience.
Choose List vs Set vs Stream vs Sorted Set from Ordering, Delivery, Replay, and Membership Needs
Choose hashes, Redis JSON, or many keys from structure, update/query patterns, expiration granularity, memory/cardinality, indexing, ACL boundaries, and migration evidence.
Learning outcomes
AtlasMart has four superficially similar needs: a recent ordered activity feed, a unique VIP audience, durable fulfillment work with retries, and delayed jobs scheduled by time. Choosing a structure because one command looks convenient creates hidden reliability and cost problems. Choose from semantics first.
Choose a List when ordered deque behavior is primary and replay/ack state is not.
Choose a Set when exact unique membership is primary and order is irrelevant.
Choose a Stream when retained entries, IDs, consumer groups, pending state, acknowledgment, and replay matter.
Choose a Sorted Set when score-ordered ranking/scheduling/range queries matter.
Document migration, retention, persistence, Cluster, retry, and observability consequences of the choice.
All Chapter 05 mandatory labs reuse the disposable Chapter 01
environment: Redis Open Source 8.10.1 from Docker
Official Image redis:8.10.1, container
atlasmart-redis-ch01, standalone topology, host
publication 127.0.0.1:6379, TLS disabled only
because traffic stays on loopback, default ACL user disabled,
named ACL users atlasmart-app and
academy-admin, logical database 0, AOF with
appendfsync everysec plus RDB snapshots,
persistent /data volume, and no explicit Redis
maxmemory limit or eviction policy. The
application ACL is restricted to ~atlasmart:* and
normal read/write/connection categories. The primary interface
is the redis-cli shipped in the same 8.10.1
image, so server and CLI versions stay aligned. Mandatory
examples use only bounded synthetic keys under
atlasmart:ch05:*.
1. Start with the invariant, not the command
| Question | List | Set | Stream | Sorted Set |
|---|---|---|---|---|
| Primary order | Sequence by list position | None | Entry ID order | Score then member ordering |
| Uniqueness | No | Yes, exact member uniqueness | No inherent member uniqueness | Unique member with mutable score |
| Blocking wait | Yes | No | Yes for reads | No direct queue-block primitive |
| Replay/history | Only retained list elements | Membership only | Yes, retained entries/IDs | Range by score/rank while retained |
| Consumer acknowledgment | No built-in | Not applicable | Consumer groups + XACK | Application-built |
| Pending/reclaim | Application-built | Not applicable | PEL + claim mechanisms | Application-built |
| Best-fit examples | bounded feed/simple queue | tags/audiences/dedupe | events/work processing | leaderboard/delayed schedule |
2. Same business story, four different models
Model one AtlasMart fulfillment event in four structures and inspect what each one remembers.
docker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 redis-cli --user atlasmart-app DEL atlasmart:ch05:model:list atlasmart:ch05:model:set atlasmart:ch05:model:stream atlasmart:ch05:model:zsetdocker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 redis-cli --user atlasmart-app RPUSH atlasmart:ch05:model:list order:8001docker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 redis-cli --user atlasmart-app SADD atlasmart:ch05:model:set customer:vip:88docker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 redis-cli --user atlasmart-app XADD atlasmart:ch05:model:stream "*" order order:8001 state queueddocker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 redis-cli --user atlasmart-app ZADD atlasmart:ch05:model:zset 1893456000 order:8001docker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 redis-cli --user atlasmart-app TYPE atlasmart:ch05:model:listdocker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 redis-cli --user atlasmart-app TYPE atlasmart:ch05:model:setdocker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 redis-cli --user atlasmart-app TYPE atlasmart:ch05:model:streamdocker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 redis-cli --user atlasmart-app TYPE atlasmart:ch05:model:zset
The commands all “store something,” but the data structures preserve different facts: sequence, membership, event history, or score order. Migration between them is a semantic migration, not merely a syntax change.
3. Ordering: position, ID, and score are different
A List's order is explicit element position changed by pushes/pops. A Stream entry has an ID and remains addressable while retained; consumer groups add delivery state. A Sorted Set orders unique members by numeric score and then member tie-breaking rules. A Set has no business ordering contract. If the requirement says “oldest queued item,” “event history,” “highest rank,” or “is member?”, those are four different questions.
4. Delivery and replay decide queue architecture
If work may be lost/retried and you need to know which consumer owns unfinished work, a Stream consumer group is usually a better primitive than a destructive List pop. A List can be upgraded with processing lists, but you then own acknowledgment/reclaim metadata. Neither primitive makes external side effects exactly-once. A durable broker outside Redis may be warranted for requirements beyond Redis's intended topology/retention/ecosystem envelope.
5. Membership and algebra decide audience architecture
Sets excel when the dominant operations are add/remove member, membership test, cardinality, and set algebra. A Sorted Set also has unique members but adds a score and ranking/range semantics at extra conceptual and memory cost. Do not choose a Sorted Set merely because “we may sort someday”; choose it when score order is already part of the contract.
6. Scheduling and ranking decide Sorted Set architecture
For delayed work, store the due timestamp as score and the job ID as member. The scheduler asks for due ranges. This is not the same as a List queue: producers can insert future jobs out of arrival order, and consumers must define claiming/removal/retry semantics.
docker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 redis-cli --user atlasmart-app DEL atlasmart:ch05:scheduledocker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 redis-cli --user atlasmart-app ZADD atlasmart:ch05:schedule 100 job:early 300 job:late 200 job:middledocker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 redis-cli --user atlasmart-app ZRANGE atlasmart:ch05:schedule 0 -1 WITHSCORESdocker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 redis-cli --user atlasmart-app ZRANGEBYSCORE atlasmart:ch05:schedule -inf 200 WITHSCORES
Chapter 06 develops Sorted Sets fully. Here the point is selection: score order is native there and absent from Lists/Sets.
7. Persistence, replication, and Cluster are orthogonal dimensions
Data-structure choice does not select durability automatically. RDB/AOF, asynchronous replication, Sentinel, and Cluster each add different recovery/availability semantics. In Cluster, multi-key List/Set operations may require same-slot locality; Streams and Sorted Sets can also become hot keys. Plan key distribution, payload/cardinality, failover ambiguity, and client retries separately from the abstract data model.
8. Deliberately wrong: choose the shortest command
An engineer picks BLPOP because it is one line,
then later adds retries, history, multiple consumer groups,
pending recovery, and auditing. The system gradually rebuilds
Stream semantics around Lists. Another engineer uses a Set for
“next worker” and discovers there is no ordering/fairness
contract.
Repair: write a one-page decision record before implementation. State ordering, uniqueness, retention, replay, acknowledgment, recovery, membership, score/range queries, expected cardinality, payload size, concurrency, persistence, topology, and migration path.
Structure: <List | Set | Stream | Sorted Set>Business invariant:Ordering contract:Uniqueness contract:Retention / replay:Acknowledgment / retry / reclaim:Expected cardinality and payload size:Hot-key / Cluster locality:Persistence and failover assumptions:Observability signals:Migration + rollback plan:
9. Hands-on decision lab
Use the same small fixture to answer four different questions.
docker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 redis-cli --user atlasmart-app LRANGE atlasmart:ch05:model:list 0 -1docker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 redis-cli --user atlasmart-app SISMEMBER atlasmart:ch05:model:set customer:vip:88docker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 redis-cli --user atlasmart-app XRANGE atlasmart:ch05:model:stream - + COUNT 10docker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 redis-cli --user atlasmart-app ZRANGE atlasmart:ch05:model:zset 0 -1 WITHSCORESdocker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 redis-cli --user atlasmart-app MEMORY USAGE atlasmart:ch05:model:listdocker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 redis-cli --user atlasmart-app MEMORY USAGE atlasmart:ch05:model:setdocker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 redis-cli --user atlasmart-app MEMORY USAGE atlasmart:ch05:model:streamdocker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 redis-cli --user atlasmart-app MEMORY USAGE atlasmart:ch05:model:zset
Do not compare these four tiny MEMORY USAGE values
as a universal “winner.” Real memory depends on element counts,
sizes, encodings, IDs/metadata, scores, allocator behavior, and
feature version. Benchmark representative datasets.
10. Migration and rollback
Changing structures can change ordering and failure semantics. A safe migration often dual-writes for a bounded period, validates counts/order/business outcomes, backfills historical state if required, cuts readers over behind a feature flag, and preserves a rollback window. Dual-write itself can fail partially, so reconciliation is mandatory. Never delete the old structure until the new path has proven correctness under retries, failover, and load.
10.1 Cleanup/reset the Chapter 05 decision fixtures
After the comparison, remove only the synthetic Chapter 05 keys. Cleanup is explicit so rerunning the lesson starts from known state without touching any unrelated Redis data.
docker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 redis-cli --user atlasmart-app DEL atlasmart:ch05:model:list atlasmart:ch05:model:set atlasmart:ch05:model:stream atlasmart:ch05:model:zsetdocker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 redis-cli --user atlasmart-app DEL atlasmart:ch05:schedule
Do not use FLUSHDB or FLUSHALL for
course cleanup; the shared lab may contain state from earlier
chapters.
11. Production judgment
Prefer the simplest structure that natively represents the invariant you actually need. Simplicity means fewer application-built correctness mechanisms—not merely fewer command characters. Lists are ordered deques; Sets are unique membership; Streams are retained event/work logs with group delivery state; Sorted Sets are score-ordered unique members. Revisit the decision when cardinality, retention, multi-reader behavior, retry policy, or topology changes.
12. Summary and next step
Chapter 05 separated sequence, membership, delivery state, replay, and ranking so “Redis collection” no longer means one generic bucket. Chapter 06 now focuses on Sorted Sets: scores, ranks, ranges, leaderboards, scheduling, and aggregation.
Check your understanding
- Which structure natively answers “is customer 88 in the VIP audience?”
- Which structure natively tracks delivered-but-unacknowledged work for a consumer group?
- Which structure best represents due-time ordering by numeric timestamp?
- Why is a processing List not automatically equivalent to a Stream consumer group?
- What must a migration validate besides element counts?
Review the answers
A Set.
A Stream with a consumer group and its Pending Entries List.
A Sorted Set, using due time as score.
The application must build ownership, acknowledgment, reclaim, history, and retry metadata around Lists.
Ordering, uniqueness, replay/retention, retry/ack semantics, business side effects, topology behavior, performance, and rollback/reconciliation.
Authoritative references
- Redis Open Source 8.10 release notes — 8.10.1 security baseline and 8.10 list/set additions
- Redis lists — list semantics and common queue patterns
- Redis sets — set uniqueness, membership, and algebra
- Redis Streams — persistent entries, consumer groups, pending entries, and acknowledgments
- Redis 8.10 command reference — current command syntax and complexity
- Redis Cluster specification — hash slots and multi-key locality
- Redis sorted sets — score/rank semantics used for the selection comparison
- XREADGROUP — consumer-group delivery state
- ZRANGE — score/rank range access