关于用 Redis 限制用户行为频率的方法,麻烦 V 友帮看看
时间: 2020-08-21来源:V2EX
前景提要
需求:
这是昨天面试中遇到的一个问题,当用户 x 秒内请求 y 次,禁止用户访问
先写一些我自己想的方案,下面的 windowStart 为 x 秒之前的 timestamp, current 为现在的 timestamp, threshold 为请求次数限制。下列方法都需要设置和更新 key 过期时间,以免造成 key 泄漏。
方案 1:
用 Redis 的 List 数据结构记录用户访问的时间
当用户访问时 rpush 当前时间戳并删掉左侧 x 秒之前的时间戳,此时该列表的长度为 x 秒请求次数,注意此方法的 timestamp 需要用 Redis 的 Lua 脚本否则可能因为各机器时间不同出问题(例如某台机器时间为未来,则 ltrim 不到该条数据后面的正常时间戳)
伪代码: redis.rpush(key, current); redis.expire(key, current + y); counter = 0; while (redis.get(key, 0) < windowStart) { counter++; } redis.ltrim(0, counter); if (redis.llen(key) > y) { doBanUser(); }
方案 1 的问题:
只需要最多存 y 条数据,但是这里用到了 List,Redis 中的 List 为双向链表,每个节点包含两个指针,在 64 位机器上占用 128bit,而实际有用的时间戳只要 64bit 。迭代链表时会更慢一些。
方案 2:
使用数组 ringbuffer 代替方案一的链表。
方案 2 的问题:
需要自己开发 Redis module,开发和运维成本较高。另外方案 1 和方案 2 中均存不方便重试和测试的问题。例如其他业务调自己服务但是自己服务超时,此时业务方重试,这种情况不应该算用户访问了两次。
方案 3:
使用 Redis 的 ZSet 数据结构
以用户请求的唯一 requestId 为 member,当前时间为 score,如果 requestId 已存在( logn 复杂度),不进行任何操作,如果不存在,则 ZADD,然后使用 ZRANGEBYSCORE 查看该时间段内请求数量。该方案同时需要使用 ZREMRANGEBYSCORE 来删除过老的元素,我认为可以每次或 N 次请求的时候删除,面试官说有更好的删除时机,但是我也没有问,这个问题就这么过去了。
想问问 V 友该方法应该如何删除,或者有没有更好的方法来用 Redis 限制用户行为频率?

科技资讯:

科技学院:

科技百科:

科技书籍:

网站大全:

软件大全:

热门排行