Subscribe to channels in Redis
Learn how to subscribe to channels in Redis for real-time messaging. This hands-on tutorial covers the SUBSCRIBE command, channel patterns, and practical examples for building pub/sub systems.
Focus: subscribe to channels in redis
You've built a Redis-backed application that stores and retrieves data, but what if you need to notify other parts of your system instantly when something changes? Polling the database every few seconds is inefficient and slow. Redis's pub/sub (publish/subscribe) model solves this by letting you subscribe to channels and receive messages in real-time, enabling everything from live chat updates to event-driven microservices.
The problem this lesson solves
Traditional request-response patterns force your application to constantly ask "is there new data?" This burns CPU, increases latency, and creates unnecessary load on both your app and Redis. Consider a stock ticker that needs to display price changes to hundreds of users — polling every second would be wasteful and still miss updates between polls.
Redis pub/sub eliminates polling entirely. When you subscribe to channels in Redis, your client receives messages the moment they're published. This "fire and forget" pattern is perfect for:
- Real-time notifications — new user signups, payment confirmations
- Live dashboards — pushing metrics to connected browsers
- Inter-service communication — microservices reacting to events without tight coupling
The key insight: subscribers never ask for data. They listen. Publishers never know who's listening. This decoupling makes your system more scalable and responsive.
Core concept / mental model
Think of Redis pub/sub like a radio broadcast. The channel is the radio frequency. A publisher is the radio station — it transmits on a frequency without knowing who's listening. A subscriber is someone with a radio tuned to that frequency — they hear the broadcast when it happens, but can't rewind or ask for past content.
Pro tip: Unlike a message queue, Redis pub/sub has no memory. If you're not subscribed when a message is published, you'll never receive it. This is by design — it's for real-time streams, not guaranteed delivery.
Key definitions:
- Channel: A string name (e.g.,
notifications,orders:new) that organizes messages - Publisher: A client that sends a message to a channel using
PUBLISH - Subscriber: A client that receives messages from a channel after using
SUBSCRIBE - Message: The payload — always a string in Redis pub/sub
How it works step by step
Follow this sequence to understand the lifecycle of a pub/sub message:
- A subscriber client connects to Redis and issues the
SUBSCRIBEcommand with one or more channel names. - Redis marks the client as a subscriber — the connection enters "subscriber mode" and can no longer issue regular commands like
GETorSET. - A publisher client sends a
PUBLISHcommand with a channel name and a message. - Redis looks up all subscribers for that channel and forwards the message to each one.
- Each subscriber receives the message as a three-element array: the message type (
message), the channel name, and the actual payload.
When you subscribe to channels in Redis, the connection becomes dedicated to receiving messages. You can't interleave regular commands — the only allowed commands are SUBSCRIBE, UNSUBSCRIBE, PSUBSCRIBE (pattern-based), and QUIT.
# In terminal 1 — subscriber
redis-cli SUBSCRIBE orders:new
# Output:
# 1) "subscribe"
# 2) "orders:new"
# 3) (integer) 1
# In terminal 2 — publisher
redis-cli PUBLISH orders:new "order-101: paid"
# Output: (integer) 1 ← one subscriber received the message
# Back in terminal 1, the subscriber sees:
# 1) "message"
# 2) "orders:new"
# 3) "order-101: paid"
Subscribers can also unsubscribe:
redis-cli UNSUBSCRIBE orders:new
# Output:
# 1) "unsubscribe"
# 2) "orders:new"
# 3) (integer) 0 ← no longer subscribed to any channels
Hands-on walkthrough
Let's build a practical example: a Python application that monitors new user registrations and sends a welcome notification. We'll start with a publisher script and a subscriber script.
Publisher — publish_user.py
import redis
import json
import time
# Connect to Redis
client = redis.Redis(host='localhost', port=6379, decode_responses=True)
# Simulate new user registrations
def register_user(user_id, username):
event = {
'user_id': user_id,
'username': username,
'timestamp': time.time()
}
# Publish the event to the 'users:new' channel
result = client.publish('users:new', json.dumps(event))
print(f"Published event for {username}. Subscribers reached: {result}")
# Simulate a few registrations
register_user(101, 'alice')
register_user(102, 'bob')
time.sleep(1)
register_user(103, 'charlie')
Subscriber — subscribe_users.py
import redis
import json
client = redis.Redis(host='localhost', port=6379, decode_responses=True)
pubsub = client.pubsub()
# Subscribe to the 'users:new' channel
pubsub.subscribe('users:new')
print("Subscribed to 'users:new'. Waiting for events...")
# Listen for messages (this blocks)
for message in pubsub.listen():
if message['type'] == 'message':
data = json.loads(message['data'])
print(f"New user registered: {data['username']} (ID: {data['user_id']})")
# In a real app, you'd send an email, update a dashboard, etc.
Run these in two terminals:
# Terminal 1 — subscriber
python subscribe_users.py
# Output:
# Subscribed to 'users:new'. Waiting for events...
# Terminal 2 — publisher
python publish_user.py
# Output in subscriber terminal:
# New user registered: alice (ID: 101)
# New user registered: bob (ID: 102)
# New user registered: charlie (ID: 103)
Pro tip: The
decode_responses=Trueparameter ensures messages come back as strings instead of bytes, which makes JSON handling much cleaner.
Pattern-based subscriptions with PSUBSCRIBE
You can subscribe to multiple channels using glob-style patterns:
# Subscribe to all order-related channels (orders:new, orders:cancelled, etc.)
pubsub.psubscribe('orders:*')
# This will receive messages from:
# - PUBLISH orders:new "order-201: pending"
# - PUBLISH orders:cancelled "order-101: refunded"
# But NOT from:
# - PUBLISH payments:failed "order-301: declined"
Pattern subscriptions require more CPU on the Redis server because they must match against the channel name. Use them sparingly in high-throughput systems.
Compare options / when to choose what
Not all real-time messaging is the same. Here's how Redis pub/sub stacks up against alternatives:
| Feature | Redis Pub/Sub | Redis Streams | Message Queue (e.g., RabbitMQ) |
|---|---|---|---|
| Message persistence | None — lost if no subscriber | Persistent — can replay | Usually persistent |
| Delivery guarantee | At-most-once — if subscriber disconnects, message is lost | At-least-once with consumer groups | At-least-once or exactly-once |
| Subscriber state | Stateless — just listens | Stateful — tracks read position | Stateful — acks messages |
| Scalability | Excellent — fan-out to many subscribers | Good — consumer groups share load | Moderate — depends on broker |
| Use case | Real-time broadcasts, notifications | Event sourcing, job queues | Reliable task processing |
When to use publish/subscribe to channels in Redis:
- You need fast, simple fan-out to multiple consumers
- Message loss is acceptable (notifications can be retried via another channel)
- You want zero overhead — no persistent storage, no ack framework
- Your subscribers are typically always-on services
When to avoid it:
- You need guaranteed delivery (use Redis Streams or a message broker)
- Subscribers connect intermittently (they'll miss messages)
- You need message ordering across subscriber reconnections (Streams track this)
Troubleshooting & edge cases
Subscriber mode restriction
Once a client issues SUBSCRIBE, it enters subscriber mode. Any attempt to run a non-subscription command will fail:
# Subscriber tries to run GET after subscribing
redis-cli SUBSCRIBE test
redis-cli GET mykey
# Error: Redis is in subscriber mode, cannot execute non-subscription commands
Fix: Use a separate connection for non-subscription work, or call UNSUBSCRIBE first to exit subscriber mode.
Missing messages during reconnection
If your subscriber disconnects and reconnects, any messages published during the gap are lost forever:
# This will miss messages published between disconnection and resubscription
pubsub = client.pubsub()
pubsub.subscribe('important_alerts')
Workaround: Use a heartbeat or health-check channel. Have publishers periodically send a "still alive" message. Better yet, use Redis Streams if you can't tolerate loss.
High memory from slow subscribers
If a subscriber is slow to process messages but fast enough to keep the connection alive, Redis buffers messages in the output buffer. This can consume memory:
# Check memory per client
redis-cli CLIENT LIST
# Look for "omem" field — if very large, the subscriber is falling behind
Mitigation: Increase client-output-buffer-limit for pub/sub clients, or implement backpressure in your subscriber code.
Pattern subscription performance
Using PSUBSCRIBE orders:* with thousands of PUBLISH calls per second can slow down Redis because it must pattern-match each message. A dedicated channel per entity (e.g., orders:101, orders:102) is faster because Redis uses a hash table lookup.
What you learned & what's next
You've mastered how to subscribe to channels in Redis — from the basic SUBSCRIBE command to pattern matching and Python integration. You understand:
- The fire-and-forget nature of pub/sub and when to use it over queues or streams
- How to build a publisher and subscriber in Python with the
redis-pylibrary - The critical difference between subscribe mode and normal mode
- How to choose between pub/sub, Streams, and external message brokers
You're now ready to build real-time features like live dashboards, notification systems, and event-driven microservices. But pub/sub has a limitation — no history and no replay. The next lesson introduces Redis Streams, which solve exactly that: persistent, replayable message channels with consumer groups for distributed processing. You'll learn how to build a job queue that survives crashes and scales across workers.
Practice challenge: Modify the subscriber example to subscribe to multiple channels (e.g.,
'users:new','orders:new'). Have it print which channel each message came from. Then try running two subscriber instances and see how many get each message.
Practice recap
Open two terminal windows. In the first, run redis-cli SUBSCRIBE practice to listen. In the second, run redis-cli PUBLISH practice "hello world". Watch the message appear in the first terminal. Then modify the Python subscriber script to handle multiple channels — add orders:new to your subscription and send test messages from a third terminal.
Common mistakes
- Trying to run
GETorSETcommands on a connection that's in subscriber mode — you must use a separate connection for non-pub/sub operations - Assuming messages persist if no subscriber is active — pub/sub messages are fire-and-forget; use Streams if you need durable delivery
- Forgetting
decode_responses=Truein Python, which makes message data appear as bytes instead of strings, breaking JSON parsing - Using
PSUBSCRIBEwith a broad pattern like*on high-traffic channels, causing Redis to waste CPU on pattern matching
Variations
- Instead of
SUBSCRIBE, usePSUBSCRIBEto match multiple channels with glob patterns (e.g.,users:,orders:) - Use the
redis-cli--csvflag to output messages as comma-separated values for simple logging or scripting - For async Python applications, try
aioredislibrary which providesasync for message in pubsub.listen():syntax
Real-world use cases
- A live sports scoreboard pushing point updates to all connected mobile apps using a single
scores:livechannel - A microservices architecture where
orders:createdtriggers inventory updates, email confirmations, and analytics logging - A real-time chat application with per-room channels (e.g.,
rooms:general,rooms:support) sending messages to all participants
Key takeaways
- Subscribing to channels in Redis uses
SUBSCRIBEandPSUBSCRIBE— no polling needed, messages arrive instantly - Publishers don't know who's listening; subscribers receive messages only if connected when published
- Once a connection subscribes, it enters subscriber mode — regular commands like GET are blocked until UNSUBSCRIBE
- Redis pub/sub has no message persistence or delivery guarantees — use Streams for durable, replayable messaging
- Pattern subscriptions (
PSUBSCRIBE) are powerful but cost CPU on the server; use specific channels for high throughput - Python's
redis-pylibrary providespubsub().subscribe()and.listen()— always setdecode_responses=True
Keep learning
Related tutorials, quizzes, and articles for this topic.
Discussion
Questions, corrections, and tips help everyone reading this page.
0 comments
Add a comment
No comments yet — start the thread.