Redis鍵空間通知

重要 鍵空間通知是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".

至少KE必須在字符串中出現(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事件。
  • LPUSHLPUSHX 生成一個(gè)單獨(dú)的lpush事件,即使在一個(gè)可變參數(shù)的情況下。
  • RPUSHRPUSHX 生成一個(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事件生成。
  • RPOPLPUSHBRPOPLPUSH 生成一個(gè)rpop事件和一個(gè)lpush事件。在這兩種情況下順序都是有保證的(lpush總是在rpop)。
  • LMOVEBLMOVE 生成一個(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, HSETNXHMSET 都生成一個(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, ZINTERSTOREZUNIONSTORE 分別生成 zdiffstore, zinterstorezunionstore 事件。在特殊情況下,當(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:增加了鍵丟失事件
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容