实时监控 Redict 键和值的更改

键空间通知允许客户端订阅 Pub/Sub 频道,以接收以某种方式影响 Redict 数据集的事件。

可以接收的事件示例包括:

  • 影响给定键的所有命令。
  • 接收 LPUSH 操作的所有键。
  • 在数据库 0 中过期的所有键。

注意:Redict Pub/Sub 是 fire and forget 的,也就是说,如果您的 Pub/Sub 客户端断开连接,并且在稍后重新连接,客户端断开连接期间传递的所有事件都会丢失。

事件类型

键空间通知通过为每个影响 Redict 数据空间的操作发送两种不同类型的事件来实现。例如,针对数据库 0 中名为 mykey 的键的 DEL 操作将触发传递两个消息,这两个消息与以下两个 PUBLISH 命令完全等效:

  1. PUBLISH __keyspace@0__:mykey del
  2. PUBLISH __keyevent@0__:del mykey

第一个频道监听针对键 mykey 的所有事件,另一个频道仅监听键 mykey 上的 del 操作事件。

第一种类型的事件,以 keyspace 前缀在频道中,称为 键空间通知,而第二种,以 keyevent 前缀,称为 键事件通知

在前面的示例中,为键 mykey 生成了一个 del 事件,导致两条消息:

  • 键空间频道接收事件名称作为消息。
  • 键事件频道接收键名称作为消息。

可以仅启用一种类型的通知,以便仅传递我们感兴趣的事件子集。

配置

默认情况下,键空间事件通知是禁用的,因为虽然这个特性不是很敏感,但是会使用一些 CPU 资源。可以使用 redict.conf 中的 notify-keyspace-events 或通过 CONFIG SET 启用通知。

将参数设置为空字符串会禁用通知。为了启用此特性,使用一个非空字符串,由多个字符组成,每个字符都有特殊含义,如下表所示:

  1. K Keyspace 事件,以 __keyspace@<db>__ 前缀发布。
  2. E Keyevent 事件,以 __keyevent@<db>__ 前缀发布。
  3. g 通用命令(非类型特定)如 DELEXPIRERENAME,...
  4. $ 字符串命令
  5. l 列表命令
  6. s 集合命令
  7. h 哈希命令
  8. z 有序集合命令
  9. t 流命令
  10. d 模块键类型事件
  11. x 过期事件(每次键过期时生成的事件)
  12. e 逐出事件(当键因 maxmemory 被逐出时生成的事件)
  13. m 键缺失事件(当访问不存在的键时生成的事件)
  14. n 新键事件(注意:未包含在 'A' 类中)
  15. A g$lshztxed”的别名,因此“AKE”字符串意味着除“m”和“n”之外的所有事件。

字符串中至少应该有 KE,否则不管字符串的其余部分如何,都不会传递任何事件。

例如,要仅启用针对列表的键空间事件,必须将配置参数设置为 Kl,以此类推。

您可以使用字符串 KEA 来启用大多数类型的事件。

不同命令生成的事件

