1. 背景
目前网上有很多关于保证redis缓存数据和mysql数据一致性的方案,想在此做一些总结。
2. 延时双删
延时双删方案:先删除redis缓存 -> 再更新数据库 -> 延时再删除一次redis缓存; 不适用与写频繁的场景
2.1 为什么要删除两次呢?
最开始的方案其实是先删除redis缓存,再更新数据库。但是在并发情况下,线程a删除了缓存,但是还没有把数据库更新,这个时候线程b查询redis没有,就将mysql中更新前的数据查询回来放到了redis中,就会导致mysql和redis缓存不一致了。再加一次延迟删除主要是为了解决并发情况下的问题。
2.2 先更新数据库再删除缓存是否可行?
如果我们先更新了数据库,再删除缓存,因为不在一个事务当中,可能出现数据库更新了,但是缓存可能没有更新。包括发mq,依赖mq的重试方案,也会出现丢消息的情况。
2.3 那么为什么延迟双删方案可以先更新数据库后删除还款呢?
首先,如果第一步的redis删除失败,就不会更新数据库了;然后在延迟双删的方案当中第二次删除相当于是在高并发情况下的一个兜底,丢消息的影响没有只有一次删除的方案中依赖性那么重。这块博主也不是很肯定这个答案,欢迎大家指点哈。
2.4 为什么不使用写频繁
如果我们频繁地修改某一个key的时候,由于我们每一次修改都要删除一次缓存,就会出现redis缓存长时间不存在,需要到mysql查询,出现缓存击穿,延时增加。
3. 逻辑过期
上面延迟双删的方案,有着多次删除,可能会出现缓存穿透,所以为了避免,我们可能在缓存中放一个逻辑时间,通过job扫描逻辑时间过期后更新缓存。
3.1 实时性不高
job执行是有时间的,例如说我们每5min扫描一次,那么不一致的情况就会存在最长5min,对于实时性要求高的场景无法满足。
4. 基于binlog订阅
大家日常如果使用的数据库是主从架构,就会知道,当一条数据更新后,立马查询,可能会发现还是老的数据;这种业务,查询默认走的是从库,而更新是直接更新的主库,从库的数据依赖主从同步,所以会有延迟。我们在更新redis缓存的时候,其实也可以基于binlog进行。
4.1 canel框架
使用阿里的开源框架canel,我们可以对mysql的binlog进行定义,基于binlog进行缓存的更新。
5. 总结
没有银弹
没有一个完全完美的方案,在缓存一致性的这个课题当中,一旦我们开始使用缓存;就没法100%解决一致性问题,如果通过手段保证强一致性,那么使用缓存带来的优势也会不那么明显;我们所能做的也是在业务场景下,尽可能保证缓存的一致性,如果对一致性有着100%要求的,那么是否不应该使用缓存呢?