Redis Keyspace Notifications(Redis键空间通知)

时间:2022-07-23
本文章向大家介绍Redis Keyspace Notifications(Redis键空间通知),主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

重要: Redis在2.8.0版本后支持键空间通知功能

功能概述

键空间通知允许客户端订阅发布/订阅通道, 来接收某些影响Redis数据的事件回调.

例如发生下面这些事件:

  • 所有影响给定键的命令时
  • 所有键受到LPUSH操作时.
  • 所有key在数据库0中过期时.

因为是使用Redis常规的 发布/订阅层传递事件, 所以发布/订阅客户端不用修改就能使用这个功能.

因为Redis的发布/订阅目前是发送后不再关心(fire and forget), 所以如果你应用需要有可靠的通知事件, 那么就无法使用这个功能, 也就是说, 如果你的 发布/订阅 客户端断开了, 以及后面重连了, 所有在你断开时间内触发的事件都会丢失.

在未来, 有计划来允许更可靠的事件传递, 但是这可能将会在更一般的层面为 发布/订阅 本身带来可靠性, 或者允许Lua脚本截获 发布/订阅 的消息, 来执行像把事件推入列表志列的操作.

事件类型

键空间通知是通过向影响Redis数据空间的每个操作发送两种不同类型的事件来实现的. 例如一个对在数据库0中名为mykey的键的 DEL操作, 将会触发两条消息传递, 这等效于下面两个PUBLISH 命令:

PUBLISH __keyspace@0__:mykey del
PUBLISH __keyevent@0__:del mykey

很容易可以看到一个通道是如何让我们能够监听到所有针对键名为mykey的事件, 而另一个通道则允许获得关于被del操作的所有键的信息.

第一类事件, 带有 keyspace前缀的事件叫做 Key-space notification, 第二类事件, 带有keyevent 前缀的事件叫做Key-event notification.

上面的例子中, 一个 del 事件会为键名为mykey的键生成事件. 将会发生:

  • Key-space 通道接收以事件名称作为内容的消息.
  • Key-event 通道接收以键名作为内容的消息.

为了能传递我们感兴趣的事件的子集, 可能只启用一种通知.

配置

默认情况下键空间时间通知处于禁用状态, 因为该功能会占用一些CPU资源. 使用Redis的notify-keyspace-events 来启用通知. 或者通过 CONFIG SET.

将参数设置为空字符串将会禁用通知. 为了启用该功能, 我们要使用由多个字符组成的非空字符串, 在下表中, 我们可以看到, 每个字符都有特殊的含义:

K     键空间事件, 通过 __keyspace@<db>__ 前缀发布.
E     键事件事件, 通过 __keyevent@<db>__ 前缀发布.
g     通用命令 (非特定类型) 例如 DEL, EXPIRE, RENAME, ...
$     字符串命令
l      列表命令
s     Set 命令
h     Hash 命令
z     排序集命令
t      流命令
x     过期事件 (键过期时生成的事件)
e     撤出事件(当键由于最大内存策略而被撤出时生成的事件) Evicted events (events generated when a key is evicted for maxmemory)
A      g$lshztxe的别名, so that the "AKE" string means all the events.

无论如何 K 或者 E 应该以字符串存在, 否则不管字符串其余部分是什么, 都不会传递任何事件.

例如, 仅启用列表的键空间事件, 配置参数必须设置为Kl, 依此类推.

KEA 字符串可以被用来启用所有可能的事件.

不同命令生成的事件

