如何通过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
相关产品推荐
相关产品推荐

