INCR

语法
INCR key
可用版本
Redis开源版 1.0.0
时间复杂度
O(1)
ACL分类
@write, @string, @fast,

将存储在 key 中的数字增加一。如果该键不存在,则在执行操作前将其设置为 0。如果键包含错误类型的值或包含无法表示为整数的字符串,则会返回错误。此操作仅限于64位带符号整数。

注意:这是一个字符串操作,因为Redis没有专用的整数类型。存储在键中的字符串被解释为基于10的64位带符号整数来执行此操作。

Redis以其整数表示形式存储整数,因此对于实际存储整数的字符串值,存储整数的字符串表示形式没有额外开销。

示例

在交互式控制台中尝试此命令

SET mykey "10" INCR mykey GET mykey

模式:计数器

计数器模式是使用Redis原子增量操作最显而易见的应用。基本思路是每次操作发生时,简单地向Redis发送一个 INCR 命令。例如,在Web应用程序中,我们可能想知道某个用户一年中每天的页面浏览量。

为此,Web应用程序可以在用户每次执行页面浏览时简单地增加一个键的值,键名可以通过连接用户ID和表示当前日期的字符串来创建。

这种简单的模式可以通过多种方式扩展

  • 可以在每次页面浏览时同时使用 INCREXPIRE,以获得一个仅计算间隔小于指定秒数的最新N次页面浏览的计数器。
  • 客户端可以使用 GETSET 原子地获取当前计数器值并将其重置为零。
  • 使用其他原子增减命令,如 DECRINCRBY,可以处理根据用户执行的操作可能变大或变小的值。例如,想象一下在线游戏中不同用户的分数。

模式:限速器

限速器模式是一种特殊的计数器,用于限制操作执行的速率。该模式的经典实现是限制对公共API的请求次数。

我们提供了使用 INCR 实现此模式的两种方式,其中假设要解决的问题是将每个IP地址每秒的API调用次数限制为最多十次请求

模式:限速器 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会自动移除它们。

注意使用 MULTIEXEC 来确保在每次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地址。

通过将带有可选 EXPIREINCR 转换为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

The RPUSHX 命令仅在键已存在时才推送元素。

请注意,这里存在竞态,但这并不是问题:EXISTS 可能会返回 false,但在我们在 MULTI / EXEC 块内创建该键之前,另一个客户端可能已经创建了它。然而,这种竞态只会在极少数情况下导致丢失一次API调用,因此限速功能仍然会正常工作。

RESP2/RESP3 回复

整数回复:递增后键的值。
评价本页
回到顶部 ↑