You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何通过Jedis捕获Redis中特定的新增与更新缓存事件

准确区分Redis新增与更新Key事件的解决方案

问题根源

当通过SET类命令创建新Key时,Redis会同时触发两类事件:

  • __keyevent@0__:new:键被首次创建时触发
  • __keyevent@0__:set:键值被设置时触发(无论键是否已存在)
    这是Redis的默认行为,所以你会看到新增Key时两个事件同时出现。

解决方案:临时标记过滤重复事件

通过本地缓存记录刚触发new事件的Key,设置短过期时间,在处理set事件时过滤掉新增导致的重复触发。

步骤1:修改监听类,添加临时标记缓存

使用线程安全的本地缓存(如Guava Cache)存储刚创建的Key,自动清理过期条目:

import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;

import java.util.concurrent.TimeUnit;

public class RedisCacheEventHandleConfig implements MessageListener {
    private final CacheHandleService cacheHandleService;
    private final Gson gson;
    private final JedisPool jedisPool;
    // 本地缓存:存储刚触发new事件的Key,100ms后自动过期
    private final Cache<String, Boolean> newKeyCache = CacheBuilder.newBuilder()
            .expireAfterWrite(100, TimeUnit.MILLISECONDS)
            .concurrencyLevel(Runtime.getRuntime().availableProcessors())
            .build();

    // 构造函数注入连接池,避免每次创建新Jedis实例
    public RedisCacheEventHandleConfig(CacheHandleService cacheHandleService, Gson gson) {
        this.cacheHandleService = cacheHandleService;
        this.gson = gson;
        this.jedisPool = new JedisPool(new JedisPoolConfig(), "localhost", 6379);
    }

    @Override
    public void onMessage(Message message, byte[] pattern) {
        String key = new String(message.getBody());
        String channel = new String(message.getChannel());
        log.info("Received task event: {} for channel: {}", key, channel);

        switch (channel) {
            case "__keyevent@0__:new":
                log.info("HANDLING NEW CACHE EVENT!!!");
                cacheHandleService.handleAddCacheForKey(key);
                // 将Key存入临时缓存,标记为刚创建
                newKeyCache.put(key, true);
                break;
            case "__keyevent@0__:set":
                // 若Key刚被创建,忽略本次set事件
                if (newKeyCache.getIfPresent(key) != null) {
                    log.info("Ignoring set event for newly created key: {}", key);
                    break;
                }
                // 使用连接池获取Jedis实例,自动释放资源
                try (Jedis jedis = jedisPool.getResource()) {
                    DataExample res = gson.fromJson(jedis.get(key), DataExample.class);
                    if (res != null){
                        log.info("HANDLING UPDATE CACHE EVENT!!!");
                        cacheHandleService.handleUpdateCacheForKey(key);
                    }
                }
                break;
            case "__keyevent@0__:expired":
                log.info("HANDLING EXPIRED CACHE EVENT!!!");
                cacheHandleService.handleExpiredCacheForKey(key);
                break;
        }
    }
}

步骤2:优化说明

  • 临时缓存过期时间:100ms足够覆盖Redis事件的传输延迟,既不会漏判真正的更新,也不会占用过多内存。
  • Jedis连接池:替换每次创建新连接的方式,提升性能并避免资源泄漏。
  • 线程安全:Guava Cache本身支持多线程场景,适合事件监听的并发处理。

备选方案:基于业务命令区分

如果业务中新增和更新使用不同命令(比如新增用SETNX,更新用SET),可以开启Redis的命令事件监听(__keyevent@0__:command),通过判断命令类型区分场景,但这种方式依赖业务命令规范,通用性不如临时标记方案。


内容的提问来源于stack exchange,提问作者Đinh Tuấn Anh

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.20 10:20:32