下面列表展示了不同命令生成的事件.

  • DEL 为每个被删除的键生成del事件.
  • RENAME 生成两个事件, 一个rename_from 是原始键的事件, 另一个rename_to 是目标键的事件.
  • EXPIRE 生成expire事件, 当一个键设置为过期时, 或者生成每次对键设置正超时都会导致键被删除的expired事件(更多详情查看 EXPIRE 文档).
  • SORT 生成 sortstore 事件, 当新的键设置成STORE的时候生成. 如果结果列表为空, 且配置了STORE属性, 并且存在了那个名称的键, 那么会把那个键删除, 因此这种情况下还会生成一个del 事件.
  • SET 以及所有变体 (SETEX, SETNX,GETSET) 生成一个set事件. 然而SETEX 也会生成一个expire事件.
  • MSET 会给每个键分别生成set事件.
  • SETRANGE 生成一个setrange 事件.
  • INCR, DECR, INCRBY, DECRBY 命令全部生成incrby 事件.
  • INCRBYFLOAT 生成一个incrbyfloat 事件.
  • APPEND 生成一个 append 事件.
  • LPUSHLPUSHX 生成一个单独的lpush事件, 即使在可变的情况下.
  • RPUSHRPUSHX 生成一个单独的rpush事件, 即使在可变的情况下.
  • RPOP 生成一个rpop 事件. 另外因为列表最后一个元素被弹出, 所以键被移除, 会生成一个del事件.
  • LPOP 生成一个lpop 事件. 另外因为列表最后一个元素被弹出, 所以键被移除, 会生成一个del事件.
  • LINSERT 生成一个linsert 事件.
  • LSET 生成一个 lset 事件.
  • LREM 生成一个lrem 事件, 另外如果结果列表为空以及键被移除, 会生成一个del事件.
  • LTRIM生成一个ltrim事件, 另外如果结果列表为空以及键被移除, 会生成一个del事件.
  • RPOPLPUSHBRPOPLPUSH 生成一个rpop 事件和一个 lpush 事件. 在两种情况下都可以保证命令 (lpush事件将经常在rpop事件后传递). 另外如果结果列表为空以及键被移除会生成一个 del 事件.
  • HSET, HSETNXHMSET 都会生成一个 单独的 hset 事件.
  • HINCRBY 生成一个hincrby 事件.
  • HINCRBYFLOAT 生成一个 hincrbyfloat 事件.
  • HDEL 生成一个单独的hdel事件, 以及如果hash结果为空和键被移除的话, 会另外生成一个del.
  • SADD 生成一个sadd事件,甚至在可变情况下.
  • SREM 生成一个srem 事件, 如果结果set为空和键被移除, 会另外生成一个del事件.
  • SMOVE 给原始键生成一个srem事件, 以及给目标键生成一个sadd .
  • SPOP 生成一个 spop 事件, 如果结果set为空和键被移除, 会另外生成素一个del事件.
  • SINTERSTORE, SUNIONSTORE, SDIFFSTORE 分别生成sinterstore, sunionstore, sdiffstore事件. 在特殊情况下, 结果set为空, 以及键已存在, 将会在键被移除时生成一个 del 事件.
  • ZINCR 生成一个 zincr 事件.
  • ZADD 生成 zadd 事件, 甚至当多节点被添加的时候.
  • ZREM 生成 zrem事件, 甚至当多节点被删除的时候. 当结果排序集为空和并生成键时, 一个额外的del会被生成.
  • ZREMBYSCORE 生成 zrembyscore事件. 当排序结果集为空和生成键时,一个额外的del会被生成.
  • ZREMBYRANK 生成一个zrembyrank事件. 当排序结果集为空和生成键时,一个额外的del会被生成.
  • ZINTERSTORE and ZUNIONSTORE 分别生成zinterstorezunionstore 事件. 在特殊情况下, 结果set为空, 以及键已存在, 将会在键被移除时生成一个 del 事件.
  • XADD 生成xadd 事件, 使用 MAXLEN 子命令时, 可能会伴随xtrim 事件.
  • XDEL 生成 xdel 事件, 即使多个入口被删除.
  • XGROUP CREATE 生成 xgroup-create 事件.
  • XGROUP DELCONSUMER 生成xgroup-delconsumer 事件.
  • XGROUP DESTROY生成 xgroup-destroy 事件.
  • XGROUP SETID 生成xgroup-setid 事件.
  • XSETID 生成xsetid 事件.
  • XTRIM 生成xtrim 事件.
  • 每次将过期了的具有生存事件的键从数据集中删除时, 会生成expired .
  • 每次由于maxmemory策略而从数据集中撤出键时, 会生成evicted.

重要: 所有的命令只有在键确实被修改的时候才会生成事件. 例如 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-cli 在另一个终端里发送命令到Redis服务器, 查看事件生成:

"pmessage","__key*__:*","__keyspace@0__:foo","set"
"pmessage","__key*__:*","__keyevent@0__:set","foo"
...

过期事件的事件

Redis通过两种方式使具有生存时间的键失效:

  • 当键通过命令访问被发现已过期的时候.
  • 通过后台系统来查找过期的键, 以便能够收集从未访问过的密钥.

expired 事件是在访问键时被生成的 , 并且被上述的其中一种机制发现, 因此无法保证在建的生存时间达到零值的时候, Redis服务器能够生成 expired 事件.

如果没有命令始终以这个键为目标, 并且有许多键与TTL关联, 那么在键的生存时间变为零的事件与expired 事件生成的时间之间会有很大的延迟.

基本上 expired 时间会在Redis服务器删除这个键的时候生成 , 而不是理论上的生存时间达到零值时.