基于redis文档:http://redis.io/commands/incr
在段落模式:速率限制器2中 较短的版本代码:
value = INCR(ip) IF value == 1 THEN EXPIRE(ip, 1)
据称有一种竞争条件让EXPIRE永远不会执行。这意味着ip的值可以某种方式从0反弹到2。
然而在我的想法中,由于Redis是单线程而INCR是一个原始命令,它本身不应该是原子的?即使两个客户几乎同时进行INCR,它们如何检索0或两者都检索2?
答案 0 :(得分:13)
想象一下,在INCR
命令已执行但执行EXPIRE
之前,您断开了与redis服务器的连接。在这种情况下,您永远不会执行EXPIRE
因为下一次代码调用会给出您的价值> 1.在redis文档中使用了术语竞争条件。但这不是一个成功的术语。更正确的术语是不完美的算法。所以这个案例不是关于两个或更多客户之间的竞争条件,而是关于现实世界中的特殊情况。例如,服务器连接丢失。
答案 1 :(得分:8)
仍然可以以原子方式实现您的目标:您可以使用EVAL命令。
EVAL
用于执行在Redis服务器内部用Lua编写的脚本,其中最好的部分是执行此脚本,就像执行单个原子操作一样。
以下脚本可用于此目的:
local v = redis.call('INCR',ARGV [1])如果v == 1则redis.call('EXPIRE',ARGV [1],ARGV [2])结束返回v
逻辑非常简单:我们将INCR
命令的返回值存储到标记为v的变量中,然后检查v值是否为1(第一个增量),如果是,我们调用命令{ {1}}为该键,然后我们返回v的值.ARGV [...]是传递给脚本的参数,ARGV [1]是键名,ARGV [2]是以秒为单位的超时时间。给定的密钥。
使用此脚本的示例:
&GT; eval“local v = redis.call('INCR',ARGV [1])如果v == 1则redis.call('EXPIRE',ARGV [1],ARGV [2])结束返回v”0 my_key 10 < / p>
(整数)1
&GT; eval“local v = redis.call('INCR',ARGV [1])如果v == 1则redis.call('EXPIRE',ARGV [1],ARGV [2])结束返回v”0 my_key 10 < / p>
(整数)2
&GT;得到my_key
“2”
[等待10秒]
&GT;得到my_key
(无)
答案 2 :(得分:1)
我遇到了同样的问题,那是怎么回事: value = INCR(ip) IF [value == 1 || PTTL(ip)== -1]然后 EXPIRE(ip,1)
如果2个客户端几乎同时执行PTTL,则获取-1,并设置几乎同时到期