# 鍵空間通知(keyspace notification))
Note
本文檔翻譯自: [http://redis.io/topics/notifications](http://redis.io/topics/notifications) 。
Warning
鍵空間通知功能目前仍在開發中,這個文檔所描述的內容,以及功能的具體實現,可能會在未來數周內改變,敬請知悉。
## 功能概覽
鍵空間通知使得客戶端可以通過訂閱頻道或模式, 來接收那些以某種方式改動了 Redis 數據集的事件。
以下是一些鍵空間通知發送的事件的例子:
* 所有修改鍵的命令。
* 所有接收到 [LPUSH](../list/lpush.html#lpush) 命令的鍵。
* `0` 號數據庫中所有已過期的鍵。
事件通過 Redis 的訂閱與發布功能(pub/sub)來進行分發, 因此所有支持訂閱與發布功能的客戶端都可以在無須做任何修改的情況下, 直接使用鍵空間通知功能。
因為 Redis 目前的訂閱與發布功能采取的是發送即忘(fire and forget)策略, 所以如果你的程序需要可靠事件通知(reliable notification of events), 那么目前的鍵空間通知可能并不適合你: 當訂閱事件的客戶端斷線時, 它會丟失所有在斷線期間分發給它的事件。
未來將會支持更可靠的事件分發, 這種支持可能會通過讓訂閱與發布功能本身變得更可靠來實現, 也可能會在 Lua 腳本中對消息(message)的訂閱與發布進行監聽, 從而實現類似將事件推入到列表這樣的操作。
## 事件的類型
對于每個修改數據庫的操作,鍵空間通知都會發送兩種不同類型的事件。
比如說,對 `0` 號數據庫的鍵 `mykey` 執行 [DEL](../key/del.html#del) 命令時, 系統將分發兩條消息, 相當于執行以下兩個 [PUBLISH](../pub_sub/publish.html#publish) 命令:
```
PUBLISH __keyspace@0__:mykey del
PUBLISH __keyevent@0__:del mykey
```
訂閱第一個頻道 `__keyspace@0__:mykey` 可以接收 `0` 號數據庫中所有修改鍵 `mykey` 的事件, 而訂閱第二個頻道 `__keyevent@0__:del` 則可以接收 `0` 號數據庫中所有執行 `del` 命令的鍵。
以 `keyspace` 為前綴的頻道被稱為鍵空間通知(key-space notification), 而以 `keyevent` 為前綴的頻道則被稱為鍵事件通知(key-event notification)。
當 `del mykey` 命令執行時:
* 鍵空間頻道的訂閱者將接收到被執行的事件的名字,在這個例子中,就是 `del` 。
* 鍵事件頻道的訂閱者將接收到被執行事件的鍵的名字,在這個例子中,就是 `mykey` 。
## 配置
因為開啟鍵空間通知功能需要消耗一些 CPU , 所以在默認配置下, 該功能處于關閉狀態。
可以通過修改 `redis.conf` 文件, 或者直接使用 `CONFIG SET` 命令來開啟或關閉鍵空間通知功能:
* 當 `notify-keyspace-events` 選項的參數為空字符串時,功能關閉。
* 另一方面,當參數不是空字符串時,功能開啟。
`notify-keyspace-events` 的參數可以是以下字符的任意組合, 它指定了服務器該發送哪些類型的通知:
| 字符 | 發送的通知 |
| --- | --- |
| `K` | 鍵空間通知,所有通知以 `__keyspace@<db>__` 為前綴 |
| `E` | 鍵事件通知,所有通知以 `__keyevent@<db>__` 為前綴 |
| `g` | `DEL` 、 `EXPIRE` 、 `RENAME` 等類型無關的通用命令的通知 |
| `$` | 字符串命令的通知 |
| `l` | 列表命令的通知 |
| `s` | 集合命令的通知 |
| `h` | 哈希命令的通知 |
| `z` | 有序集合命令的通知 |
| `x` | 過期事件:每當有過期鍵被刪除時發送 |
| `e` | 驅逐(evict)事件:每當有鍵因為 `maxmemory` 政策而被刪除時發送 |
| `A` | 參數 `g$lshzxe` 的別名 |
輸入的參數中至少要有一個 `K` 或者 `E` , 否則的話, 不管其余的參數是什么, 都不會有任何通知被分發。
舉個例子, 如果只想訂閱鍵空間中和列表相關的通知, 那么參數就應該設為 `Kl` , 諸如此類。
將參數設為字符串 `"AKE"` 表示發送所有類型的通知。
## 命令產生的通知
以下列表記錄了不同命令所產生的不同通知:
* [DEL](../key/del.html#del) 命令為每個被刪除的鍵產生一個 `del` 通知。
* [RENAME](../key/rename.html#rename) 產生兩個通知:為來源鍵(source key)產生一個 `rename_from` 通知,并為目標鍵(destination key)產生一個 `rename_to` 通知。
* [EXPIRE](../key/expire.html#expire) 和 [EXPIREAT](../key/expireat.html#expireat) 在鍵被正確設置過期時間時產生一個 `expire` 通知。當 [EXPIREAT](../key/expireat.html#expireat) 設置的時間已經過期,或者 [EXPIRE](../key/expire.html#expire) 傳入的時間為負數值時,鍵被刪除,并產生一個 `del` 通知。
* [SORT](../key/sort.html#sort) 在命令帶有 `STORE` 參數時產生一個 `sortstore` 事件。如果 `STORE` 指示的用于保存排序結果的鍵已經存在,那么程序還會發送一個 `del` 事件。
* [SET](../string/set.html#set) 以及它的所有變種([SETEX](../string/setex.html#setex) 、 [SETNX](../string/setnx.html#setnx) 和 [GETSET](../string/getset.html#getset))都產生 `set` 通知。其中 [SETEX](../string/setex.html#setex) 還會產生 `expire` 通知。
* [MSET](../string/mset.html#mset) 為每個鍵產生一個 `set` 通知。
* [SETRANGE](../string/setrange.html#setrange) 產生一個 `setrange` 通知。
* [INCR](../string/incr.html#incr) 、 [DECR](../string/decr.html#decr) 、 [INCRBY](../string/incrby.html#incrby) 和 [DECRBY](../string/decrby.html#decrby) 都產生 `incrby` 通知。
* [INCRBYFLOAT](../string/incrbyfloat.html#incrbyfloat) 產生 `incrbyfloat` 通知。
* [APPEND](../string/append.html#append) 產生 `append` 通知。
* [LPUSH](../list/lpush.html#lpush) 和 [LPUSHX](../list/lpushx.html#lpushx) 都產生單個 `lpush` 通知,即使有多個輸入元素時,也是如此。
* [RPUSH](../list/rpush.html#rpush) 和 [RPUSHX](../list/rpushx.html#rpushx) 都產生單個 `rpush` 通知,即使有多個輸入元素時,也是如此。
* [RPOP](../list/rpop.html#rpop) 產生 `rpop` 通知。如果被彈出的元素是列表的最后一個元素,那么還會產生一個 `del` 通知。
* [LPOP](../list/lpop.html#lpop) 產生 `lpop` 通知。如果被彈出的元素是列表的最后一個元素,那么還會產生一個 `del` 通知。
* [LINSERT](../list/linsert.html#linsert) 產生一個 `linsert` 通知。
* [LSET](../list/lset.html#lset) 產生一個 `lset` 通知。
* [LTRIM](../list/ltrim.html#ltrim) 產生一個 `ltrim` 通知。如果 [LTRIM](../list/ltrim.html#ltrim) 執行之后,列表鍵被清空,那么還會產生一個 `del` 通知。
* [RPOPLPUSH](../list/rpoplpush.html#rpoplpush) 和 [BRPOPLPUSH](../list/brpoplpush.html#brpoplpush) 產生一個 `rpop` 通知,以及一個 `lpush` 通知。兩個命令都會保證 `rpop` 的通知在 `lpush` 的通知之前分發。如果從鍵彈出元素之后,被彈出的列表鍵被清空,那么還會產生一個 `del` 通知。
* [HSET](../hash/hset.html#hset) 、 [HSETNX](../hash/hsetnx.html#hsetnx) 和 [HMSET](../hash/hmset.html#hmset) 都只產生一個 `hset` 通知。
* [HINCRBY](../hash/hincrby.html#hincrby) 產生一個 `hincrby` 通知。
* [HINCRBYFLOAT](../hash/hincrbyfloat.html#hincrbyfloat) 產生一個 `hincrbyfloat` 通知。
* [HDEL](../hash/hdel.html#hdel) 產生一個 `hdel` 通知。如果執行 [HDEL](../hash/hdel.html#hdel) 之后,哈希鍵被清空,那么還會產生一個 `del` 通知。
* [SADD](../set/sadd.html#sadd) 產生一個 `sadd` 通知,即使有多個輸入元素時,也是如此。
* [SREM](../set/srem.html#srem) 產生一個 `srem` 通知,如果執行 [SREM](../set/srem.html#srem) 之后,集合鍵被清空,那么還會產生一個 `del` 通知。
* [SMOVE](../set/smove.html#smove) 為來源鍵(source key)產生一個 `srem` 通知,并為目標鍵(destination key)產生一個 `sadd` 事件。
* [SPOP](../set/spop.html#spop) 產生一個 `spop` 事件。如果執行 [SPOP](../set/spop.html#spop) 之后,集合鍵被清空,那么還會產生一個 `del` 通知。
* [SINTERSTORE](../set/sinterstore.html#sinterstore) 、 [SUNIONSTORE](../set/sunionstore.html#sunionstore) 和 [SDIFFSTORE](../set/sdiffstore.html#sdiffstore) 分別產生 `sinterstore` 、 `sunionostore` 和 `sdiffstore` 三種通知。如果用于保存結果的鍵已經存在,那么還會產生一個 `del` 通知。
* [ZINCRBY](../sorted_set/zincrby.html#zincrby) 產生一個 `zincr` 通知。(譯注:非對稱,請注意。)
* [ZADD](../sorted_set/zadd.html#zadd) 產生一個 `zadd` 通知,即使有多個輸入元素時,也是如此。
* [ZREM](../sorted_set/zrem.html#zrem) 產生一個 `zrem` 通知,即使有多個輸入元素時,也是如此。如果執行 [ZREM](../sorted_set/zrem.html#zrem) 之后,有序集合鍵被清空,那么還會產生一個 `del` 通知。
* [ZREMRANGEBYSCORE](../sorted_set/zremrangebyscore.html#zremrangebyscore) 產生一個 `zrembyscore` 通知。(譯注:非對稱,請注意。)如果用于保存結果的鍵已經存在,那么還會產生一個 `del` 通知。
* [ZREMRANGEBYRANK](../sorted_set/zremrangebyrank.html#zremrangebyrank) 產生一個 `zrembyrank` 通知。(譯注:非對稱,請注意。)如果用于保存結果的鍵已經存在,那么還會產生一個 `del` 通知。
* [ZINTERSTORE](../sorted_set/zinterstore.html#zinterstore) 和 [ZUNIONSTORE](../sorted_set/zunionstore.html#zunionstore) 分別產生 `zinterstore` 和 `zunionstore` 兩種通知。如果用于保存結果的鍵已經存在,那么還會產生一個 `del` 通知。
* 每當一個鍵因為過期而被刪除時,產生一個 `expired` 通知。
* 每當一個鍵因為 `maxmemory` 政策而被刪除以回收內存時,產生一個 `evicted` 通知。
Note
所有命令都只在鍵**真的**被改動了之后,才會產生通知。
比如說,當 [SREM](../set/srem.html#srem) 試圖刪除不存在于集合的元素時,刪除操作會執行失敗,因為沒有真正的改動鍵,所以這一操作不會發送通知。
如果對命令所產生的通知有疑問, 最好還是使用以下命令, 自己來驗證一下:
```
$ redis-cli config set notify-keyspace-events KEA
$ redis-cli --csv psubscribe '__key*__:*'
Reading messages... (press Ctrl-C to quit)
"psubscribe","__key*__:*",1
```
然后, 只要在其他終端里用 Redis 客戶端發送命令, 就可以看到產生的通知了:
```
"pmessage","__key*__:*","__keyspace@0__:foo","set"
"pmessage","__key*__:*","__keyevent@0__:set","foo"
...
```
## 過期通知的發送時間
Redis 使用以下兩種方式刪除過期的鍵:
* 當一個鍵被訪問時,程序會對這個鍵進行檢查,如果鍵已經過期,那么該鍵將被刪除。
* 底層系統會在后臺漸進地查找并刪除那些過期的鍵,從而處理那些已經過期、但是不會被訪問到的鍵。
當過期鍵被以上兩個程序的任意一個發現、 并且將鍵從數據庫中刪除時, Redis 會產生一個 `expired` 通知。
Redis 并不保證生存時間(TTL)變為 `0` 的鍵會立即被刪除: 如果程序沒有訪問這個過期鍵, 或者帶有生存時間的鍵非常多的話, 那么在鍵的生存時間變為 `0` , 直到鍵真正被刪除這中間, 可能會有一段比較顯著的時間間隔。
因此, Redis 產生 `expired` 通知的時間為過期鍵被刪除的時候, 而不是鍵的生存時間變為 `0` 的時候。
- Redis 文檔
- 鍵空間通知(keyspace notification)
- 事務(transaction)
- 發布與訂閱(pub/sub)
- 復制(Replication)
- 通信協議(protocol)
- 持久化(persistence)
- Sentinel
- 集群教程
- Redis 集群規范
- Redis 命令參考
- Key(鍵)
- DEL
- DUMP
- EXISTS
- EXPIRE
- EXPIREAT
- KEYS
- MIGRATE
- MOVE
- OBJECT
- PERSIST
- PEXPIRE
- PEXPIREAT
- PTTL
- RANDOMKEY
- RENAME
- RENAMENX
- RESTORE
- SORT
- TYPE
- SCAN
- String(字符串)
- APPEND
- BITCOUNT
- BITOP
- DECR
- DECRBY
- GET
- GETBIT
- GETRANGE
- GETSET
- INCR
- INCRBY
- INCRBYFLOAT
- MGET
- MSET
- MSETNX
- PSETEX
- SET
- SETBIT
- SETEX
- SETNX
- SETRANGE
- STRLEN
- Hash(哈希表)
- HDEL
- HEXISTS
- HGET
- HGETALL
- HINCRBY
- HINCRBYFLOAT
- HKEYS
- HLEN
- HMGET
- HMSET
- HSET
- HSETNX
- HVALS
- HSCAN
- List(列表)
- BLPOP
- BRPOP
- BRPOPLPUSH
- LINDEX
- LINSERT
- LLEN
- LPOP
- LPUSH
- LRANGE
- LREM
- LSET
- LTRIM
- RPOP
- RPOPLPUSH
- RPUSH
- RPUSHX
- Set(集合)
- SADD
- SCARD
- SDIFF
- SDIFFSTORE
- SINTER
- SINTER
- SINTERSTORE
- SISMEMBER
- SMEMBERS
- SMOVE
- SPOP
- SRANDMEMBER
- SREM
- SUNION
- SUNIONSTORE
- SSCAN
- SortedSet(有序集合)
- ZADD
- ZCARD
- ZCOUNT
- ZINCRBY
- ZRANGE
- ZRANGEBYSCORE
- ZRANK
- ZREM
- ZREMRANGEBYRANK
- ZREMRANGEBYSCORE
- ZREVRANGE
- ZREVRANGEBYSCORE
- ZREVRANK
- ZSCORE
- ZUNIONSTORE
- ZINTERSTORE
- ZSCAN
- Pub/Sub(發布/訂閱)
- PSUBSCRIBE
- PUBLISH
- PUBSUB
- PUNSUBSCRIBE
- SUBSCRIBE
- UNSUBSCRIBE
- Transaction(事務)
- DISCARD
- EXEC
- MULTI
- UNWATCH
- WATCH
- Script(腳本)
- EVAL
- EVALSHA
- SCRIPT EXISTS
- SCRIPT FLUSH
- SCRIPT KILL
- SCRIPT LOAD
- Connection(連接)
- AUTH
- ECHO
- PING
- QUIT
- SELECT
- Server(服務器)
- BGREWRITEAOF
- BGSAVE
- CLIENT GETNAME
- CLIENT KILL
- CLIENT LIST
- CLIENT SETNAME
- CONFIG GET
- CONFIG RESETSTAT
- CONFIG REWRITE
- CONFIG SET
- DBSIZE
- DEBUG OBJECT
- DEBUG SEGFAULT
- FLUSHALL
- FLUSHDB
- INFO
- LASTSAVE
- MONITOR
- PSYNC
- SAVE
- SHUTDOWN
- SLAVEOF
- SLOWLOG
- SYNC
- TIME
- 關于