INCR
将键的整数值加一。如果键不存在,则使用 0 作为初始值。
语法
INCR key
引入版本:1.0.0
复杂度摘要:O(1)
将 key 中存储的数字加 1。如果该键不存在,则在执行操作之前将其设置为 0。如果键包含错误类型的值或包含无法表示为整数的字符串,则会返回错误。此操作仅限于 64 位有符号整数。
注意:这是一个字符串操作,因为 Redis 没有专用的整数类型。存储在键处的字符串被解释为以 10 为基数的 64 位有符号整数来执行操作。
Redis 以整数表示形式存储整数,因此对于实际保存整数的字符串值,存储整数的字符串表示形式不会产生任何开销。
必需参数
key
键名称。
示例
> SET mykey "10"
OK
> INCR mykey
(integer) 11
> GET mykey
"11"
详细说明
图案:计数器
计数器模式是 Redis 原子增量操作中最明显的事情。这个想法只是在每次操作发生时向 Redis 发送一个 INCR 命令。例如,在 Web 应用程序中,我们可能想知道该用户一年中每天的页面浏览量。
为此,Web 应用程序可以在用户每次执行页面查看时简单地增加一个键,创建连接用户 ID 和表示当前日期的字符串的键名称。
这个简单的模式可以通过多种方式进行扩展:
- 可以在每次页面浏览时一起使用
INCR和EXPIRE,以让计数器仅对间隔小于指定秒数的最新 N 个页面浏览进行计数。 - 客户端可以使用 GETSET 来自动获取当前计数器值并将其重置为零。
- 使用其他原子递增/递减命令(如
DECR或INCRBY),可以处理可能会根据用户执行的操作而变大或变小的值。例如,想象一下在线游戏中不同用户的得分。
模式:速率限制器
速率限制器模式是一种特殊的计数器,用于限制可以执行操作的速率。此模式的经典具体化涉及限制可以针对公共 API 执行的请求数量。
您可以通过两种方式使用 INCR 来实现此模式。这两个示例都将 API 调用限制为每个 IP 地址每秒最多 10 个请求。
模式:速率限制器1
该模式更简单直接的实现如下:
FUNCTION LIMIT_API_CALL(ip)
ts = CURRENT_UNIX_TIME()
keyname = ip+":"+ts
MULTI
INCR(keyname)
EXPIRE(keyname,10)
EXEC
current = RESPONSE_OF_INCR_WITHIN_MULTI
IF current > 10 THEN
ERROR "too many requests per second"
ELSE
PERFORM_API_CALL()
END
在此示例中,每个 IP、每个不同的秒都有一个计数器。但这些计数器始终会递增,并设置 10 秒的过期时间,以便当当前秒不同时,Redis 会自动删除它们。
请注意 MULTI 和 EXEC 的使用,以确保我们在每次 API 调用时都会递增并设置过期时间。
模式:速率限制器2
另一种实现使用单个计数器,但要在没有竞争条件的情况下正确实现它要复杂一些。我们将研究不同的变体。
FUNCTION LIMIT_API_CALL(ip):
current = GET(ip)
IF current != NULL AND current > 10 THEN
ERROR "too many requests per second"
ELSE
value = INCR(ip)
IF value == 1 THEN
EXPIRE(ip,1)
END
PERFORM_API_CALL()
END
计数器的创建方式是,从当前秒执行的第一个请求开始,它只能存活一秒。如果同一秒内有超过 10 个请求,计数器将达到大于 10 的值,否则它将过期并从 0 重新开始。
上面的代码中有一个竞争条件。如果由于某种原因客户端执行 INCR 命令但不执行 EXPIRE,则键将被泄露,直到我们再次看到相同的 IP 地址。
通过将带有可选 EXPIRE 的 INCR 转换为 Lua 脚本,然后使用 EVAL 命令(仅自 Redis 版本 2.6 起可用)发送该脚本,可以轻松解决此问题。
local current
current = redis.call("incr",KEYS[1])
if current == 1 then
redis.call("expire",KEYS[1],1)
end
有一种不使用脚本来解决此问题的不同方法,即使用 Redis 列表而不是计数器。该实现更加复杂并使用更高级的功能,但具有记住当前执行 API 调用的客户端的 IP 地址的优点,这可能有用也可能没用,具体取决于应用程序。
FUNCTION LIMIT_API_CALL(ip)
current = LLEN(ip)
IF current > 10 THEN
ERROR "too many requests per second"
ELSE
IF EXISTS(ip) == FALSE
MULTI
RPUSH(ip,ip)
EXPIRE(ip,1)
EXEC
ELSE
RPUSHX(ip,ip)
END
PERFORM_API_CALL()
END
RPUSHX 命令仅在键已存在时才推送元素。
请注意,我们这里有一个竞争,但这不是问题:EXISTS 可能返回 false,但键可能在我们在 MULTI / EXEC 块内创建之前由另一个客户端创建。然而,在极少数情况下,这种竞争只会错过 API 调用,因此速率限制仍然可以正常工作。
Redis Software 与 Redis Cloud 兼容性
| Redis 软件 |
Redis Cloud |
说明 |
|---|---|---|
| ✅ 标准 |
✅ 标准 |
返回值
RESP2
整数回复:递增后key的值。
RESP3
整数回复:递增后key的值。
资料来源:Redis 官方 INCR 文档;中文内容参考 Redis 文档许可。