Redis Pub/Sub
Fire-and-forget messaging in Redis, its delivery semantics, and when to use Streams instead.
Pub/Sub delivers a message to every client currently subscribed to a channel.
SUBSCRIBE shop:events:orders
PUBLISH shop:events:orders '{"order_id":4711,"status":"paid"}'
PSUBSCRIBE shop:events:* # pattern subscriptionDelivery semantics
That makes it suitable for genuinely ephemeral signals — cache invalidation hints, live presence updates, dashboard ticks — and unsuitable for anything where a lost message matters.
For durable messaging with consumer groups and acknowledgements, use Streams.
Operational hazards
Slow subscribers consume server memory. Redis buffers outgoing messages per client. A subscriber that reads slower than the publish rate causes its output buffer to grow.
# class hard limit soft limit soft seconds
client-output-buffer-limit pubsub 32mb 8mb 60When a limit is exceeded, Redis disconnects that client — which is the intended protection, and means subscribers must handle reconnection and accept that they missed messages while away.
Publishing costs scale with subscriber count. A publish to a channel with a thousand subscribers writes a thousand times, on the single command thread.
Pattern subscriptions are more expensive, because every publish is matched against every pattern.
Cluster behaviour
In Redis Cluster, plain PUBLISH is broadcast across the whole cluster, which does not scale with
node count. Redis 7 added sharded pub/sub (SPUBLISH / SSUBSCRIBE), where the channel is
hashed to a slot and the message stays on the owning shard. Use the sharded form in cluster
deployments where message volume is significant.
A reasonable use: cache invalidation
# Writer, after updating the system of record.
r.publish("shop:cache:invalidate", json.dumps({"key": "shop:product:882"}))
# Each application instance clears its local in-process cache.
for message in pubsub.listen():
local_cache.pop(json.loads(message["data"])["key"], None)This is a correct use precisely because a missed message is tolerable: the local entry simply expires by its own TTL a little later. Design any pub/sub consumer so that missing a message degrades freshness rather than correctness.