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() {
            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() {
            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 cacheNames) {
        super(redisOperations, cacheNames);
    }

    public CustomRedisCacheManager(RedisOperations redisOperations, Collection 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以上

   
          org.springframework.data
          spring-data-redis
           1.8.22
  

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

你可能感兴趣的:(spring,springcloud)