Keyspace Notification 让你订阅「某个 key 被改了」「某个 key 过期了」这类事件。最常见的用法是用 expired 事件做「到期触发」,比如订单超时关闭。但 expired 事件到底有多准时?这篇量了一个多数人没量过的场景:数据库里有大量 TTL 很长的 key 时,短 TTL key 的过期事件要等多久。
> CONFIG GET notify-keyspace-events 1) "notify-keyspace-events" 2) "" 订阅 __key*__:* 后 SET k v,收到: []
> CONFIG SET notify-keyspace-events KEA
"OK"
> SET k v
__keyspace@0__:k → set
__keyevent@0__:set → k
> LPUSH l a
__keyspace@0__:l → lpush
__keyevent@0__:lpush → l
> EXPIRE k 100
__keyspace@0__:k → expire
__keyevent@0__:expire → k
> RENAME k k2
__keyspace@0__:k → rename_from
__keyevent@0__:rename_from → k
__keyspace@0__:k2 → rename_to
__keyevent@0__:rename_to → k2
> DEL k2
__keyspace@0__:k2 → del
__keyevent@0__:del → k2K 是 keyspace 频道(__keyspace@0__:<key>,内容是事件名),E 是 keyevent 频道(__keyevent@0__:<事件>,内容是 key 名),A 表示所有事件类型。所以一条命令会推两条消息。RENAME 会产生 rename_from 和 rename_to 两个事件。
> CONFIG SET notify-keyspace-events Ex "OK" 延迟(事件到达时间 − 300 ms):最小 2.9 中位 5.3 最大 155.8 ms
每次 SET key v PX 300,然后等 expired 事件,量它比 300 ms 晚多少。30 次:最小 2.9 ms、中位 5.3 ms、最大 156 ms。空闲时事件几乎准点,但最大值说明它不是一个有保证的上限。
长 TTL key 数 20 秒内收到 最早(ms) 中位(ms) 最晚(ms)
0 30/30 4 4 4
1000 22/30 199 8761 18239
10000 13/30 1367 11964 17975
100000 3/30 1430 7950 15390
300000 1/30 6930 6930 6930先往库里灌不同数量、TTL 为 1 小时的 key,再同时设 30 个 300 ms 的 key,看 20 秒内能收到几个 expired 事件。库里没有长 TTL key 时 30/30 都在 4 ms 到;1000 个长 TTL key 时只有 22/30,中位延迟约 8.8 秒;10 万个以上时,20 秒内一个到不了或只到寥寥几个。我把这个实验跑了三次:三次里 1000 个长 TTL key 都是 22–24/30,10000 个是 12–15/30,10 万个是 0–3/30,30 万个是 0–1/30——具体个数每次不同,但趋势一致。上面打印的是最后一次。
这意味着:expired 事件不能当作准点的定时器。库里有大量长 TTL key 时,一个刚过期的 key 可能很久之后才被后台清理发现。为什么?我推测是后台过期清理采用随机抽样(每次只看一小批带 TTL 的 key),池子越大越难抽中那几个过期的——这是我对这个现象的解释,没有读源码验证。
过期 0.4 秒后,GET lazy → None;expired 事件在 GET 之后 0.0 ms 内到达 另一个 key:PX 100,不去访问,(池子里有 300002 个 key,hz=1)等 8 秒:8 秒内没有收到 expired 事件
把 hz 调到 1 让后台清理变慢。一个已过期的 key,被 GET 访问时,返回 nil、同时 expired 事件在 0.05 ms 内到达;而另一个同样过期、但没人访问的 key,在 30 万 key 的库里 8 秒内一直没有事件。所以「读一下」比「等通知」更可靠——但你得知道要读哪个 key。
重新订阅后收到: 什么都没有
Keyspace Notification 基于 Pub/Sub(见第 49 篇),订阅者不在时事件就丢了,没有补发。
关闭通知:735k/s KEA 但无订阅者:606k/s KEA 且有 1 个订阅 set 事件的订阅者:554k/s
开启 KEA 之后管道写入吞吐从 735k/s 降到 606k/s(无订阅者),有一个订阅者时 554k/s。只开需要的事件类型(比如只开 Ex)可以少产生通知——这一点我没有另外测量。