redis经常作为mysql的缓存以提升用户访问速度、降低mysql服务的压力但是这样就会涉及到mysql和redis数据同步问题我们得尽量保证修改完数据库后redis缓存也能同步成新的数据否则redis层一直用旧的数据拦截请求更新会很长时间不起作用除非缓存过期。缓存延时双删步骤核心使命如果没有它会发生什么① 先删缓存制造“空窗期”避免更新期间有人读到明确的旧缓存。更新过程中读请求会一直命中旧缓存造成短暂脏读。② 更新数据库持久化新值这是最终的事实标准。无意义这是必做项③ 延时等待并发读请求完成“旧数据回填”操作。在进行数据库更新之前如果有一个查询请求提前查询到了数据库的旧数据或者说因为隔离性等原因看到旧数据就会把旧数据回填到redis缓存中第二次如果删除执行太早没有延时“旧回填数据”就会发生在第二次删除之后等于白删。④ 再删缓存清除被并发读线程误回填的旧缓存保证最终一致性。缓存中可能会永久残留旧数据直到过期导致长期不一致。需要注意的是缓存延时双删保证的是最终一致性而不是实时一致性总有一些时候缓存设Mysql数据库的内容不一致我们能做的只是尽力缩短不一致的时间。另外这个方法也可能失效假设在进行数据库更新之前一个查询请求提前查询到了数据库的旧数据或者说因为隔离性等原因看到旧数据等到延时时间过了之后才把旧数据回填到redis缓存中这样redis中仍旧还会长期存放旧数据。延时可能降低缓存命中率降低性能造成mysql服务压力增大。缓存版本号每一条数据多存储一个版本号一条修改请求这样进行首先直接修改mysql中的数据并把版本号然后修改redis缓存对比当前数据库和redis缓存的版本号如果大于则更新否则说明更加新的修改已经先其一步回填操作取消因为不会删除缓存所以不会出现旧数据回填的情景这种方法不需要延时等待性能较高但是实现较为复杂而且仍旧会出现数据短暂不一致的的情况不过相对于缓存延时双删它结果更加稳定一般不会有失效问题。Binlog订阅CanalMQ一个标准的CanalMQ方案的架构如下业务应用只负责更新MySQL数据库完全不操作Redis缓存。MySQL数据变更时将变更细节写入Binlog日志。Canal Server伪装成从库实时获取Binlog解析后作为生产者将变更事件发送到MQ。消息队列MQ作为消息的缓冲和分发中心保证消息可靠投递。缓存同步服务作为消费者从MQ订阅消息根据事件内容如更新、删除执行对应的Redis操作更新或删除缓存。这种方法的问题在于缓存更新延时比较大数据不一致的情况比较久而且实现复杂优点在于业务代码与缓存彻底解耦业务代码只操作数据库同时解决了缓存版本号的一个漏洞如果两个更新是修改一条数据的不同部分另一个修改可能因为版本号小后更新数据库的范围先更新缓存而更新不到缓存上。redis执行失败无论哪种同步方法其实都害怕redis执行失败一旦失败由于数据库修改了缓存却修改失败就会造成长时间的数据不一致问题。所以一般来说每种方法都要搭配一个队列队列里存放执行失败的redis语句设置一个线程来反复执行之前失败的语句直到执行成功。