介绍

键空间通知可以让客户端实时监听Redis键和值的变化,比如删除、新增、修改重命名等

配置

因为开启键空间通知功能需要消耗一些CPU,所以默认清空下该功能是关闭的,可以通过修改redis.conf文件,windows版本是redis.windows.conf文件,或者使用config set

  • 当notify-keyspace-events选项为空字符串时,该功能关闭
  • 当notify-keyspace-events选项不为空字符串时,该功能开启
  • 当notify-keyspace-events选项为KEA时,开启所有配置
  • 要注意大小写

notify-keyspace-events可以是以下任意组合

字符

发送的通知

K

键空间通知,所有通知以 __keyspace@<db>__ 为前缀

E

键事件通知,所有通知以 __keyevent@<db>__ 为前缀

g

del、expire、rename 等类型无关的通用命令的通知

$

string命令的通知

l

list命令的通知

s

set命令的通知

h

hash命令的通知

z

zset命令的通知

x

过期事件:每当有过期键被删除时发送

e

驱逐(evict)事件:每当有键因为 maxmemory 政策而被删除时发送

A

参数 g$lshzxe 的别名

注:参数至少要有一个k或者E,否则不会有任何通知,将参数设为AKE表示所有类型的通知

各种通知的产生

  • DEL key [key …] 命令为每个被删除的键产生一个 del 通知。
  • RENAME key newkey 产生两个通知:为来源键(source key)产生一个 rename_from 通知,并为目标键(destination key)产生一个 rename_to 通知。
  • EXPIRE key seconds 和 EXPIREAT key timestamp 在键被正确设置过期时间时产生一个 expire 通知。当 EXPIREAT key timestamp 设置的时间已经过期,或者 EXPIRE key seconds 传入的时间为负数值时,键被删除,并产生一个 del 通知。
  • SORT key [BY pattern] [LIMIT offset count] [GET pattern [GET pattern …]] [ASC | DESC] [ALPHA] [STORE destination] 在命令带有 STORE 参数时产生一个 sortstore 事件。如果 STORE 指示的用于保存排序结果的键已经存在,那么程序还会发送一个 del 事件。
  • SET key value [EX seconds] [PX milliseconds] [NX|XX] 以及它的所有变种(SETEX key seconds value 、 SETNX key value 和 GETSET key value)都产生 set 通知。其中 SETEX key seconds value 还会产生 expire 通知。
  • MSET key value [key value …] 为每个键产生一个 set 通知。
  • SETRANGE key offset value 产生一个 setrange 通知。
  • INCR key 、 DECR key 、 INCRBY key increment 和 DECRBY key decrement 都产生 incrby 通知。
  • INCRBYFLOAT key increment 产生 incrbyfloat 通知。
  • APPEND key value 产生 append 通知。
  • LPUSH key value [value …] 和 LPUSHX key value 都产生单个 lpush 通知,即使有多个输入元素时,也是如此。
  • RPUSH key value [value …] 和 RPUSHX key value 都产生单个 rpush 通知,即使有多个输入元素时,也是如此。
  • RPOP key 产生 rpop 通知。如果被弹出的元素是列表的最后一个元素,那么还会产生一个 del 通知。
  • LPOP key 产生 lpop 通知。如果被弹出的元素是列表的最后一个元素,那么还会产生一个 del 通知。
  • LINSERT key BEFORE|AFTER pivot value 产生一个 linsert 通知。
  • LSET key index value 产生一个 lset 通知。
  • LTRIM key start stop 产生一个 ltrim 通知。如果 LTRIM key start stop 执行之后,列表键被清空,那么还会产生一个 del 通知。
  • RPOPLPUSH source destination 和 BRPOPLPUSH source destination timeout 产生一个 rpop 通知,以及一个 lpush 通知。两个命令都会保证 rpop 的通知在 lpush 的通知之前分发。如果从键弹出元素之后,被弹出的列表键被清空,那么还会产生一个 del 通知。
  • HSET hash field value 、 HSETNX hash field value 和 HMSET 都只产生一个 hset 通知。
  • HINCRBY 产生一个 hincrby 通知。
  • HINCRBYFLOAT 产生一个 hincrbyfloat 通知。
  • HDEL 产生一个 hdel 通知。如果执行 HDEL 之后,哈希键被清空,那么还会产生一个 del 通知。
  • SADD key member [member …] 产生一个 sadd 通知,即使有多个输入元素时,也是如此。
  • SREM key member [member …] 产生一个 srem 通知,如果执行 SREM key member [member …] 之后,集合键被清空,那么还会产生一个 del 通知。
  • SMOVE source destination member 为来源键(source key)产生一个 srem 通知,并为目标键(destination key)产生一个 sadd 事件。
  • SPOP key 产生一个 spop 事件。如果执行 SPOP key 之后,集合键被清空,那么还会产生一个 del 通知。
  • SINTERSTORE destination key [key …] 、 SUNIONSTORE destination key [key …] 和 SDIFFSTORE destination key [key …] 分别产生 sinterstore 、 sunionostore 和 sdiffstore 三种通知。如果用于保存结果的键已经存在,那么还会产生一个 del 通知。
  • ZINCRBY key increment member 产生一个 zincr 通知。(译注:非对称,请注意。)
  • ZADD key score member [[score member] [score member] …] 产生一个 zadd 通知,即使有多个输入元素时,也是如此。
  • ZREM key member [member …] 产生一个 zrem 通知,即使有多个输入元素时,也是如此。如果执行 ZREM key member [member …] 之后,有序集合键被清空,那么还会产生一个 del 通知。
  • ZREMRANGEBYSCORE key min max 产生一个 zrembyscore 通知。(译注:非对称,请注意。)如果用于保存结果的键已经存在,那么还会产生一个 del 通知。
  • ZREMRANGEBYRANK key start stop 产生一个 zrembyrank 通知。(译注:非对称,请注意。)如果用于保存结果的键已经存在,那么还会产生一个 del 通知。
  • ZINTERSTORE destination numkeys key [key …] [WEIGHTS weight [weight …]] [AGGREGATE SUM|MIN|MAX] 和 ZUNIONSTORE destination numkeys key [key …] [WEIGHTS weight [weight …]] [AGGREGATE SUM|MIN|MAX] 分别产生 zinterstore 和 zunionstore 两种通知。如果用于保存结果的键已经存在,那么还会产生一个 del 通知。

