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 文档许可。