重要: 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@__ 前缀发布.
E 键事件事件, 通过 __keyevent@__ 前缀发布.
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
事件. - LPUSH 和LPUSHX 生成一个单独的
lpush
事件, 即使在可变的情况下. - RPUSH 和 RPUSHX 生成一个单独的
rpush
事件, 即使在可变的情况下. - RPOP 生成一个
rpop
事件. 另外因为列表最后一个元素被弹出, 所以键被移除, 会生成一个del
事件. - LPOP 生成一个
lpop
事件. 另外因为列表最后一个元素被弹出, 所以键被移除, 会生成一个del
事件. - LINSERT 生成一个
linsert
事件. - LSET 生成一个
lset
事件. - LREM 生成一个
lrem
事件, 另外如果结果列表为空以及键被移除, 会生成一个del
事件. - LTRIM生成一个
ltrim
事件, 另外如果结果列表为空以及键被移除, 会生成一个del
事件. - RPOPLPUSH 和BRPOPLPUSH 生成一个
rpop
事件和一个lpush
事件. 在两种情况下都可以保证命令 (lpush
事件将经常在rpop
事件后传递). 另外如果结果列表为空以及键被移除会生成一个del
事件. - HSET, HSETNX 和HMSET 都会生成一个 单独的
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 分别生成
zinterstore
和zunionstore
事件. 在特殊情况下, 结果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服务器删除这个键的时候生成 , 而不是理论上的生存时间达到零值时.