请问redis incr命令可以限制特定数量吗?
Will redis incr command can be limitation to specific number?
在我们的项目中,我们想使用redis的incr命令来限制存储。例如,对于特定商品 A,我们希望在促销期间只销售其中的 10 个。所以我们打算用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 命令的包装器,我们在 java 项目中使用 jedis 作为驱动程序来驱动我们的 redis。
然后我们启动了tomcat,我们可以直接从浏览器调用上面的界面。结果很好,没有出错,没有错误。
然后我们改用jmeter这个性能测试工具,它可以模拟用户调用接口的巨大压力。我们在 1 秒内创建了 1500 个调用接口的请求。但结果如下:
起初,代码运行正常,我们可以看到商品存储限制为 10 个。
过了一会儿,我们看到计数突破了限制,越来越大了!!!
不知道为什么,redis incr在用户请求大的情况下无法对存储做限制。任何人都可以给我一些灯吗?
基于redis的incr机制,执行完incr命令后会return新的值,如果是这样,为什么不能阻止数字的增长,很奇怪,真的。
对我来说,我想,可能同时有两个操作发送到redis做incr操作,但是一个直接做incr命令和returns,另一个做incr好慢而当它returns的时候,发现它增加的数量不是2,而是11,因为其他请求已经做了incr命令,好的存储已经增加到10了,也许吧?但是这个解释是不正确的,因为redis是单线程模型。
--------编辑----------------
我更改了代码以避免线程安全问题:
long result = redisUtil.incrRedisEx("axgggggggggg");
if (result <= 10) {
logger.error("==generate==order==result==" + result);
}
Struts2Utils.renderJson(result );
return;
刚发现日志会被写十几次
您可以使用一些 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"
然后你可以call/execute它:
127.0.0.1:6379> evalsha 6e6ad88c9a2b7dfdade9c5763467aaab2358d4e1 1 CappedInt get
在我们的项目中,我们想使用redis的incr命令来限制存储。例如,对于特定商品 A,我们希望在促销期间只销售其中的 10 个。所以我们打算用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 命令的包装器,我们在 java 项目中使用 jedis 作为驱动程序来驱动我们的 redis。
然后我们启动了tomcat,我们可以直接从浏览器调用上面的界面。结果很好,没有出错,没有错误。
然后我们改用jmeter这个性能测试工具,它可以模拟用户调用接口的巨大压力。我们在 1 秒内创建了 1500 个调用接口的请求。但结果如下:
起初,代码运行正常,我们可以看到商品存储限制为 10 个。
过了一会儿,我们看到计数突破了限制,越来越大了!!!
不知道为什么,redis incr在用户请求大的情况下无法对存储做限制。任何人都可以给我一些灯吗?
基于redis的incr机制,执行完incr命令后会return新的值,如果是这样,为什么不能阻止数字的增长,很奇怪,真的。
对我来说,我想,可能同时有两个操作发送到redis做incr操作,但是一个直接做incr命令和returns,另一个做incr好慢而当它returns的时候,发现它增加的数量不是2,而是11,因为其他请求已经做了incr命令,好的存储已经增加到10了,也许吧?但是这个解释是不正确的,因为redis是单线程模型。
--------编辑----------------
我更改了代码以避免线程安全问题:
long result = redisUtil.incrRedisEx("axgggggggggg");
if (result <= 10) {
logger.error("==generate==order==result==" + result);
}
Struts2Utils.renderJson(result );
return;
刚发现日志会被写十几次
您可以使用一些 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"
然后你可以call/execute它:
127.0.0.1:6379> evalsha 6e6ad88c9a2b7dfdade9c5763467aaab2358d4e1 1 CappedInt get