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,点击时间时2012年3月22日,那么执行命令INCR Peter::2012.3.22
可以用以下几种方式扩展这个简单的模式:
①可以通过组合使用INCR和EXPIRE,来达到只在规定的生存时间内进行计数(counting)的目的。
②客户端可以使用GETSET命令原子性地获取计数器当前值并将计数器清零。
③使用其他自增/自减操作,比如DECR和INCRBY,用户可以通过执行不同的操作增加或减少计数器的值,比如在游戏中的计分器就可能用到这些命令。
模式:限速器
限速器是特殊的计算器,它用于限制一个操作可以被执行的速率(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的话,那么访问就会被禁止。
这个新的限速器在思路方面是没有问题的,但它在实现方面不够严谨,如果我们仔细观察一下的话,就会发现在INCR和EXPIRE之间存在着一个竞争条件,假如客户端在执行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上运行,我们保证了INCR和EXPIRE两个操作的原子性,现在这个脚本实现不会引入竞争条件,它可以运作的很好。
还有另外一种消灭竞争条件的方法,就是使用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用于对访问次数进行检查,一个事物包裹着RPUSH和EXPIRE两个命令,用于在第一次执行计数时创建列表,并正确设置过期时间,最后,RPUSHX在后续的计数操作中进行增加操作。
介绍Redis中INCR命令的基本用法及其在计数器模式中的应用,并详细探讨了如何利用Redis实现限速器模式,包括使用INCR、EXPIRE、Lua脚本等多种方法。
1928

被折叠的 条评论
为什么被折叠?



