ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Redis键过期失效?这个坑我踩得明明白白

Redis键过期失效?这个坑我踩得明明白白 明明设置了过期时间为什么内存还是被打爆了——这是去年我们在一个日均千万级UV的推荐系统里遇到的灵异事件。Redis集群频繁触发内存告警但监控显示所有key都设置了24小时TTL。你以为的自动过期可能根本没按你想的方式工作。现象悄悄膨胀的Redis内存项目背景一个基于用户行为的实时推荐服务用Redis存储用户最近24小时的行为特征key设计为user_behavior:{user_id}。理论上每天的数据会自动淘汰内存占用应该保持稳定。但上线两周后运维突然告警Redis内存使用率突破80%阈值。用redis-cli --bigkeys分析发现大量本该过期的key仍然存在。手动执行TTL命令检查居然返回-1永不过期而我们明明在代码里写了EXPIRE// 错误的写法但看起来完全合理 public void saveUserBehavior(long userId, String behavior) { String key user_behavior: userId; redisTemplate.opsForValue().set(key, behavior); redisTemplate.expire(key, 24, TimeUnit.HOURS); // 设置24小时过期 }根因SETEXPIRE的原子性漏洞问题出在两步操作的非原子性上。在高并发场景下可能出现线程A执行SET key value线程B执行DEL key比如用户主动清除记录线程A继续执行EXPIRE key 86400此时key已经被删除EXPIRE实际上作用在了一个不存在的key上自然不会生效。更讽刺的是这种竞争条件在测试环境几乎无法复现——需要特定并发时序才会触发。Redis的SETEXPIRE不是事务操作中间可能被其他命令插入。你以为的设置值并立即设置过期时间在高并发下可能变成设置值→其他操作→设置过期时间失败。解决方案原子操作才是王道正确做法是用Redis的原生原子操作一个命令完成SET和EXPIRE// 正确的原子操作 public void saveUserBehavior(long userId, String behavior) { String key user_behavior: userId; redisTemplate.opsForValue().set(key, behavior, 24, TimeUnit.HOURS); // 单命令原子操作 }或者用SETEX命令注意Java客户端封装在setIfAbsent等方法里// 另一种原子写法 Boolean result redisTemplate.execute((RedisCallbackBoolean) connection - { byte[] keyBytes redisTemplate.getKeySerializer().serialize(key); byte[] valueBytes redisTemplate.getValueSerializer().serialize(value); return connection.setEx(keyBytes, 86400, valueBytes); });性能对比原子操作不仅更安全还能减少网络往返1次 vs 2次RTT。实测在千级QPS下这种改动能降低约15%的Redis负载。更深层的坑你以为过期就真的删除了解决了原子性问题后内存仍然有小幅增长。进一步排查发现Redis的过期删除是惰性定期两种策略惰性删除只有访问key时才会检查并删除已过期的key定期删除Redis每10秒随机检查20个key删除其中过期的这意味着如果没有主动访问大量已过期的key可能长期占用内存直到下一次定期扫描碰巧选中它。对于冷数据这个时间差可能长达数小时。解决方案对重要key启用主动扫描# 定期扫描匹配模式的key触发被动删除 redis-cli --scan --pattern user_behavior:* | xargs redis-cli ttl适当调高定期删除的频率修改redis.confhz 10 → hz 100 # 提高后台任务执行频率 active-expire-effort 1 → active-expire-effort 10 # 增加CPU消耗更积极地删除避坑清单关于Redis过期的那些坑TTL的单位陷阱EXPIRE单位是秒PEXPIRE是毫秒客户端封装可能不同比如Java的TimeUnit要明确指定DEL会清除过期时间在key过期前手动执行DEL再执行SET会导致key永不过期RENAME的副作用重命名key会继承原key的过期时间但若新key已存在会丢弃原有过期时间持久化时的坑AOF模式下过期key删除操作会追加到AOF文件但RDB持久化时已过期但未删除的key会被持久化总结与讨论Redis的过期机制就像瑞士钟表——精密但需要理解其运作原理。核心经验任何非原子操作在高并发下都可能失效对关键操作要追问如果在这一步被打断会怎样你在项目中还遇到过哪些Redis的隐藏规则欢迎分享你的踩坑经历。
返回列表