Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/redis/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
redis钥匙被TTL卡在-1_Redis - Fatal编程技术网

redis钥匙被TTL卡在-1

redis钥匙被TTL卡在-1,redis,Redis,我使用redis管理API上的费率限制,并使用SETEX每小时自动重置费率限制 我发现redis无法清除某些键并在-1上报告它们的TTL。以下是使用占位符IP地址的redis cli会话示例: > GET allowance:127.0.0.1 > 0 > TTL allowance:127.0.0.1 -1 > GET allowance:127.0.0.1 0 请注意,尽管其TTL为负值,但redis在IGETit时不会清除该键 我试图复制这种状态,但无法复制 &g

我使用redis管理API上的费率限制,并使用
SETEX
每小时自动重置费率限制

我发现redis无法清除某些键并在
-1
上报告它们的
TTL
。以下是使用占位符IP地址的redis cli会话示例:

> GET allowance:127.0.0.1
> 0
> TTL allowance:127.0.0.1
-1
> GET allowance:127.0.0.1
0
请注意,尽管其TTL为负值,但redis在I
GET
it时不会清除该键

我试图复制这种状态,但无法复制

> SETEX doomedkey -1 hello
(error) ERR invalid expire time in SETEX
> SETEX doomedkey 0 hello
(error) ERR invalid expire time in SETEX
> SETEX doomedkey 5 hello
OK
> TTL doomedkey
4
> GET doomedkey
hello

(... wait 5 seconds)

> TTL doomedkey
-2
> GET doomedkey
(nil)
这是某种不幸的竞争条件导致redis无法使这些密钥过期吗?在数万个已成功过期的产品中,只有大约10个仍处于
-1
状态


我使用的是redis\u版本:2.8.9

我遇到了同样的问题,只使用了redis 2.8.24,还使用它进行API速率限制

我怀疑您正在这样做速率限制(仅以Ruby代码为例):

def消耗率限制
#获取给定帐户或用户的当前限制
rate\u limit=Redis.get('available\u limit:account\u id')
#如果尚未初始化或TTL已过期,则可以为零
如果费率限制==零
#让我们把它初始化为初始极限
#让我们使用10000个请求的窗口,每小时重置一次
费率限制=10000
setex('available\u limit:account\u id',3600,rate\u limit-1)
其他的
#如果密钥已经存在,只需减小限制
Redis.decr('可用限额:帐户id')
结束
#如果我们确定或为false,则返回true已达到限制
返回(速率限制>0)
结束
嗯,我使用这种方法,发现“get”和“decr”调用之间存在共电流问题,这导致了您描述的确切问题

当rate limit键的TTL在“get”调用之后,但在“decr”调用之前过期时,就会发生此问题。将会发生什么:

首先,“get”调用将返回当前限制。假设它返回了500。 然后在几毫秒内,该密钥的TTL过期,因此它在Redis中不再存在。 因此,代码继续运行,并达到“decr”调用。这里也出现了bug:

美国(我强调):

将键上存储的数字减一如果该键不可用 如果存在,则在执行操作之前将其设置为0。(……)

由于密钥已被删除(因为它已过期),“decr”指令会将密钥初始化为零,然后将其递减,这就是密钥值为-1的原因。并且密钥将在没有TTL的情况下创建,因此发出
TTL key\u name
也将发出-1

解决方案可能是使用MULTI和EXEC命令将所有代码包装在一个文件中。但是,这可能会很慢,因为它需要多次往返到Redis服务器

我使用的解决方案是编写一个Lua脚本并使用EVAL命令运行它。它的优点是原子性(这意味着没有并发问题),并且只有一个到Redis服务器的RTT

localexpire\u time=ARGV[1]
本地初始利率限制=ARGV[2]
本地速率\u限制=redis.call('get',键[1])
--当密钥不存在时,rate_limit将为false。
--这是因为redis在Lua脚本中将Nil转换为false。
如果速率限制==假,则
费率限制=初始费率限制
redis.call('setex',键[1],初始速率限制,速率限制-1)
其他的
redis.call('decr',键[1])
结束
回报率上限
要使用它,我们可以将
consumer\u rate\u limit
函数重写为:

def消耗率限制

script=
-1
表示没有与密钥相关的过期。我想有人在键上调用了
set key-value
,过期时间被重置了。@for_stack,这将是我的答案。谢谢,我会调查为什么会发生这种情况。