0

In our project, we want to use redis's incr command to do limitation to the storage. For example, for the specific goods A, We want to sell only ten of them during the promotion. So we planned to use incr command to add the storage value from 0 to 10. In theory, this plan is no problem with this scenario.

Before start, we did a performance test on the incr command, the result appeared that using incr to do limitation to the goods storage may be unrealiable.

The testing code below:</p>

static int count = 0;

public void performanceToken() throws RedisAccessException {

    long result = redisUtil.incrRedisEx("myrrrxxxrrrediskey6");

    if (result <= 10) {
        count ++;
    }

    Struts2Utils.renderJson(result +"|"+count);
    return;
}

The incrRedisEx was the wrapper of incr command and we used jedis as the driver to drive our redis in our java project.

Then We started our tomcat and we could invoke above interface directly from our browser. The result was good, no mistake, no error.

And then we switched to jmeter, the performance testing tool, who could simulate great pressure from users to invoke the interface. we created 1500 requests to invoke the interface in 1 seconds. But the result is below:

At the first, the code worked ok and we can see that the goods storage was limited to 10. enter image description here enter image description here

After a while, we can see that the count number broke the limitation, it growssss!!! enter image description here

Don't know why, redis incr can't do limitation to storage under great user requests. Anyone can give me some lights on it?

Based on redis incr mechanism, it will return new value after doing incr command , if so, why it can't stop the growth of the number, it's a strange thing, really.

For me, I think, maybe at the same time, there are two operations send to redis to do incr operation, but one does the incr command and returns directly, another one does incr so slow and when it returns, it is found that the number it increases is not 2, but 11, because other requests do the incr command already and the good storage has been increased to 10, maybe? But this explaination is not correct because redis is single thread model.

--------EDIT----------------

I changed my code to avoid thread safe problem:

 long result = redisUtil.incrRedisEx("axgggggggggg");

    if (result <= 10) {
        logger.error("==generate==order==result==" + result);
    }

    Struts2Utils.renderJson(result );
    return;

Just found that the log will be write more than ten times.

4

1 回答 1

3

您可以使用一个小 Lua 脚本在 Redis 本身中进行增量,使其本质上是单线程的:

127.0.0.1:6379> set CappedInt 7
OK
127.0.0.1:6379> eval "local c=redis.call(ARGV[1],KEYS[1])+0;if c<10 then return redis.call('INCR',KEYS[1]); else return 10; end" 1 CappedInt get
(integer) 8
127.0.0.1:6379> eval "local c=redis.call(ARGV[1],KEYS[1])+0;if c<10 then return redis.call('INCR',KEYS[1]); else return 10; end" 1 CappedInt get
(integer) 9
127.0.0.1:6379> eval "local c=redis.call(ARGV[1],KEYS[1])+0;if c<10 then return redis.call('INCR',KEYS[1]); else return 10; end" 1 CappedInt get
(integer) 10
127.0.0.1:6379> eval "local c=redis.call(ARGV[1],KEYS[1])+0;if c<10 then return redis.call('INCR',KEYS[1]); else return 10; end" 1 CappedInt get
(integer) 10

除了输入脚本,您还可以将 Lua 代码放入一个文件,IncWithCap.lua如下所示:

local cap=10
if(redis.call(ARGV[1],KEYS[1])+0 < cap) then
   return redis.call('INCR',KEYS[1])
end
return cap

然后你可以将它加载到 Redis 中:

redis-cli SCRIPT LOAD "$(cat IncWithCap.lua)"

样本输出

"6e6ad88c9a2b7dfdade9c5763467aaab2358d4e1"

然后你可以调用/执行它:

127.0.0.1:6379> evalsha 6e6ad88c9a2b7dfdade9c5763467aaab2358d4e1 1 CappedInt get
于 2017-12-20T10:45:06.700 回答