技巧分享 21周 redis缓存 @Cacheable 高并发为null

1,530 阅读1分钟

Spring boot 整合redis时,使用@Cacheable实现redis实现缓存时,如果出现单线程时无报错。但是高并发的状态之下却出现报null情况。 遇到该问题时,可以升级spring-data-redis的版本即可 会BUG在1.8.11以上的版本得到修复。

具体原因如下(参考别人的文章): 在使用注解获取缓存的时候,RedisCache的get方法会先去判断key是否存在,然后再去获取值。这了就有一个漏铜,当线程1判断了key是存在的,紧接着这个时候这个key过期了,这时线程1再去获取值的时候返回的是null。

RedisCache的get方法源码:

public RedisCacheElement get(final RedisCacheKey cacheKey) {
 
    Assert.notNull(cacheKey, "CacheKey must not be null!");
 
    // 判断Key是否存在
    Boolean exists = (Boolean) redisOperations.execute(new RedisCallback<Boolean>() {
 
        @Override
        public Boolean doInRedis(RedisConnection connection) throws DataAccessException {
            return connection.exists(cacheKey.getKeyBytes());
        }
    });
 
    if (!exists.booleanValue()) {
        return null;
    }
    
    // 获取key对应的值
    return new RedisCacheElement(cacheKey, fromStoreValue(lookup(cacheKey)));
}
 
// 获取值
protected Object lookup(Object key) {
 
    RedisCacheKey cacheKey = key instanceof RedisCacheKey ? (RedisCacheKey) key : getRedisCacheKey(key);
 
    byte[] bytes = (byte[]) redisOperations.execute(new AbstractRedisCacheCallback<byte[]>(
            new BinaryRedisCacheElement(new RedisCacheElement(cacheKey, null), cacheValueAccessor), cacheMetadata) {
 
        @Override
        public byte[] doInRedis(BinaryRedisCacheElement element, RedisConnection connection) throws DataAccessException {
            return connection.get(element.getKeyBytes());
        }
    });
 
    return bytes == null ? null : cacheValueAccessor.deserializeIfNecessary(bytes);
}