Dav*_*Hsu 8 increment race-condition redis
基于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?
mis*_*ion 14
想象一下,在INCR命令已经执行但之前EXPIRE执行之前,您将连接到redis服务器.在这种情况下,您永远不会执行EXPIRE因为下一次代码调用会使您的值> 1.在redis文档中使用的术语竞争条件.但这不是一个成功的术语.更正确的术语是不完美的算法.所以这个案例不是关于两个或更多客户之间的竞争条件,而是关于现实世界中的特殊情况.例如,服务器连接丢失.
小智 9
仍然可以以原子方式实现您想要的:您可以使用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(第一个增量),如果是,我们调用该EXPIRE键的命令然后返回值v.ARGV [...]是传递给脚本的参数,ARGV [1]是密钥名称,ARGV [2]是给定密钥的超时秒数.
使用此脚本的示例:
> eval"local v = redis.call('INCR',ARGV [1])如果v == 1则redis.call('EXPIRE',ARGV [1],ARGV [2])结束返回v"0 my_key 10
(整数)1
> eval"local v = redis.call('INCR',ARGV [1])如果v == 1则redis.call('EXPIRE',ARGV [1],ARGV [2])结束返回v"0 my_key 10
(整数)2
>得到my_key
"2"
[等待10秒]
>得到my_key
(零)
| 归档时间: |
|
| 查看次数: |
8739 次 |
| 最近记录: |