不同命令根据以下列表生成不同类型的事件。

  • DEL 为每个被删除的键生成一个 del 事件。
  • RENAME 生成两个事件,源键的 rename_from 事件和目标键的 rename_to 事件。
  • MOVE 生成两个事件,源键的 move_from 事件和目标键的 move_to 事件。
  • COPY 生成一个 copy_to 事件。
  • MIGRATE 如果源键被移除,则生成一个 del 事件。
  • RESTORE 为键生成一个 restore 事件。
  • EXPIRE 及其所有变体(PEXPIREEXPIREATPEXPIREAT)在调用时带有正超时(或未来时间戳)时生成一个 expire 事件。请注意,当这些命令使用负超时值或过去的时间戳调用时,键被删除,并且只生成一个 del 事件。
  • SORT 当使用 STORE 设置新键时,生成一个 sortstore 事件。如果结果列表为空,并且使用了 STORE 选项,并且已经存在具有该名称的键,则结果是键被删除,因此在这种情况下会生成一个 del 事件。
  • SET 及其所有变体(SETEXSETNX,GETSET)生成 set 事件。然而 SETEX 还将生成一个 expire 事件。
  • MSET 为每个键生成一个单独的 set 事件。
  • SETRANGE 生成一个 setrange 事件。
  • INCRDECRINCRBYDECRBY 命令都生成 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 事件。
  • LMOVEBLMOVE 生成一个 lpop/rpop 事件(取决于 wherefrom 参数)和一个 lpush/rpush 事件(取决于 whereto 参数)。在这两种情况下,顺序都是保证的(lpush/rpush 事件总是在 lpop/rpop 事件之后传递)。此外,如果结果列表的长度为零并且键被移除,还会生成一个 del 事件。
  • HSETHSETNXHMSET 都生成一个单独的 hset 事件。
  • HINCRBY 生成一个 hincrby 事件。
  • HINCRBYFLOAT 生成一个 hincrbyfloat 事件。
  • HDEL 生成一个单独的 hdel 事件,如果结果哈希为空并且键被移除,还会生成一个 del 事件。
  • SADD 即使在可变参数的情况下,也生成一个单独的 sadd 事件。
  • SREM 生成一个单独的 srem 事件,如果结果集合为空并且键被移除,还会生成一个 del 事件。
  • SMOVE 为源键生成一个 srem 事件,为目标键生成一个 sadd 事件。
  • SPOP 生成一个 spop 事件,如果结果集合为空并且键被移除,还会生成一个 del 事件。
  • SINTERSTORESUNIONSTORESDIFFSTORE 分别生成 sinterstoresunionstoresdiffstore 事件。在特殊情况下,如果结果集合为空,并且已经存在存储结果的键,则会生成一个 del 事件,因为键被移除。
  • ZINCR 生成一个 zincr 事件。
  • ZADD 即使添加了多个元素,也生成一个单独的 zadd 事件。
  • ZREM 即使删除了多个元素,也生成一个单独的 zrem 事件。当结果有序集合为空并且生成键时,还会生成一个额外的 del 事件。
  • ZREMBYSCORE 生成一个 zrembyscore 事件。当结果有序集合为空并且生成键时,还会生成一个额外的 del 事件。
  • ZREMBYRANK 生成一个 zrembyrank 事件。当结果有序集合为空并且生成键时,还会生成一个额外的 del 事件。
  • ZDIFFSTOREZINTERSTOREZUNIONSTORE 分别生成 zdiffstorezinterstorezunionstore 事件。在特殊情况下,如果结果有序集合为空,并且已经存在存储结果的键,则会生成一个 del 事件,因为键被移除。
  • XADD 生成一个 xadd 事件,在使用 MAXLEN 子命令时,可能还会跟随一个 xtrim 事件。
  • XDEL 即使删除了多个条目,也生成一个单独的 xdel 事件。
  • XGROUP CREATE 生成一个 xgroup-create 事件。
  • XGROUP CREATECONSUMER 生成一个 xgroup-createconsumer 事件。
  • XGROUP DELCONSUMER 生成一个 xgroup-delconsumer 事件。
  • XGROUP DESTROY 生成一个 xgroup-destroy 事件。
  • XGROUP SETID 生成一个 xgroup-setid 事件。
  • XSETID 生成一个 xsetid 事件。
  • XTRIM 生成一个 xtrim 事件。
  • PERSIST 如果与键关联的过期时间被成功删除,生成一个 persist 事件。
  • 每当与生存时间相关联的键被 Redict 访问并发现已过期时,会生成一个 expired 事件。
  • 每当由于 maxmemory 策略而从数据集中逐出键以释放内存时,会生成一个 evicted 事件。
  • 每当向数据集添加新键时,会生成一个 new 事件。

重要 所有命令只有在真正修改了目标键时才会生成事件。例如,从集合中删除一个不存在的元素的 SREM 命令实际上并不会改变键的值,因此不会生成任何事件。

如果对给定命令生成的事件有疑问,最简单的方法是自己观察:

  1. $ redict-cli config set notify-keyspace-events KEA
  2. $ redict-cli --csv psubscribe '__key*__:*'
  3. Reading messages... (press Ctrl-C to quit)
  4. "psubscribe","__key*__:*",1

此时,在另一个终端中使用 redict-cli 向 Redict 服务器发送命令,并观察生成的事件:

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

过期事件的时间

带有生存时间关联的键通过以下两种方式由 Redict 过期:

  • 当通过命令访问键并发现已过期时。
  • 通过后台系统在后台查找过期键,逐步进行,以便也能够收集从未被访问的键。

expired 事件是在键被访问并被发现过期时生成的,因此不能保证 Redict 服务器能够在键的生存时间达到零值时生成 expired 事件。

如果没有命令持续地针对键,并且有许多带有 TTL 关联的键,键生存时间降到零和生成 expired 事件之间的延迟可能相当长。

基本上 expired 事件是在 Redict 服务器删除键时 生成的,而不是在生存时间理论上达到零值时。

集群中的事件

Redict 集群的每个节点都会生成关于其自己的键空间子集的事件,如上所述。然而,与集群中常规的 Pub/Sub 通信不同,事件通知 不是 广播到所有节点。换句话说,键空间事件是节点特定的。这意味着要接收集群的所有键空间事件,客户端需要订阅每个节点。

@历史

  • >= 6.0:添加了键缺失事件。
  • >= 7.0:添加了事件类型 new