面试题答案
一键面试Redis的EXPIRE命令实现键的过期管理原理
- 设置过期时间:
EXPIRE key seconds
命令用于为指定的键设置过期时间,单位为秒。当执行该命令时,Redis会在内部数据结构中记录该键的过期时间戳(当前时间 + seconds)。 - 过期检查机制:Redis使用惰性删除和定期删除两种策略来处理过期键。
- 惰性删除:当客户端尝试访问一个键时,Redis会首先检查该键是否过期。如果过期,则删除该键并返回相应的结果(例如
nil
)。这种方式不会主动去扫描所有键来删除过期键,只有在访问时才检查,减少了额外的性能开销。 - 定期删除:Redis会定期(默认每秒10次)随机抽取一定数量的键进行检查,删除其中过期的键。这个定期操作是为了防止惰性删除导致大量过期键长时间占用内存。每次抽取的键数量和执行频率可以通过配置参数调整。
- 惰性删除:当客户端尝试访问一个键时,Redis会首先检查该键是否过期。如果过期,则删除该键并返回相应的结果(例如
实际项目中使用EXPIRE命令实现过期管理的常见场景
- 缓存数据过期:
- 场景描述:在Web应用中,经常会缓存数据库查询结果。例如,一个新闻网站会缓存热门新闻的详情页面数据。由于新闻内容可能会更新,缓存的数据不能永久保存。
- 代码示例(Python + Redis):
import redis
r = redis.Redis(host='localhost', port=6379, db=0)
news_id = 123
news_detail = get_news_detail_from_db(news_id) # 假设从数据库获取新闻详情
r.set(f'news:{news_id}', news_detail)
r.expire(f'news:{news_id}', 3600) # 设置缓存1小时过期
- 限时令牌桶:
- 场景描述:在API限流场景中,使用令牌桶算法来控制请求频率。每个请求需要消耗一个令牌,而令牌以固定的速率生成。可以利用Redis的过期键机制来模拟令牌桶。
- 代码示例(Java + Redis):
import redis.clients.jedis.Jedis;
public class RateLimiter {
private Jedis jedis;
private String key;
private int capacity;
private int refillRate;
public RateLimiter(Jedis jedis, String key, int capacity, int refillRate) {
this.jedis = jedis;
this.key = key;
this.capacity = capacity;
this.refillRate = refillRate;
}
public boolean tryAcquire() {
long now = System.currentTimeMillis();
jedis.watch(key);
Long tokens = jedis.get(key) == null? 0 : Long.parseLong(jedis.get(key));
long lastRefill = jedis.get(key + ":lastRefill") == null? 0 : Long.parseLong(jedis.get(key + ":lastRefill"));
long newTokens = Math.min(capacity, tokens + (now - lastRefill) / 1000 * refillRate);
jedis.multi();
jedis.setex(key, 3600, String.valueOf(newTokens)); // 设置过期时间,保证令牌桶的持续运行
jedis.setex(key + ":lastRefill", 3600, String.valueOf(now));
jedis.exec();
if (newTokens > 0) {
jedis.decr(key);
return true;
}
return false;
}
}
在上述代码中,setex
命令设置了键的过期时间,确保令牌桶的持续运行和重置,实现了限时令牌桶的功能。