Spring-data-redis cacheable并发导致的null问题

Spring-data-redis cacheable并发导致的null,版本低于1.8.11会导致该问题

1.8.11之前的版本通过@cacheable缓存获取内容,代码层面是先判断缓存key值是否存在,存在在进行get缓存值,这就会导致非原子性操作。

问题场景:(高并发情况下,多线程操作同一个key)
步骤:
1.线程1获取缓存值,刚判断key值存在
2.线程2在此期间删除了缓存中的该key值
3.线程1继续执行,这时候获取缓存中的key值为null

1.8.11版本一下源代码如下:
RedisCache类中的get方法:

    public RedisCacheElement get(final RedisCacheKey cacheKey) {
        Assert.notNull(cacheKey, "CacheKey must not be null!");
        Boolean exists = (Boolean)this.redisOperations.execute(new RedisCallback<Boolean>() {
            public Boolean doInRedis(RedisConnection connection) throws DataAccessException {
                return connection.exists(cacheKey.getKeyBytes());
            }
        });
        return !exists ? null : new RedisCacheElement(cacheKey, this.fromStoreValue(this.lookup(cacheKey)));
    }

解决方案一:
自定义RedisCache,覆写get方法

public class CustomRedisCache extends RedisCache {

        @Override
        public RedisCacheElement get(RedisCacheKey cacheKey) {
            Assert.notNull(cacheKey, "CacheKey must not be null!");
            //获取值代码提前
        RedisCacheElement redisCacheElement=new RedisCacheElement(cacheKey, this.fromStoreValue(this.lookup(cacheKey)))
        Boolean exists = (Boolean)this.redisOperations.execute(new RedisCallback<Boolean>() {
            public Boolean doInRedis(RedisConnection connection) throws DataAccessException {
                return connection.exists(cacheKey.getKeyBytes());
            }
        });
        if(!exists){
          return null;
        }
        return redisCacheElement;
        }
  }      

自定义 RedisCacheManager 覆写createCache方法

public class CustomRedisCacheManager extends RedisCacheManager {

    private boolean cacheNullValues;

    public CustomRedisCacheManager(RedisOperations redisOperations) {
        super(redisOperations);
    }

    public CustomRedisCacheManager(RedisOperations redisOperations, Collection<String> cacheNames) {
        super(redisOperations, cacheNames);
    }

    public CustomRedisCacheManager(RedisOperations redisOperations, Collection<String> cacheNames, boolean cacheNullValues) {
        super(redisOperations, cacheNames, cacheNullValues);
        this.cacheNullValues = cacheNullValues;
    }

    @Override
    protected RedisCache createCache(String cacheName) {
        long expiration = computeExpiration(cacheName);
        return new CustomRedisCache(cacheName, (isUsePrefix() ? getCachePrefix().prefix(cacheName) : null), getRedisOperations(), expiration,
                cacheNullValues);
    }
 }

解决方案二:
讲spring-data-redis升级到1.8.11以上

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

1.8.22版本源码如下:

   public RedisCacheElement get(final RedisCacheKey cacheKey) {
        Assert.notNull(cacheKey, "CacheKey must not be null!");
        Boolean exists = (Boolean)this.redisOperations.execute(new RedisCallback<Boolean>() {
            public Boolean doInRedis(RedisConnection connection) throws DataAccessException {
                return connection.exists(cacheKey.getKeyBytes());
            }
        });
        if (!exists) {
            return null;
        } else {
            byte[] bytes = this.doLookup(cacheKey);
            return bytes == null ? null : new RedisCacheElement(cacheKey, this.fromStoreValue(this.deserialize(bytes)));
        }
    }
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值