所有命令只有在key真的被改动之后,才会产生通知,比如del key,如果这个key不存在那么就不会产生通知。

使用Redis-cli来模拟键空间通知

可以先临时更改notify-keyspace-events的选项

127.0.0.1:6379> config set notify-keyspace-events Ex

然后开始订阅

127.0.0.1:6379> psubscribe '__key*__:*'

psubscribe说明请参考psubscribe命令详解

回车之后我们可以看到如下东西

127.0.0.1:6379> psubscribe '__key*__:*'
Reading messages... (press Ctrl-C to quit) #按ctrl+c会退出redis-cli.exe
1) "psubscribe"	 #返回值的类型,显示订阅成功
2) "__key*__:*" #订阅的模式
3) (integer) 1	#目前已订阅成功的数量

然后我们再开一个客户端然后设置一个key

127.0.0.1:6379> set ab a

这边我们就会收到这样的内容

1) "pmessage"	#返回值的类型
2) "__key*__:*"	#订阅的模式
3) "__keyevent@0__:set" #订阅的通知类型
4) "ab" #key名

如果是Hash、list、set和zset的话会增加一个key空间通知

1) "pmessage" #返回值的类型
2) "__key*__:*" #订阅的模式
3) "__keyspace@0__:key" #键空间通知+key名
4) "hset" #通知类型
5) "pmessage" #返回值的类型
6) "__key*__:*" #订阅的模式
7) "__keyevent@0__:hset" #键事件通知+几库+通知类型
8) "key" #key名

如此可以得出,键空间通知是指针对hash、list、set和zset的时候才会产生,而针对String不会产生,所以想要订阅hash、list、set和zset的时候,配置一定要加上K选项。

整合SpringBoot来完成键空间通知

1、引入依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

2、配置好yml

spring:
  redis:
    host: localhost #主机
    port: 6379 #端口号
    password:  #密码
    jedis:
      pool:
        max-active: 8
        max-wait: -1
        max-idle: 500
        min-idle: 0
    lettuce:
      shutdown-timeout: 0

3、增加监听类

下面的topic是订阅set通知,如果还想要订阅其他的请再写一个类,并修改订阅的通知类型

import lombok.Data;
import org.springframework.data.redis.connection.Message;
import org.springframework.data.redis.connection.MessageListener;
import org.springframework.data.redis.listener.PatternTopic;
import org.springframework.stereotype.Component;

@Component
@Data
public class RedisUpdateAndAddListener implements MessageListener {
    //监听的主题
    private  final PatternTopic topic = new PatternTopic("__keyevent@*__:set");

    @Override
    public void onMessage(Message message, byte[] pattern){
        String topic = new String(pattern);
        String msg = new String(message.getBody());
        System.out.println("收到key更新或修改,消息主题是:"+ topic+",消息内容是:"+msg);
    }

}

4、在RedisConf文件增加以下代码

@Autowired
private RedisUpdateAndAddListener redisUpdateAndAddListener;

@Bean
RedisMessageListenerContainer redisMessageListenerContainer(RedisConnectionFactory connectionFactory) {
    RedisMessageListenerContainer container = new RedisMessageListenerContainer();

    container.setConnectionFactory(connectionFactory);

    //监听所有的key的set事件
    container.addMessageListener(redisUpdateAndAddListener, redisUpdateAndAddListener.getTopic());
    return container;
}

到此则已经完成了整合SpringBoot。