spring-data-redis - redis并发计数
phpcn_u1582
phpcn_u1582 2017-04-25 09:03:18
0
2
1133

说明:
用redis计数器存取用户的操作次数,最多3次,但是并发会超过3次,下面的代码对吗?

BoundValueOperations<String, String> operations = redisTemplate.boundValueOps("key1");
        String key1 = operations.get();
        if (StringUtils.isEmpty(key1)) {
            service.do();//这里是业务逻辑操作成功之后,计数器加1
            operations.increment(1);
        } else {
            if (Integer.parseInt(key1) < 2) {
                service.do();//这里是业务逻辑操作成功之后,计数器加1
                operations.increment(1);
            }
        }
phpcn_u1582
phpcn_u1582

reply all(2)
世界只因有你

For this kind of counting business, you should count first and then do business. If the business is completed and the counter is greater than the limit value, you will be stunned.

My plan is:
Due to data inconsistency caused by concurrency, you can consider using Redis's INCR command to increment by 1 and get the latest value (this is a one-time operation, so it will not cause inconsistency):

  • If it is greater than 3, it means it has been exceeded and it is over;

  • is less than or equal to 3, then execute the business (you may also consider the rollback counter for business execution failure).

左手右手慢动作

There are certain flaws in implementing the above functions through the Java client. In the case of high concurrency, the data may be inconsistent. It is recommended to use Lua script to encapsulate the entire logic to ensure the atomicity of the operation. The script can be cached through SCRIPT LOAD. Go to the server and execute it through sha1 check value + parameters (Key, ARG) to reduce network transmission;

Give you an example:
1. Functional requirements: If the key does not exist, set it and return false, and set the timeout and count = 1; when the timeout is reached or the specified count is reached, return false and reset the timeout and count. count=1; if the timeout is not reached & the specified count is not reached, return true and incr count

redis.lua script is as follows:

local key = KEYS[1];
local expire = tonumber(KEYS[2]);
local number = tonumber(KEYS[3]);
local count = tonumber(redis.call("GET",key));
if count == nil then
        redis.call("SETEX",KEYS[1],expire,"1");
        return false;
else
        if count +1 >= number then;
                redis.call("SETEX",KEYS[1],expire,"1");
                return false;
        else
                redis.call("INCR",KEYS[1]);
                return true;
        end
end

The java code is as follows:

   public static final String REDIS_LUA = "redis.call('select',1);local key = KEYS[1];local expire = tonumber(KEYS[2]);" +
            "local number = tonumber(KEYS[3]);local count = tonumber(redis.call('GET',key));" +
            "if count == nil then redis.call('SETEX',KEYS[1],expire,'1');return 0;" +
            "else if count +1 >= number then redis.call('SETEX',KEYS[1],expire,'1');return 0;" +
            "else redis.call('INCR',KEYS[1]);return 1;end;end;";
    
    private static final String redisScript;
    

    static {
        try {
            Conf.load();
        } catch (Exception e) {
            e.printStackTrace();
        }
        jedisPool = new JedisPool(Conf.getRedisHost(), Conf.getRedisPort());
        redisScript = loadLuaScript();
    }
   private static String loadLuaScript() {
        Jedis jedis = null;
        String redisScript = null;
        try {
            jedis = jedisPool.getResource();
            redisScript = jedis.scriptLoad(REDIS_LUA);
        } catch (Exception e) {
            if (jedis != null) {
                jedisPool.returnResource(jedis);
            }
        } finally {
            if (jedis != null) {
                jedisPool.returnResource(jedis);
            }
        }
        return redisScript;
    }
    
    
  
调用脚本
 Object result = jedis.evalsha(redisScript, ARGS_LENGTH
                    , key, expireTime, countVal);
Latest Downloads
More>
Web Effects
Website Source Code
Website Materials
Front End Template