在我们的项目中,我们希望使用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个调用接口的请求。但结果如下:
不知道为什么,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;
刚发现日志写入次数超过十次。
答案 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