记一次Redis脏读问题


这周二遇到一个问题:用户刚新增了一个数据,但是刷新的时候没能显示出来。

于是开始进行问题排查,首先登陆用户的界面,发现果然没有显示数据,然后查了一下线上的数据库,发现新增的数据已经入库了,接着判断是不是代码层面存在什么漏洞。

所以开始猜测引起的原因是数据脏读,现在项目中使用的数据存储结构如下:

缓存层+持久层

使用这个设计的好处是:

  1. 加速读写:因为缓存放在内存中,可以有效加速读写。
  2. 降低后端负载:帮助后端减少访问量和复杂计算(例如复杂的SQL语句)

同样也存在一些问题:

  1. 数据不一致性:缓存层和存储层的数据存在着一定时间窗口的不一致性,这个跟更新策略有关。
  2. 代码维护成本:加入缓存后,需要同时处理缓存层和存储层的逻辑。

现在采用的更新策略是:超时删除+主动更新
不经常更新的值存进缓存中,设定超时时间,让其在过期时间后自动删除,如果进行数据修改操作(更新、删除、新增),会主动删除这个键,而读操作就跟上图一样,先访问缓存,没有命中会去持久层查询,然后将结果写到缓存中,下次读取就能直接从缓存中读取。


回到正题,初步判断出现是因为新增数据的时候,并没有将这个key删除,于是用户刷新页面,后端返回给前端的还是缓存里面的数据,但这个问题是怎么产生的呢?

经过同事金伟的排查,发现有可能是用户新增数据的时候,鼠标焦点还在输入框,在这时点击了导航栏,这样会同时发起两个请求,一个是新增数据的请求,另一个是读取科目的请求。

正常来说,新增数据会将缓存里面对应的key删除,但由于并发操作,存在一定概率没有将缓存的key删除掉,详细解决方法还有待讨论。

对于redis,我了解不够多,所以趁着这个机会,好好拜读了付磊和张益军著作的《Redis开发与运维》,初步了解了主从、哨兵、集群的概念,还有不同参数的设定会起到什么效果。

先给自己挖个坑,慢慢填Redis学习这个坑吧~