springboot(19)Redis缓存@Cacheable对存在的数据返回null

问题描述

Spring Cache + redis做缓存,使用的是1.8.10.RELEASE版本的spring-data-redis。在高并发下数据库存在数据但是@Cacheable 注解返回的内容是null。查看了一下源代码,在使用注解获取缓存的时候,RedisCache的get方法会先去判断key是否存在,然后再去获取值。这了就有一个漏铜,当线程1判断了key是存在的,紧接着这个时候这个key过期了,这时线程1再去获取值的时候返回的是null。

org.springframework.data.redis.cache.RedisCache.get(RedisCacheKey)方法源码:

public RedisCacheElement get(final RedisCacheKey cacheKey) {

    Assert.notNull(cacheKey, "CacheKey must not be null!");

    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;
    }

    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);
}

解决方案

更换版本:spring-data-redis在1.8.11的版本中修复了该问题

<dependency>
    <groupId>org.springframework.data</groupId>
    <artifactId>spring-data-redis</artifactId>
    <version>1.8.11.RELEASE</version>
</dependency>

1.8.11的源代码:

public RedisCacheElement get(final RedisCacheKey cacheKey) {

    Assert.notNull(cacheKey, "CacheKey must not be null!");

    Boolean exists = (Boolean) redisOperations.execute(new RedisCallback<Boolean>() {

        @Override
        public Boolean doInRedis(RedisConnection connection) throws DataAccessException {
            return connection.exists(cacheKey.getKeyBytes());
        }
    });

    if (!exists) {
        return null;
    }

    byte[] bytes = doLookup(cacheKey);

    // safeguard if key gets deleted between EXISTS and GET calls.
    if (bytes == null) {
        return null;
    }

    return new RedisCacheElement(cacheKey, fromStoreValue(deserialize(bytes)));
}

private byte[] doLookup(Object key) {

    RedisCacheKey cacheKey = key instanceof RedisCacheKey ? (RedisCacheKey) key : getRedisCacheKey(key);

    return (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());
        }
    });
}

配置Redis管理器

@Configuration
public class RedisConfig {

    /**
     * ehcache默认过期时间
     * @param redisTemplate
     * @return
     */
    @Bean
    public CacheManager cacheManager(RedisTemplate<String,?> redisTemplate) {
        RedisCacheManager cacheManager= new RedisCacheManager(redisTemplate);
        // 开启使用缓存名称最为key前缀
        cacheManager.setUsePrefix(true);
        cacheManager.setDefaultExpiration(60);
        return cacheManager;
    }

}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值