skip to content

Redis publishes key events on two families of channels, __keyspace@<db>__:<key> and __keyevent@<db>__:<event>. What is the difference between them, and which would you subscribe to in order to react to every key expiring in database 0?

level: juniorimportance: should knowfreq 30%

answer

  1. keyspace channel = key name, payload = event
  2. keyevent channel = event name, payload = key
  3. K enables keyspace, E enables keyevent
  4. @0 is the database number in the channel
  5. all expiries: __keyevent@0__:expired

basics

~20 s

A keyspace channel is named after the key and its message is the event name; a keyevent channel is named after the event and its message is the key name. To catch every expiry in database 0 subscribe to keyevent@0:expired.

solid answer

~50 s

The two families carry the same information with the subject and payload swapped. - `__keyspace@0__:user:42` - the channel identifies the *key*, the message body is the *event name*, for example `expired` or `del`. Enabled by the `K` flag. Use it when you care about one key or one key prefix and want to know everything that happens to it. - `__keyevent@0__:expired` - the channel identifies the *event*, the message body is the *key name*. Enabled by the `E` flag. Use it when you care about one kind of event across all keys. So watching all expiries in database 0 means `SUBSCRIBE __keyevent@0__:expired`, with `notify-keyspace-events` containing at least `Ex`. Watching one key means `SUBSCRIBE __keyspace@0__:user:42`. The `@0` is the database number, part of the channel name. Pattern subscriptions such as `PSUBSCRIBE __keyspace@0__:cart:*` work because these are ordinary Pub/Sub channels.

code

text · 9 lines
text
redis-cli CONFIG SET notify-keyspace-events KEx

# subscriber A - everything that happens to one key
redis-cli SUBSCRIBE '__keyspace@0__:sess:7'
# receives: message  __keyspace@0__:sess:7   expired

# subscriber B - every expiry in database 0
redis-cli SUBSCRIBE '__keyevent@0__:expired'
# receives: message  __keyevent@0__:expired  sess:7

go deeper

for a junior

Recall the two channel shapes and which payload each carries, and name keyevent@0:expired as the way to see all expiries.

for a middle

Explain the filter-dimension choice, the database segment in the channel name, and the cost of pattern subscriptions on busy keyspaces.

for a senior

Point out that enabling both K and E doubles notification work, and design the subscription shape around the consumer's real filter.

for a principal

Judge whether coupling application logic to per-key events is warranted at all, given the fanout cost and the fact that the payload never carries data.

## Two views of the same event When a key is affected, Redis can publish two messages describing it. They are not different events; they are two indexes over the same event, chosen so that a subscriber can filter cheaply on whichever dimension it cares about. **Keyspace notification (flag `K`)** ``` channel: __keyspace@<db>__:<keyname> message: <event name> ``` The channel name embeds the key, so subscribing means "tell me everything that happens to this key". The payload is what happened - `set`, `del`, `expire`, `expired`, `lpush`, and so on. **Keyevent notification (flag `E`)** ``` channel: __keyevent@<db>__:<event name> message: <keyname> ``` The channel name embeds the event type, so subscribing means "tell me every key this happened to". The payload is which key it happened to. Enabling both `K` and `E` publishes both messages for every qualifying operation, doubling the notification work; most applications only need one family. ## Choosing between them The question to ask is what your filter dimension is. - A cache-invalidation listener that must drop local copies of anything that expired filters by *event*: `__keyevent@0__:expired`, one subscription, every key. - A UI that live-updates one document filters by *key*: `__keyspace@0__:doc:1234`, one subscription, every event type on that key. - A listener for a family of keys filters by key pattern with `PSUBSCRIBE __keyspace@0__:doc:*`. Pattern subscriptions are more expensive than exact channels because every published message must be matched against every registered pattern, so a wildcard over a busy keyspace is a real cost. ## The database number The `@<db>` segment is the numeric database index, which means channel names differ per database. A subscriber must therefore either know its database or subscribe by pattern, for example `PSUBSCRIBE __keyevent@*__:expired`. In Redis Cluster only database 0 exists, so the segment is always `@0` there. The subscription itself is database-agnostic: Pub/Sub in Redis is not scoped to the currently selected database, so `SELECT` on the subscriber connection changes nothing about which notifications arrive. Only the channel name determines what you see. ## Things people get wrong The most common mistake is subscribing to `__keyevent@0__:expired` and then trying to `GET` the key to read its value. By the time the notification is published the key has already been deleted; the event exists precisely because the data is gone. If a workflow needs the value, it must be stored elsewhere - for example a companion key with a longer TTL, or a record in a durable store keyed by the same identifier. The second mistake is expecting a `del` event when a key expires. Expiry publishes `expired`, not `del`, and expiry-driven removal is also propagated to replicas as an explicit deletion by the primary - so a subscriber should match on the event names it actually wants rather than assuming one covers the other.

  • Can a subscriber read the value of a key after receiving its expired notification?
    No. The event is published after the key has been removed, so a read returns nothing. If the workflow needs the data, keep it in a companion key with a longer TTL or in a durable store and look it up by the identifier carried in the notification.
  • How do you watch a whole family of keys rather than one exact key?
    Use a pattern subscription on the keyspace family, for example PSUBSCRIBE __keyspace@0__:cart:*. Because these are ordinary Pub/Sub channels, all pattern-matching rules apply. Be aware that every published message must be matched against every registered pattern, so broad wildcards on a busy instance cost real CPU.

Two indexes on the same log: one filed by which file was touched, one filed by what kind of touch it was.

saying these in an interview costs you the question

  • Swapping the two: expecting the key name on a keyspace channel or the event name on a keyevent channel
  • Trying to read the expired key's value after the notification arrives
  • Thinking SELECT on the subscriber connection filters which notifications arrive
  • Expecting a del event when a key expires instead of expired

context