当前位置:首页 > 问答 > 正文

Redis缓存里属性值没了,搞得人头大到底咋回事啊?

(来源:知乎专栏《程序员日常踩坑实录》)这事儿可太常见了,兄弟,你不是一个人在头疼,感觉就像你明明把家门钥匙挂在玄关那个熟悉的钩子上,结果一摸,嘿,空了!那种瞬间的懵逼和烦躁,我懂,咱们就用人话捋一捋,钥匙(也就是Redis里的属性值)为啥会不翼而飞,大概逃不出下面这几种情况。

第一种情况:你自己“扔”的,但你可能忘了。(来源:Stack Overflow高赞回答总结)

这种是最常见的,很多时候不是Redis的锅,是你自己的代码在某个犄角旮旯把数据给清掉了。

  • 被动过期(TTL到期): 这是最应该先检查的,你往Redis里塞数据的时候,是不是顺手设置了一个过期时间(TTL)?set key value EX 3600,意思是3600秒(一小时)后自动消失,如果你的业务逻辑以为这个数据会永远存在,但一小时后它真就“准时下班”了,那你再去取,可不就是空的了嘛,这就好比你买了瓶鲜牛奶,写了保质期到明天,结果你大后天想起来要喝,那肯定坏了啊,检查一下代码里设置key的地方,是不是有过期时间设得太短了,或者根本不该设过期时间你却设了。
  • 主动删除: 你的代码里有没有执行 DEL 命令的地方?或者用了像 flushdb 这种核弹级的命令(这个一般不会在业务代码里,但运维操作时可能误触)?可能是某个清理缓存的逻辑条件判断写错了,本来只想删掉A,结果把B也给连带删了,又或者,团队里另一个哥们写的代码,在某个你意想不到的流程里把key给干掉了,你压根不知道,这种就得好好翻代码,或者查看Redis的慢查询日志、监控记录,看有没有可疑的删除操作。

第二种情况:Redis这个“管家”有点自己的小脾气。(来源:Redis官方文档内存管理部分)

Redis是跑在服务器内存里的,内存是有限的宝贵资源,当内存快被塞满的时候,Redis会启动它的“内存淘汰策略”来腾地方。

Redis缓存里属性值没了,搞得人头大到底咋回事啊?

  • 内存淘汰惹的祸: 你得看看Redis的配置(redis.conf里的 maxmemory-policy),默认策略可能是 noeviction(不淘汰,但新写入会报错),但很多生产环境会设置为 allkeys-lru 之类的,意思是,当内存不足时,Redis会挑一些最近最少使用的(LRU算法)key给淘汰掉,不管它有没有设置过期时间,如果你的某个重要属性值很久没被访问,恰好这时候内存紧张,它就可能被当成“闲杂人等”清退出场了,这就好比你的衣柜满了,你要塞进一件新衣服,你可能会把一件很久没穿的旧衣服拿出来捐掉,即使你没设过期时间,数据也可能因为内存压力而消失,你得确认一下Redis实例的内存使用率是不是一直很高。

第三种情况:网络或者Redis本身“抽风”了。(来源:个人及团队运维经验)

这种相对少见,但一旦发生就很棘手。

  • 网络闪断: 你的应用程序和Redis服务器之间的网络偶尔抖了一下,导致写操作实际上失败了,但你应用程序的代码可能因为没做异常处理,以为写入成功了,等你过会儿去读的时候,发现根本没数据,这就像你网上付款,手机提示支付成功,但其实因为网络问题请求没到银行那边,最后发现扣款失败。
  • Redis持久化问题: Redis为了数据安全,会把内存数据定期写到硬盘上(RDB快照)或者记录每一步操作(AOF日志),如果Redis因为某种原因崩溃了,重启后会从硬盘恢复数据,但如果最后一次持久化之后的数据还没来得及保存,那么这部分数据就丢了,你设置了每5分钟存一次快照,结果Redis在第4分59秒崩溃了,那这5分钟内的新数据就全没了,不过这种情况一般会导致大量数据丢失,不会只丢一两个属性值。
  • 主从复制问题: 如果你用了Redis主从模式,数据会从主库同步到从库,有时候复制链路会有延迟,甚至出错,万一你在从库上读数据(读写分离架构),可能读到的还是旧数据或者空数据,而实际上主库的数据是好的。

第四种情况:最诡异的——命名冲突。(来源:无数程序员的血泪教训)

Redis缓存里属性值没了,搞得人头大到底咋回事啊?

这个错误很低级,但很容易被忽略,就是你给key取的名字太普通了,跟别人重名了!

  • 不同业务撞车: 用户模块用 user:123 来存用户信息,订单模块也傻乎乎地用 user:123 来存这个用户的订单快照,两个不同的程序或者同一个程序的两个功能,都在读写同一个key,结果就是订单服务覆盖了用户服务设置的值,或者反过来,这就像两家人共用一个小区的“001”号储物柜,能不乱套吗?给key起名最好有清晰的命名空间,user:info:123, order:snapshot:123,这样就能避免冲突。

那咋办呢?排查思路是啥?

别慌,按这个顺序摸摸看:

  1. 先查监控: 如果有Redis的监控工具,看那个key的访问曲线,是突然掉没的(像被删除),还是平滑下降到0(像自然过期),同时看下内存使用历史。
  2. 检查过期时间:TTL key_name 命令看看这个key还剩多少秒过期。-1表示永不过期,-2表示不存在(已经没了),正数就是剩余的生存时间。
  3. 翻日志: 查应用程序日志和Redis的慢查询日志,看看在数据丢失的时间点附近,有没有执行过删除(DEL)命令或者覆盖写(SET)命令。
  4. 复盘代码: 仔细检查所有操作这个key的代码段,特别是设置过期时间和删除的地方,想想有没有并发操作可能导致的覆盖。
  5. 确认配置: 看看Redis的 maxmemorymaxmemory-policy 配置是什么,评估一下内存是否紧张。

Redis缓存值没了,十有八九是使用姿势不对,它是个好工具,但也不是保险箱,理解了它什么时候、为什么会“丢”东西,下次再遇到这事儿,你就能更快地锁定目标,不至于满头包了。