
我直接先抛一下结论在满足实时性的条件下不存在两者完全保存一致的方案只有最终一致性方案。根据网上的众多解决方案总结出 6 种直接看目录不好的方案1. 先写 MySQL再写 Redis图解说明这是一副时序图描述请求的先后调用顺序橘黄色的线是请求 A黑色的线是请求 B橘黄色的文字是 MySQL 和 Redis 最终不一致的数据数据是从 10 更新为 11后面所有的图都是这个含义不再赘述。请求 A、B 都是先写 MySQL然后再写 Redis在高并发情况下如果请求 A 在写 Redis 时卡了一会请求 B 已经依次完成数据的更新就会出现图中的问题。这个图已经画的很清晰了我就不用再去啰嗦了吧不过这里有个前提就是对于读请求先去读 Redis如果没有再去读 DB但是读请求不会再回写 Redis。大白话说一下就是读请求不会更新 Redis。2. 先写 Redis再写 MySQL同“先写 MySQL再写 Redis”看图可秒懂。3. 先删除 Redis再写 MySQL这幅图和上面有些不一样前面的请求 A 和 B 都是更新请求这里的请求 A 是更新请求但是请求 B 是读请求且请求 B 的读请求会回写 Redis。请求 A 先删除缓存可能因为卡顿数据一直没有更新到 MySQL导致两者数据不一致。这种情况出现的概率比较大因为请求 A 更新 MySQL 可能耗时会比较长而请求 B 的前两步都是查询会非常快。好的方案4. 先删除 Redis再写 MySQL再删除 Redis对于“先删除 Redis再写 MySQL”如果要解决最后的不一致问题其实再对 Redis 重新删除即可这个也是大家常说的“缓存双删”。为了便于大家看图对于蓝色的文字“删除缓存 10”必须在“回写缓存10”后面那如何才能保证一定是在后面呢网上给出的第一个方案是让请求 A 的最后一次删除等待 500ms。对于这种方案看看就行反正我是不会用太 Low 了风险也不可控。那有没有更好的方案呢我建议异步串行化删除即删除请求入队列异步删除对线上业务无影响串行化处理保障并发情况下正确删除。如果双删失败怎么办网上有给 Redis 加一个缓存过期时间的方案这个不敢苟同。个人建议整个重试机制可以借助消息队列的重试机制也可以自己整个表记录重试次数方法很多。简单小结一下“缓存双删”不要用无脑的 sleep 500 ms通过消息队列的异步串行实现最后一次缓存删除缓存删除失败增加重试机制。5. 先写 MySQL再删除 Redis对于上面这种情况对于第一次查询请求 B 查询的数据是 10但是 MySQL 的数据是 11只存在这一次不一致的情况对于不是强一致性要求的业务可以容忍。那什么情况下不能容忍呢比如秒杀业务、库存服务等。当请求 B 进行第二次查询时因为没有命中 Redis会重新查一次 DB然后再回写到 Reids。这里需要满足 2 个条件缓存刚好自动失效请求 B 从数据库查出 10回写缓存的耗时比请求 A 写数据库并且删除缓存的还长。对于第二个条件我们都知道更新 DB 肯定比查询耗时要长所以出现这个情况的概率很小同时满足上述条件的情况更小。6. 先写 MySQL通过 Binlog异步更新 Redis这种方案主要是监听 MySQL 的 Binlog然后通过异步的方式将数据更新到 Redis这种方案有个前提查询的请求不会回写 Redis。这个方案会保证 MySQL 和 Redis 的最终一致性但是如果中途请求 B 需要查询数据如果缓存无数据就直接查 DB如果缓存有数据查询的数据也会存在不一致的情况。所以这个方案是实现最终一致性的终极解决方案但是不能保证实时性。几种方案比较我们对比上面讨论的 6 种方案先写 Redis再写 MySQL这种方案我肯定不会用万一 DB 挂了你把数据写到缓存DB 无数据这个是灾难性的我之前也见同学这么用过如果写 DB 失败对 Redis 进行逆操作那如果逆操作失败呢是不是还要搞个重试先写 MySQL再写 Redis对于并发量、一致性要求不高的项目很多就是这么用的我之前也经常这么搞但是不建议这么做当 Redis 瞬间不可用的情况需要报警出来然后线下处理。先删除 Redis再写 MySQL这种方式我还真没用过直接忽略吧。先删除 Redis再写 MySQL再删除 Redis这种方式虽然可行但是感觉好复杂还要搞个消息队列去异步删除 Redis。先写 MySQL再删除 Redis比较推荐这种方式删除 Redis 如果失败可以再多重试几次否则报警出来这个方案是实时性中最好的方案在一些高并发场景中推荐这种。先写 MySQL通过 Binlog异步更新 Redis对于异地容灾、数据汇总等建议会用这种方式比如 binlog kafka数据的一致性也可以达到秒级纯粹的高并发场景不建议用这种方案比如抢购、秒杀等。个人结论实时一致性方案采用“先写 MySQL再删除 Redis”的策略这种情况虽然也会存在两者不一致但是需要满足的条件有点苛刻所以是满足实时性条件下能尽量满足一致性的最优解。最终一致性方案采用“先写 MySQL通过 Binlog异步更新 Redis”可以通过 Binlog结合消息队列异步更新 Redis是最终一致性的最优解。