高频面试Redis之签到、限流

1、简介

面试环节,面试官往往会结合一些实际的场景,下面通过分析redis的签到、限流两个最常见的场景。

2、签到

位图,本质string,可理解为byte数组,利用 0、1 标识是否签到。节约内存空间(46字节365天)

// 按月签到 setbit key offset(偏移量,0:1号,1:2号) value(1:签到;0:未签到)
setbit u:sign:1000:202101 0 1  // 1月1号签到 
setbit u:sign:1000:202101 1 1  // 1月2号签到 

bitcount u:sign:1000:202101    // 统计1月份签到总次数
3、滑动窗口限流

假设1分钟内,限定访问60次。使用zset的score值维护一个时间窗口,对于每次新的请求时间窗口都向前滑动一下,然后判断窗口中的元素个数,如果大于60个,则不能访问业务。member字段必须保证唯一性,可以考虑使用时间戳。

// period 时间周期,例如 60秒
// maxCount 时间周期内最大请求数
public boolean isActionAllowed(String userId, String actionKey, int period, int maxCount) {
          
   
    String key = String.format("hist:%s:%s", userId, actionKey);
    long now = System.currentTimeMillis();
    Jedis jedis = jedisPool.getResource();
    jedis.zadd(key, now, now + "");
    // 维护窗口,清理窗口外的数据
    // zremrangeByScore --> 移除有序集 key 中,所有 score 值介于 min 和 max 之间(包括等于 min 或 max )的成员。
    jedis.zremrangeByScore(key, 0, now - period * 1000);
    Long count = jedis.zcard(key);
    jedis.expire(key, period + 1);
    return count <= maxCount;
}
3、漏斗限流

借助 redis-cell(Redis 4.0) 模块的 cl.throttle 指令实现。

// 参数解释 
// 15 capacity 代表漏洞容量
// 30 60 组合选项,代表漏水速率  oprations / seconds
// 该表达式代表了,漏斗初始容量为 15,即默认可以请求15次;之后受漏斗漏水速率影响,60秒内只能请求30次
cl.throttle liyanchao:reply 15 30 60
4、Lua脚本限流

Lua脚本结合incr命令:

// 10秒内访问3次
-- ./redis-cli --eval ratelimiting.lua rate.limitingl:127.0.0.1 , 10 3
-- 通过逗号来分割key和arg,注意,这个逗号必须前后要有空格

-- rate.limitingl + 1
local times = redis.call(incr,keyS[1])

-- 第一次访问的时候加上过期时间10秒(10秒过后从新计数)
if times == 1 then
    redis.call(expire,keyS[1], ARGV[1])
end

-- 注意,从redis进来的默认为字符串,lua同种数据类型只能和同种数据类型比较
if times > tonumber(ARGV[2]) then
    return 0
end
return 1
经验分享 程序员 微信小程序 职场和发展