Redis保存对象引用没完成,数据存储过程还卡着没走完
- 问答
- 2025-12-28 20:00:10
- 1
(根据知乎用户“某技术宅的日常”分享的案例)当时我们系统里有一个功能,用户下单成功后,需要立刻把这个订单对象的一些关键信息,比如订单号、用户ID、商品详情什么的,存到Redis里面去,这么做的目的是为了后续能快速查询,毕竟Redis速度快嘛,这个保存操作是和我们把订单主数据写入数据库的那个大操作放在同一个事务里的,理论上,这两个操作应该要么都成功,要么都失败,保证数据一致。
(案例描述继续)但是有一天,我们遇到了一个特别奇怪的问题,有用户反馈说下单后,在APP里看不到刚下的订单,我们一查数据库,发现订单明明已经好好地存在主表里了,状态也是“已支付”,这说明写入数据库这个步骤是成功完成了的,可是,当我们去Redis里用这个订单号查的时候,却怎么也查不到对应的缓存数据,就好像存了一半,另一半给丢了一样。
(根据CSDN博客“码农小胖的踩坑记录”中的分析)一开始我们以为是网络闪断,或者Redis服务器当时压力太大响应慢了,但看了监控日志后发现,事情没那么简单,日志显示,应用服务器确实向Redis发送了存储那个订单对象的命令,Redis也接收到了,问题出在后续,在Redis还没来得及把数据完全存到内存(可能还有后续的持久化到磁盘)的这个过程中,我们应用程序里执行数据库事务的那个线程,不知道因为什么原因,被卡住了一小会儿,或者说出现了一个非常短暂的阻塞。
(分析继续)这个阻塞可能只持续了几百毫秒,甚至更短,短到一般的监控都很难捕捉到,就是这短短的一瞬间,把整个流程打乱了,因为应用程序和Redis的连接是有一个超时时间设置的,当数据库事务那边卡了一下再恢复后,它以为自己这边所有操作都成功了,就提交了事务,可实际上,它并没有耐心地等待Redis那边给它一个确切的回复,嘿,你让我存的数据我已经彻底存好了,没问题了”,可能应用程序在等待Redis响应时,因为自身的阻塞,错过了Redis返回的成功消息,或者连接超时了,导致它误以为Redis存储失败了。
(引用自开源社区一位开发者“老兵”的讨论帖)这就产生了一个矛盾的状态:数据库里,订单已经铁板钉钉地存在了,事务已经提交,无法回滚,而Redis这边,因为应用程序没有正确接收到成功的确认,它可能以为对方不要这个数据了,或者由于连接异常,它存储的动作可能根本没彻底完成,处于一种要完成没完成的“悬空”状态,结果就是,数据存储的这个过程,在Redis这一环上,可以说是“卡着没走完”,你从数据库看,订单成了;从缓存看,订单像没发生过一样。
(案例后续处理部分)后来我们排查发现,那个短暂的阻塞是因为当时服务器正在进行一次小的垃圾回收,正好在那个关键时刻发生,导致了线程暂停,解决这个问题,光靠重试机制还不够,我们改进了代码逻辑,最重要的一个改动是,我们把保存Redis的这个操作,从数据库事务内部挪到了事务成功提交之后再去执行,也就是说,只有确定数据库百分之百保存成功了,我们才去触发保存Redis的动作,在保存Redis的时候,加强了异常处理和重试,确保只要数据库成功了,Redis这边无论如何也要想办法存进去,哪怕多试几次,我们也优化了应用程序的性能,减少这种可能导致线程长时间暂停的垃圾回收情况。
(总结性描述)“Redis保存对象引用没完成,数据存储过程还卡着没走完”这个说法,描述的就是这么一种尴尬的局面,它不是指Redis服务器本身宕机了或者命令完全没执行,而是在一个涉及多个步骤(比如数据库事务和缓存操作)的复杂流程中,由于网络抖动、应用程序自身的短暂停顿、超时设置不合理等各种细微的原因,导致各个步骤之间协调出了问题,使得从全局看,数据存储的完整流程没有顺畅地走到底,在缓存这个环节掉了链子,留下了数据不一致的隐患,用户那边感受到的,就是系统行为不正常,有些地方有数据,有些地方没有。

本文由太叔访天于2025-12-28发表在笙亿网络策划,如有疑问,请联系我们。
本文链接:http://www.haoid.cn/wenda/70221.html
