前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >redis多种方式实现访问计数器实例详解

redis多种方式实现访问计数器实例详解

作者头像
青山师
发布2023-05-04 20:14:58
5650
发布2023-05-04 20:14:58
举报

REDIS - string类型

incr

用法

incr key,可以将key值原子自增1,并返回递增操作后key对应的新值。如果指定的key不存在,那么在执行incr操作之前,会先将它的值设定为0。

代码语言:javascript
复制
/*测试前,清除当前数据库所有key*/
127.0.0.1:6379> flushDB
OK
/*没有key*/
127.0.0.1:6379> keys *
(empty list or set) 
/*使用incr 一个不存在的key,有返回为1(如果指定的key不存在,那么在执行incr操作之前,会先将它的值设定为0,并返回自增后的值1)*/
127.0.0.1:6379> incr incrKey
(integer) 1
127.0.0.1:6379> get incrKey
"1"
/*自增1,返回增加后的值2*/
127.0.0.1:6379> incr incrKey
(integer) 2
127.0.0.1:6379> get incrKey
"2"
使用场景1 - 计数器

例如:一个web应用,我们想记录每个用户每天访问这个网站的次数。就可以使用这个用户的id和当天日期拼接一个key,每访问一次只用incr对该key操作,从而获得该用户当天的访问网站次数。比如用户id为9eda3e419e6eadb99293f5c9105816c93a0ca760,今日是20161015,则可以使用incr 9eda3e419e6eadb99293f5c9105816c93a0ca760:20161015作为统计该用户在2016-10-15当天的访问次数。 该场景的扩展:统计该用户在某个时间范围之内的访问次数,可以结合incr、expire来达到目标。

使用场景2 - 限制访问次数(一)

假设我们有这样的需求:每个api接口,每秒每个ip的访问次数不能超过10次。 我们可以为ip:时间戳(到秒)设置key,以下使用伪码展示:

代码语言:javascript
复制
FUNCTION LIMIT_ACCESS_COUNT(ip)
currSecond = CURRENT_UNIX_TIME()
keyName = ip+":"+currSecond
currentCnt = GET(keyName)
IF currentCnt != NULL AND currentCnt > 10 THEN
    ERROR "一秒内访问次数过多"
ELSE
    MULTI
        /*比如10.192.168.27在2016-10-15 15:20:19时访问次数不到10,一直自增*/
        INCR(keyName,1)  
        /*计数器每次递增的时候都设置了10秒的过期时间,这样在进入下一秒时,redis会自动删除前一秒的计数器。
         *  键 10.192.168.27:2016-10-15 15:20:19将会在2016-10-15 15:20:29之后删除      
         */
        EXPIRE(keyName,10)  
    EXEC
    DO_JOB()
END
使用场景2 - 限制访问次数(二)

前面例子是每个ip每一秒都生成一个key。在此例中,我们一个ip只会生成一个key,但是实际使用中需要注意竞态条件的出现。 具体思路是:从第一个请求开始设置过期时间为1秒。如果1秒内请求数超过了10个,那么会提示错误信息。到了下一秒,计数器会清零后重新开始计数。

代码语言:javascript
复制
FUNCTION LIMIT_ACCESS_COUNT(ip)
keyName = ip
currentCnt = GET(keyName)
IF currentCnt != NULL AND currentCnt > 10 THEN
    ERROR "一秒内访问次数过多"
ELSE
    MULTI
        /*比如10.192.168.27在2016-10-15 15:20:19时访问次数不到10,一直自增*/
        currentCnt = INCR(ip)
        IF currentCnt == 1 THEN
        /*计数器每次递增的时候都设置了1秒的过期时间,只有在第一次访问时才设置超时时间为1秒
         * 键 10.192.168.27:2016-10-15 15:20:19将会在2016-10-15 15:20:20之后删除      
         */
        EXPIRE(keyName,1)  
        END
    EXEC
    DO_JOB()
END

处理竞态条件 : 使用LUA脚本。 在前面的例子中,如果使用incr后,没有成功执行expire,会导致这个ip的key引起内存泄漏,知道下次有同一个ip发送相同请求过来。可以将可能发生竞态条件的逻辑放在LUA脚本中,再使用eval解决(要求REDIS2.6版本以上)

代码语言:javascript
复制
/*LUA脚本*/
local currentCnt
currentCnt = redis.call("incr",KEYS[1])
if tonumber(currentCnt) == 1 then
    redis.call("expire",KEYS[1],1)
end

getset

getset key value 会将value设置为key的值,但是返回的是key原来的值。如果key存在但是对应的value不是字符串,就返回错误。如果之前Key不存在将返回nil。

代码语言:javascript
复制
127.0.0.1:6379> flushDB
OK
127.0.0.1:6379> keys *
(empty list or set)
/*使用incr实现计数器自增,使用getset可以重置为0*/
127.0.0.1:6379> incr testKey
(integer) 1
127.0.0.1:6379> incr testKey
(integer) 2
127.0.0.1:6379> getset testKey 0
"2"
127.0.0.1:6379> get testKey
"0"

/*key不存在返回nil*/
127.0.0.1:6379> getset testKey2 0
(nil)
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2016-10-15,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • REDIS - string类型
    • incr
      • 用法
      • 使用场景1 - 计数器
      • 使用场景2 - 限制访问次数(一)
      • 使用场景2 - 限制访问次数(二)
    • getset
    相关产品与服务
    云数据库 Redis
    腾讯云数据库 Redis(TencentDB for Redis)是腾讯云打造的兼容 Redis 协议的缓存和存储服务。丰富的数据结构能帮助您完成不同类型的业务场景开发。支持主从热备,提供自动容灾切换、数据备份、故障迁移、实例监控、在线扩容、数据回档等全套的数据库服务。
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档