Spring Redis Cache @Cacheable large concurrent return Null__redis

Source: Internet
Author: User
Tags assert
Problem Description

Recently we used spring cache + Redis to do caching. Under high concurrency the @cacheable annotation returns a null content. Look at the source code, in the use of annotations to get the cache, the Rediscache get method will first determine whether the key exists, and then to get the value. There's a copper leak, and when thread 1 judges that the key is there, then the key expires, and then the thread 1 returns NULL when it gets the value.

Rediscache's Get method source code:

Public rediscacheelement get (final Rediscachekey CacheKey) {assert.notnull (CacheKey, "CacheKey must is not being null!"); Determines whether a key exists Boolean exists = (Boolean) Redisoperations.execute (new rediscallback<boolean> () {@O Verride public Boolean Doinredis (redisconnection connection) throws DataAccessException {return Conne
        Ction.exists (Cachekey.getkeybytes ());

    }
    });
    if (!exists.booleanvalue ()) {return null;
//Get key corresponding value return new Rediscacheelement (CacheKey, Fromstorevalue (lookup (CacheKey))); }//Get Value Protected object lookup (object key) {Rediscachekey CacheKey = key instanceof Rediscachekey?

    (Rediscachekey) Key:getrediscachekey (key); byte[] bytes = (byte[]) Redisoperations.execute (new abstractrediscachecallback<byte[]>  Acheelement (New rediscacheelement (CacheKey, null), cachevalueaccessor), CacheMetadata) {@Override public Byte[] DoinreDis (binaryrediscacheelement element, redisconnection connection) throws DataAccessException {return connectio
        N.get (Element.getkeybytes ());

    }
    }); return bytes = null?
Null:cacheValueAccessor.deserializeIfNecessary (bytes);
 }
Solution

The problem with this process is that the solution is to turn the process upside down, get the value first, and then determine if the key exists. You cannot use the obtained value directly to determine whether there is a value based on whether it is null, because reids may cache null values.

To override the Rediscache get method:

Public rediscacheelement get (final Rediscachekey cachekey) {

    assert.notnull (CacheKey, "CacheKey must is not being null!");

    rediscacheelement rediscacheelement = new Rediscacheelement (CacheKey, Fromstorevalue (lookup (CacheKey)));
    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 rediscacheelement;
}
Full implementation: to override the Rediscache get method
Package Com.xiaolyuh.redis.cache;
Import org.springframework.dao.DataAccessException;
Import Org.springframework.data.redis.cache.RedisCache;
Import org.springframework.data.redis.cache.RedisCacheElement;
Import Org.springframework.data.redis.cache.RedisCacheKey;
Import org.springframework.data.redis.connection.RedisConnection;
Import Org.springframework.data.redis.core.RedisCallback;
Import org.springframework.data.redis.core.RedisOperations;

Import Org.springframework.util.Assert; /** * Custom Redis Cache * * @author Yuhao.wang/public class Customizedrediscache extends Rediscache {private final

    Redisoperations redisoperations;

    Private final byte[] prefix; Public Customizedrediscache (String name, byte[] prefix, redisoperations<? extends Object,? extends Object> Redisop
        Erations, long expiration) {super (name, prefix, redisoperations, expiration);
        This.redisoperations = redisoperations;
    This.prefix = prefix; } public CustomizedreDiscache (String name, byte[] prefix, redisoperations<? extends Object,? extends Object> redisoperations, long Expir
        ation, Boolean allownullvalues) {super (name, prefix, redisoperations, expiration, allownullvalues);
        This.redisoperations = redisoperations;
    This.prefix = prefix;
     /** * Overrides the Get function of the parent class. The Get method of the parent class is to use exists to determine whether a key exists, does not exist, returns NULL, and then takes the value in the Redis cache.
     This can cause concurrency problems, * If a request calls the EXISTS function to determine that the key exists, the cache expires in the next moment, or is deleted.
     * This time to go to the cache to get the value of the return is null.
     * You can get the cached value first and then determine if the key exists. * * @param cachekey * @return/@Override public rediscacheelement get (final Rediscachekey Cacheke

        Y) {assert.notnull (CacheKey, "CacheKey must not to be null!");
        Rediscacheelement rediscacheelement = new Rediscacheelement (CacheKey, Fromstorevalue (lookup (CacheKey)));
            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 rediscacheelement; /** * Get Rediscachekey * * @param key * @return/private Rediscachekey Getrediscache Key (Object key) {return new Rediscachekey (key). Useprefix (This.prefix). Withkeyserializer (Redisope
    Rations.getkeyserializer ());

 }
}
Override Rediscachemanager
Package Com.xiaolyuh.redis.cache;
Import Org.slf4j.Logger;
Import Org.slf4j.LoggerFactory;
Import Org.springframework.cache.Cache;
Import Org.springframework.data.redis.cache.RedisCacheManager;
Import org.springframework.data.redis.core.RedisOperations;

Import Org.springframework.data.redis.core.StringRedisTemplate;

Import java.util.Collection;

    /** * Custom Redis Cache Manager * @author Yuhao.wang/public class Customizedrediscachemanager extends Rediscachemanager {

    private static final Logger Logger = Loggerfactory.getlogger (Customizedrediscachemanager.class);
    Public Customizedrediscachemanager (Redisoperations redisoperations) {super (redisoperations);
        Public Customizedrediscachemanager (Redisoperations redisoperations, collection<string> cacheNames) {
    Super (Redisoperations, cachenames);
        @Override protected Cache Getmissingcache (String name) {Long expiration = computeexpiration (name); return new CustomizedrediscachE (Name, (This.isuseprefix () this.getcacheprefix (). prefix (name): null),
    This.getredisoperations (), expiration);

 }
}
Configuring the Redis manager
@Configuration
the public class Redisconfig {

    //Redis cache has a valid time unit of seconds
    @Value ("${redis.default.expiration:3600} ")
    private long redisdefaultexpiration;

    @Bean public
    Rediscachemanager CacheManager (redistemplate<object, object> redistemplate) {
        Rediscachemanager Rediscachemanager = new Customizedrediscachemanager (redistemplate);
        Rediscachemanager.setuseprefix (true);
        Here you can set a default expiration time unit is seconds
        rediscachemanager.setdefaultexpiration (redisdefaultexpiration);

        return rediscachemanager;
    }

    /**
     * Display Declaration cache key Generator
     *
     * @return
    /@Bean public
    keygenerator keygenerator () {

        return new Simplekeygenerator ();
    }


Source:
Https://github.com/wyh-spring-ecosystem-student/spring-boot-student/tree/releases

Spring-boot-student-cache-redis Engineering

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.