Redis Messaging Showdown - Pub/Sub vs. Streams for Event-Driven Architectures
James Reed
Infrastructure Engineer · Leapcell

Introduction
In the vibrant landscape of modern software architecture, event-driven systems have emerged as a cornerstone for building scalable, resilient, and responsive applications. At the heart of these systems lies efficient message passing, enabling disparate components to communicate and react to changes. Redis, renowned for its blazing-fast performance and versatile data structures, offers two compelling options for message brokering: Pub/Sub and Streams. While both facilitate communication, they cater to fundamentally different messaging paradigms – Pub/Sub for fire-and-forget broadcasts and Streams for persistent, ordered event logs. Understanding their nuances is crucial for any developer aiming to harness Redis's full potential in event-driven designs. This article will dissect these two mechanisms, exploring their principles, implementations, and ideal use cases, empowering you to make informed architectural decisions.
The Messaging Paradigms of Redis
Before diving into the specifics, let's establish a clear understanding of the core concepts that underpin our discussion.
Publisher-Subscriber (Pub/Sub) Model: This is a classic asynchronous messaging pattern where senders (publishers) do not send messages directly to specific receivers (subscribers). Instead, they publish messages to categories or topics, without knowing who, if anyone, is listening. Subscribers express interest in one or more categories and receive all messages published to those categories. It's a broadcast mechanism.
Message Queue / Log: A message queue, or more generally, a message log, is a form of asynchronous service-to-service communication used in serverless and microservices architectures. Messages are stored persistently in a queue or log until they are processed and deleted by a consumer. This ensures reliability and allows multiple consumers to process messages even if producers are offline.
Now, let's explore how Redis implements these paradigms.
Redis Pub/Sub: The Fire-and-Forget Broadcast
Redis Pub/Sub is a straightforward, in-memory messaging system designed for real-time notifications and broadcasting.
Principle: When a client PUBLISHes a message to a channel, Redis immediately dispatches that message to all clients currently subscribed to that channel.
Key Characteristics:
- Fire-and-Forget: Messages are not persisted. If no clients are subscribed to a channel when a message is published, the message is lost.
- Decoupled: Publishers and subscribers are completely unaware of each other.
- Scalability: Can handle a large number of subscribers and message throughput, limited only by network bandwidth and client processing speed.
Implementation Example:
Let's illustrate with simple redis-cli commands.
-
Subscriber 1:
redis-cli SUBSCRIBE chat_room_1This client is now waiting for messages on
chat_room_1. -
Subscriber 2 (in another terminal):
redis-cli SUBSCRIBE chat_room_1This client also subscribes to the same channel.
-
Publisher (in a third terminal):
redis-cli PUBLISH chat_room_1 "Hello everyone in chat_room_1!"Both Subscriber 1 and Subscriber 2 will immediately receive:
1) "message" 2) "chat_room_1" 3) "Hello everyone in chat_room_1!"If a client subscribes after the
PUBLISHcommand, it will not receive the "Hello everyone..." message.
Application Scenarios:
- Real-time Chat Applications: Delivering messages instantly to online users.
- Live Scoreboards/Feeds: Pushing updates to active users.
- Cache Invalidation: Notifying application instances to refresh their local caches when underlying data changes.
- Event Notifications: Sending immediate alerts (e.g., "new order placed") to interested services for transient processing.
Redis Streams: The Persistent, Ordered Event Log
Redis Streams, introduced in Redis 5.0, offer a more robust and durable messaging solution that functions as an append-only log.
Principle: Messages (entries) are added to the end of a Stream in an ordered fashion, each tagged with a unique ID. Consumers can read from any point in the Stream and control their reading progress.
Key Characteristics:
- Persistence: Messages are stored in the Stream until explicitly removed or trimmed, making them durable.
- Ordered: Entries are guaranteed to be ordered by their entry ID.
- Multiple Consumers/Consumer Groups: Allows multiple independent consumers to process the same messages, and consumer groups to coordinate processing among workers, providing fault tolerance and load balancing.
- Acknowledged Processing: Consumers can acknowledge messages, marking them as processed, which is crucial for reliable delivery.
- History Replay: New consumers can read the entire history of events from the beginning of the Stream.
Core Terminology for Streams:
- Entry ID: A unique identifier for each message in a Stream, typically a timestamp-sequence pair (e.g.,
1678886400000-0). It determines the order. - Consumer: A client that reads messages from a Stream.
- Consumer Group: A logical group of consumers that cooperate to process a Stream. Messages dispatched to a consumer group are distributed among its members, and only one member processes a given message.
- Pending Entries List (PEL): For consumer groups, this list holds entries that have been delivered to a consumer but not yet acknowledged. Essential for recovering from consumer failures.
Implementation Example:
-
Adding Messages to a Stream:
redis-cli XADD mystream * sensor_id 123 temperature 25.5 # The '*' means Redis generates a new unique Entry ID # Response: "1678886400000-0" (example ID)Each
XADDcommand adds an entry with key-value pairs. -
Reading from a Stream (using a consumer group):
First, create a consumer group:
redis-cli XGROUP CREATE mystream mygroup 0-0 MKSTREAM # '0-0' means start reading from the beginning of the stream. # 'MKSTREAM' creates the stream if it doesn't already exist.Now, a consumer in
mygroupcan read:redis-cli XREADGROUP GROUP mygroup consumer1 mystream > COUNT 1 # 'consumer1' is the name of this specific consumer. # '>' means read new messages that haven't been delivered to this consumer group yet. # 'COUNT 1' requests up to 1 message.Example response (after adding a message):
1) 1) "mystream" 2) 1) 1) "1678886400000-0" 2) 1) "sensor_id" 2) "123" 3) "temperature" 4) "25.5" -
Acknowledging Messages: After processing, the consumer acknowledges the message:
redis-cli XACK mystream mygroup 1678886400000-0 # Acknowledges the message with the given ID for 'mygroup'.
Application Scenarios:
- Event Sourcing: Storing a complete, ordered log of all state changes for an application.
- Change Data Capture (CDC): Recording database changes as a stream of events.
- Reliable Task Queues: Ensuring that tasks are processed exactly once, even with consumer failures.
- Microservices Communication: Durable and ordered message exchange between services.
- IoT Data Ingestion: Collecting and processing sensor data streams.
Choosing Between Pub/Sub and Streams
The choice between Redis Pub/Sub and Streams boils down to your specific requirements regarding message persistence, delivery guarantees, and consumption patterns.
| Feature / Aspect | Redis Pub/Sub | Redis Streams |
|---|---|---|
| Persistence | None (fire-and-forget) | Persistent (messages stored) |
| Ordering | Not guaranteed across multiple publishers | Guaranteed by Entry ID |
| Delivery | At-most-once (if no subscriber, message lost) | At-least-once (with acknowledgment and consumer groups) |
| Consumer Tracking | None (subscribers are anonymous) | Advanced (consumer groups track offsets and pending messages) |
| Consumer Recovery | Cannot recover lost messages | Can re-read pending messages |
| Scalability | High fan-out for real-time broadcasts | High throughput for persistent event logs; scales with consumer groups |
| Complexity | Simple API | More complex API (consumer groups, PEL, trimming) |
| Ideal Use Cases | Real-time notifications, transient events, chat | Event sourcing, CDC, reliable queues, IoT data processing |
For simple, lightweight notifications where message loss is acceptable and persistence is not required, Pub/Sub is an excellent choice due to its simplicity and high performance. For scenarios demanding durability, guaranteed delivery, replayability, and complex consumption patterns with multiple consumers and fault tolerance, Redis Streams are the clear winner. They offer a more robust and feature-rich foundation for building sophisticated event-driven architectures.
Conclusion
Both Redis Pub/Sub and Streams are powerful tools for inter-process communication, yet they serve distinct purposes. Pub/Sub excels in broadcasting ephemeral events with minimal overhead, perfect for real-time, fire-and-forget scenarios. Streams, on the other hand, provide a durable, ordered, and highly flexible event log, making them indispensable for building resilient, event-sourced, and data-intensive applications. By understanding their individual strengths and weaknesses, developers can leverage Redis effectively to build efficient and scalable event-driven systems. Choosing the right tool for the job – be it a quick broadcast or a persistent event log – is paramount for robust system design.

