redis incr命令是否可以限制特定的数字?

时间:2017-12-20 04:41:38

标签: redis pressure

在我们的项目中,我们希望使用redis的incr命令来限制存储。例如,对于特定商品A,我们希望在促销期间仅销售其中的十个。因此,我们计划使用incr命令将存储值从0添加到10.理论上,此方案对此方案没有问题。

在开始之前,我们对incr命令进行了性能测试,结果表明使用incr对货物存储进行限制可能是不可行的。

以下测试代码:

static int count = 0;

public void performanceToken() throws RedisAccessException {

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

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

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

incrRedisEx是incr命令的包装器,我们使用jedis作为驱动程序来驱动我们的java项目中的redis。

然后我们启动了tomcat,我们可以直接从浏览器调用上面的界面。结果很好,没有错误,没有错误。

然后我们切换到性能测试工具jmeter,他可以模拟用户调用界面的巨大压力。我们在1秒内创建了1500个调用接口的请求。但结果如下:

首先,代码工作正常,我们可以看到货物存储限制为10。 enter image description here enter image description here

过了一会儿,我们可以看到计数突破了限制,它越来越大! enter image description here

不知道为什么,redis incr可以在很大的用户请求下限制存储。有人可以给我一些灯吗?

基于redis incr机制,它会在执行incr命令后返回新值,如果是这样,为什么它不能阻止数字的增长,这真的很奇怪。

对我来说,我想,也许在同一时间,有两个操作发送到redis进行incr操作,但是一个执行incr命令并直接返回,另一个执行incr如此慢,当它返回时,它是发现它增加的数量不是2,而是11,因为其他请求已经执行了incr命令,并且好的存储空间已增加到10,可能?但这种解释是不正确的,因为redis是单线程模型。

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

我更改了代码以避免线程安全问题:

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

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

    Struts2Utils.renderJson(result );
    return;

刚发现日志写入次数超过十次。

1 个答案:

答案 0 :(得分: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
相关问题