重要 鍵空間通知是2.8.0以后版本可用的一個(gè)特性。
特性概覽
鍵空間通知允許客戶端訂閱Pub/Sub通道,以便接收對(duì)Redis數(shù)據(jù)集的某些方面影響的事件。
可以被接收的事件的例子:
- 影響給定鍵的所有命令。
- 接收一個(gè)LPUSH操作的所有鍵。
- 在數(shù)據(jù)庫(kù)0所有過(guò)期的鍵。
事件使用Redis的Pub/Sub層來(lái)傳遞,所以客戶端可以使用這些特征實(shí)現(xiàn)Pub/Sub而不需要做修改。
因?yàn)镽edis Pub/Sub是發(fā)射既忘(fire and forget),目前沒(méi)有辦法使用這個(gè)特征,如果你的應(yīng)用需求是事件的可信通知的話,那是因?yàn)?,如果你的Pub/Sub客戶端失去連接,且稍后重連接時(shí),所有的事件在重新連接時(shí)都丟失。
未來(lái)有計(jì)劃允許更可靠的事件傳遞,不過(guò)這將很可能定位在更一般層級(jí)上的,要么是給Pub/Sub本身帶來(lái)可靠性,要么是允許Lua腳本攔截Pub/Sub消息以執(zhí)行諸如將事件推入列表這樣的操作。
事件類型
鍵空間的通知通過(guò)為每個(gè)影響Redis數(shù)據(jù)空間的操作發(fā)送兩種不同的事件類型實(shí)現(xiàn)。比如,一個(gè)DEL操作命中數(shù)據(jù)庫(kù)0中名為mykey的鍵將觸發(fā)傳遞2個(gè)消息,準(zhǔn)確來(lái)說(shuō)等于下面的兩個(gè)PUBLISH命令:
PUBLISH __keyspace@0__:mykey del
PUBLISH __keyevent@0__:del mykey
很容易的看到,一個(gè)通道是如何允許我們監(jiān)聽(tīng)命中鍵mykey的所有事件,并且其他通道允許獲取關(guān)于del操作的目標(biāo)的所有鍵的信息。
第一種類型是通道中的以keyspace為前綴,被稱為Key-space notification的事件,而第二種,是以keyevent為前綴,被稱為Key-event notification的事件。
在上面的例子中,一個(gè)del事件為鍵mykey而生成。會(huì)發(fā)生什么:
- 鍵空間通道接收了以事件為名稱的消息。
- 鍵事件通道接收了以鍵為名稱的消息。
為了傳遞我們感興趣的子集的事件,僅開(kāi)啟通知中的一種是可能的。
配置
默認(rèn)鍵空間事件通知是關(guān)閉的,因?yàn)檫@個(gè)功能使用了一些不能明確感知的CPU電源。是用redis.conf中的notify-keyspace-events或者通過(guò)CONFIG SET開(kāi)啟通知。
設(shè)置參數(shù)到空字符串禁止通知。為了開(kāi)啟一個(gè)已經(jīng)使用的非空字符串的特征,組合多個(gè)字符,每個(gè)字符有一個(gè)特殊的意味,就像下面的表格一樣:
K Keyspace events, published with __keyspace@<db>__ prefix.
E Keyevent events, published with __keyevent@<db>__ prefix.
g Generic commands (non-type specific) like DEL, EXPIRE, RENAME, ...
$ String commands
l List commands
s Set commands
h Hash commands
z Sorted set commands
t Stream commands
x Expired events (events generated every time a key expires)
e Evicted events (events generated when a key is evicted for maxmemory)
m Key miss events (events generated when a key that doesn't exist is accessed)
A Alias for "g$lshztxe", so that the "AKE" string means all the events except "m".
至少K或E必須在字符串中出現(xiàn),否則剩余的字符串中將不會(huì)有事件會(huì)被傳遞。
例如,為列表僅開(kāi)啟Key-space事件,這個(gè)配置參數(shù)必須設(shè)置為K1,等等。
字符串KEA可以用于開(kāi)啟每個(gè)可能的事件。
不同命令生成的事件
-
DEL 為每個(gè)被刪除的鍵生成一個(gè)
del事件。 -
RENAME 生成兩個(gè)事件,一個(gè)是為源鍵生成
rename_from事件,另一個(gè)是為目標(biāo)鍵生成rename_to事件。 -
MOVE 生成兩個(gè)時(shí)間,一個(gè)是為源鍵生成
move_from時(shí)間,另一個(gè)是為目標(biāo)鍵生成move_to事件。 -
COPY 生成一個(gè)
copy_to事件。 -
MIGRATE 如果源鍵被移除,生成一個(gè)
del事件。 -
RESTORE 為此鍵生成一個(gè)
restore事件。 -
EXPIRE 和它所有的變體(PEXPIRE, EXPIREAT, PEXPIREAT)使用一個(gè)正的超時(shí)時(shí)間(或一個(gè)未來(lái)的時(shí)間戳)調(diào)用時(shí),會(huì)生成一個(gè)
expire事件。 -
SORT 當(dāng)
store用于設(shè)置一個(gè)新的鍵時(shí),生成一個(gè)sortstore事件。如果結(jié)果列表為空,并且STORE選項(xiàng)已經(jīng)使用,并且有一個(gè)已經(jīng)存在的鍵使用那個(gè)名字,那么結(jié)果就是那個(gè)鍵被刪除,因此在這種情況下一個(gè)del事件被生成。 -
SET 和其所有的變種(SETEX, SETNX,GETSET)生成一個(gè)
set事件。然而SETEX還會(huì)生成一個(gè)expire事件。 -
MSET 為每個(gè)鍵生成一個(gè)單獨(dú)的
set事件。 -
SETRANGE 生成一個(gè)
setrange事件。 -
INCR, DECR, INCRBY, DECRBY 這些命令都生成
incrby事件。 -
INCRBYFLOAT 生成一個(gè)
incrbyfloat事件。 -
APPEND 生成一個(gè)
append事件。 -
LPUSH 和 LPUSHX 生成一個(gè)單獨(dú)的
lpush事件,即使在一個(gè)可變參數(shù)的情況下。 -
RPUSH 和 RPUSHX 生成一個(gè)單獨(dú)的
rpush事件,即使在可變參數(shù)的情況下。 -
RPOP 生成一個(gè)
rpop事件。如果因?yàn)榱斜碇凶詈笠粋€(gè)元素被彈出而導(dǎo)致這個(gè)鍵被刪除時(shí),會(huì)有一個(gè)額外的del事件生成。 -
LPOP 生成一個(gè)
lpop事件。如果因?yàn)榱斜碇凶詈笠粋€(gè)元素被彈出而導(dǎo)致這個(gè)鍵被刪除時(shí),會(huì)有一個(gè)額外的del事件生成。 -
LINSERT 生成一個(gè)
linsert事件。 -
LSET 生成一個(gè)
lset事件。 -
LREM 生成一個(gè)
lrem事件,如果結(jié)果列表為空且這個(gè)鍵被刪除時(shí),會(huì)有一個(gè)額外的del事件生成。 -
LTRIM 生成一個(gè)
ltrim事件,如果結(jié)果列表為空且這個(gè)鍵被刪除時(shí),會(huì)有一個(gè)額外的del事件生成。 -
RPOPLPUSH 和 BRPOPLPUSH 生成一個(gè)
rpop事件和一個(gè)lpush事件。在這兩種情況下順序都是有保證的(lpush總是在rpop)。 -
LMOVE 和 BLMOVE 生成一個(gè)
lpop/rpop事件(取決于來(lái)向參數(shù))和一個(gè)lpush/rpush事件(取決于去向參數(shù))。在這兩種情況下順序都是有保障的(lpush/rpush事件總是在lpop/rpop事件之后傳遞)。當(dāng)結(jié)果列表長(zhǎng)度為0并且該鍵被移除后,會(huì)產(chǎn)生一個(gè)額外的del事件。 -
HSET, HSETNX 和 HMSET 都生成一個(gè)
hset事件。 -
HINCRBY生成一個(gè)
hincrby事件。 -
HINCRBYFLOAT 生成一個(gè)
hincrbyfloat事件。 -
HDEL 生成一個(gè)單獨(dú)的
hdel事件,如果結(jié)果哈希為空且被移除的話,會(huì)生成一個(gè)額外的del事件。 -
SADD 生成一個(gè)單獨(dú)的
sadd事件,甚至在可變參的情形中。 -
SREM 生成一個(gè)
srem事件,如果結(jié)果集為空且鍵被移除的話,會(huì)生成一個(gè)額外的del事件。 -
SREM 生成一個(gè)單獨(dú)的
srem事件,和一個(gè)額外的del事件。 -
SMOVE 為每個(gè)源鍵生成一個(gè)
srem事件,并為每個(gè)目標(biāo)鍵生成一個(gè)sadd事件。 -
SPOP 生成一個(gè)
spop事件,如果結(jié)果集合為空且鍵被移除的話,會(huì)生成一個(gè)額外的del事件。 -
SINTERSTORE, SUNIONSTORE, SDIFFSTORE 分別生成一個(gè)
sinterstore,sunionstore,sdiffstore事件。在特定情況下,結(jié)果集合為空,且結(jié)果中已經(jīng)保存了該鍵,當(dāng)這個(gè)鍵移除時(shí)會(huì)生成一個(gè)del事件。 -
ZINCR生成一個(gè)zincr事件。 -
ZADD 生成單個(gè)
zadd事件,即使當(dāng)添加多個(gè)元素時(shí)。 -
ZREM 生成單個(gè)
zrem事件,即使當(dāng)多個(gè)元素被刪除時(shí)。當(dāng)結(jié)果有序集合為空且鍵已生成時(shí),會(huì)生成一個(gè)額外的del事件。 -
ZREMBYSCORE生成單個(gè)的zrembyscore事件。當(dāng)結(jié)果有序集合為空且鍵已生成時(shí),會(huì)生成一個(gè)額外的del事件。 -
ZREMBYRANK生成單個(gè)zrembyrank事件。當(dāng)結(jié)果有序集合為空且鍵生成時(shí),會(huì)生成一個(gè)額外的del事件。 -
ZDIFFSTORE, ZINTERSTORE 和 ZUNIONSTORE 分別生成
zdiffstore,zinterstore和zunionstore事件。在特殊情況下,當(dāng)結(jié)果有序集合為空,且鍵已經(jīng)在存儲(chǔ)結(jié)果中存在時(shí),如果鍵被移除會(huì)生成一個(gè)del事件。 -
XADD 生成一個(gè)
xadd事件,當(dāng)和子命令MAXLEN一起使用時(shí),可能會(huì)跟著一個(gè)xtrim事件。 -
XDEL 生成單個(gè)
xdel事件,即使當(dāng)多個(gè)實(shí)體被刪除時(shí)。 -
XGROUP CREATE生成一個(gè)xgroup-create事件。 -
XGROUP CREATECONSUMER生成一個(gè)xgroup-createconsumer事件。 -
XGROUP DELCONSUMER生成一個(gè)xgroup-delconsumer事件。 -
XGROUP DESTROY生成一個(gè)xgroup-destroy事件。 -
XGROUP SETID生成一個(gè)xgroup-setid事件。 -
XSETID生成一個(gè)xsetid事件。 -
XTRIM 生成一個(gè)
xtrim事件。 -
PERSIST 生成一個(gè)
persist事件,如果鍵關(guān)聯(lián)的過(guò)期時(shí)間被成功刪除的話。 - 每當(dāng)一個(gè)關(guān)聯(lián)了生存時(shí)間的鍵因?yàn)檫^(guò)期而被從數(shù)據(jù)集中刪除時(shí),一個(gè)
expired事件生成了。 - 每當(dāng)出于
maxmemory策略的結(jié)果,將一個(gè)鍵從數(shù)據(jù)集中回收從而釋放內(nèi)存的時(shí)候,一個(gè)evicted事件就會(huì)生成。
重要 所有的命令只在目標(biāo)鍵真實(shí)改變時(shí)生成事件。比如,一個(gè)SREM 從集合中刪除一個(gè)不存在的元素時(shí),將不會(huì)真正的改變鍵的值,因此將不會(huì)有事件生成。
如果對(duì)一個(gè)給定的命令如何生成一個(gè)事件有疑問(wèn)的話,最簡(jiǎn)單的事情就是觀察你自己:
$ redis-cli config set notify-keyspace-events KEA
$ redis-cli --csv psubscribe '__key*__:*'
Reading messages... (press Ctrl-C to quit)
"psubscribe","__key*__:*",1
這時(shí),在另外一個(gè)客戶端使用redis-cli發(fā)送命令到Redis服務(wù)端,并且觀察事件的生成:
"pmessage","__key*__:*","__keyspace@0__:foo","set"
"pmessage","__key*__:*","__keyevent@0__:set","foo"
...
過(guò)期事件的時(shí)間線
Redis通過(guò)兩種方式使與生存時(shí)間關(guān)聯(lián)的鍵過(guò)期:
- 當(dāng)該鍵被一個(gè)命令訪問(wèn),且被發(fā)現(xiàn)過(guò)期了。
- 通過(guò)后臺(tái)系統(tǒng),以增量方式在后臺(tái)查找過(guò)期的鍵,同時(shí)搜集那些從未被訪問(wèn)過(guò)的鍵。
當(dāng)一個(gè)鍵被訪問(wèn)且被上面的系統(tǒng)之一發(fā)現(xiàn)過(guò)期后,產(chǎn)生一個(gè)expired事件,因此不能保證Redis服務(wù)器能夠在鍵的生存時(shí)間到0時(shí)生成expired事件。
如果沒(méi)有命令經(jīng)常訪問(wèn)鍵,且有很多有TTL關(guān)聯(lián)的鍵,在鍵的生存時(shí)間降低為0和生成expired事件的時(shí)間之間將會(huì)有一個(gè)較大的延遲。
基本上來(lái)講,是當(dāng)Redis服務(wù)器刪除鍵時(shí)而并不是鍵的理論的生存時(shí)間到達(dá)為0時(shí),生成expired事件。
集群中的事件
如上所述,每個(gè)Redis集群生成自己鍵空間的子集。可是,不像規(guī)則的Pub/Sub在集群中的通訊,事件通知不是廣播到所有的節(jié)點(diǎn)。不同的是,鍵空間事件是節(jié)點(diǎn)特異性的。這意味著,為了接收集群的所有的鍵空間事件,客戶端需要訂閱每個(gè)節(jié)點(diǎn)。
History 記錄
-
>= 6.0:增加了鍵丟失事件