redis之string----INCR

介绍Redis中INCR命令的基本用法及其在计数器模式中的应用,并详细探讨了如何利用Redis实现限速器模式,包括使用INCR、EXPIRE、Lua脚本等多种方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

INCR key

key中存储的数字值增一。

如果key不存在,那么key的值会先被初始化为0,然后再执行INCR操作。

      如果值包含错误的类型,或字符串类型的值不能表示为数字,那么返回一个错误。

本操作的值限制为64(bit)有符号数字表示之内。

这是一个针对字符串的操作,因为redis没有专用的整数类型,所以key内存储的字符串被解释为十进制64位有符号整数来执行INCR操作。

返回值:

执行INCR命令之后key的值。

命令:

127.0.0.1:6379> set page_view 20
OK
127.0.0.1:6379> incr page_view
(integer) 21
127.0.0.1:6379> get page_view # 数字值在 Redis 中以字符串的形式保存
"21"
127.0.0.1:6379> get page_VIEW	#注意区分大小写
(nil)
模式:计数器

计数器是redis的原子性自增操作可实现的最直观的模式了,它的想法相当简单:每当某个操作发生时,向redis发送一个INCR命令。

比如咋一个web应用程序中,如果想知道用户在一年中每天点击量,那么只要将用户ID以及相关的日期信息作为键,并在每次用户点击页面时,执行一个自增操作即可。

比如用户名是Peter,点击时间时2012322日,那么执行命令INCR Peter::2012.3.22

可以用以下几种方式扩展这个简单的模式:

①可以通过组合使用INCREXPIRE,来达到只在规定的生存时间内进行计数(counting)的目的。

②客户端可以使用GETSET命令原子性地获取计数器当前值并将计数器清零。

③使用其他自增/自减操作,比如DECRINCRBY,用户可以通过执行不同的操作增加或减少计数器的值,比如在游戏中的计分器就可能用到这些命令。

模式:限速器

限速器是特殊的计算器,它用于限制一个操作可以被执行的速率(rate)

限速器的典型用法是限制公开API的请求次数,以下是一个限速器实现实例,它将API的最大请求次数限制在每个IP地址每秒钟十个之内:

命令:

FUNCTION LIMIT_API_CALL(ip)
ts = CURRENT_UNIX_TIME()
keyname = ip+”:”+ts
current = GET(keyname)
IF current != NULL  AND  current >10  THEN
ERROR “too many requests per second”
END
IF current == NULL  THEN
MULTI
INCR(keyname,1)
EXPIRE(keyname,1)
EXEC
ELSE
INCR(keyname,1)
END
PERFORM_API_CALL()
这个实现每秒钟为每个IP地址使用一个不同的计数器,并用EXPIRE命令设置生存时间(这样redis就会负责自动删除过期的计数器)

注意,我们使用事物打包执行INCR命令和EXPIRE命令,避免引入竞争条件,保证每次调用API时都可以正确地对计数器进行自增操作并设置生存时间。

以下是另一个限速器实现:

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的话,那么访问就会被禁止。

这个新的限速器在思路方面是没有问题的,但它在实现方面不够严谨,如果我们仔细观察一下的话,就会发现在INCREXPIRE之间存在着一个竞争条件,假如客户端在执行INCR之后,因为某些原因(比如客户端失败)而忘记设置EXPIRE的话,那么这个计数器就会一直存在下去,造成每个用户只能访问10次,哦,这简直是个灾难。

要消灭这个实现中的竞争条件,我们可以将它转化为一个Lua脚本,并放到redis中运行(这个方法仅限于redis2.6及以上的版本)

local current
current = redis.call(“incr”,KEYS[1])
if tonumber(current) == 1 then
redis.call(“expire”,KEYS[1],1)
end
通过将计数器作为脚本放到redis上运行,我们保证了INCREXPIRE两个操作的原子性,现在这个脚本实现不会引入竞争条件,它可以运作的很好。

还有另外一种消灭竞争条件的方法,就是使用redis的列表结构来代替INCR命令,这个方法无需脚本支持,因此它在redis2.6以下的版本也可以运行的很好:

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
新的限速器使用了列表结构作为容器,LLEN用于对访问次数进行检查,一个事物包裹着RPUSHEXPIRE两个命令,用于在第一次执行计数时创建列表,并正确设置过期时间,最后,RPUSHX在后续的计数操作中进行增加操作。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值