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