场景分析
- Redis 用来实现应用和数据库之间读操作的缓存层,主要目的是减少数据库 IO ,还可以提升数据的 IO 性能。
- 当应用程序需要去读取某个数据的时候,首先会先尝试去 Redis 里面加载,如果命中就 直接返回。如果没有命中,就从数据库查询,查询到数据后再把这个数据缓存到 Redis里面。
- 一份数据 ,同时保存在数据库和 Redis 里面,当数据发生变化的时候,需要同时更新 Redis 和 MySQL,由于更新是有先 后顺序的 ,并且它不像 MySQL中的多表事务操作 ,可以满足 ACID 特性,所以就会出现数据一致性问题。
- 如果先更新数据库,再更新缓存,如果缓存更新失败,就会导致数据库和 Redis 中的数据不一致。
问题分析
- 如果是先删除缓存,再更新数据库,理想情况是应用下次访问 Redis 的时候,发现 Redis 里面的数据是空的,就从数据库加载保存到 Redis 里面,那么数据是一致的。
- 在极 端情况下,由于删除 Redis 和更新数据库这两个操作并不是原子的,所以在这个过程如果有其他线程来访问 数据,还是会存在数据不一致问题。
- 所以 ,如果需要在极端情况下仍然保证 Redis 和 MySQL 的数据一致性 ,就只能采用最终一致性方案。
最终一致性方案
- 基于 RocketMQ 的可靠性消息通信 ,来实现最终一致性
- 通过 Canal 组件,监控 MySQL 中 bin log 的日志,把更新后的数据同步到 Redis 里面
- 基于SEATA组件提供的TCC模式,也可实现最终一致性
- 另外,还可以了解下延迟双删策略,核心步骤是删除redis,更新数据库,延迟500ms,再删除Redis
强一致方案
- 如果某些业务场景下,不能接受最终一致性方案,可以考虑使用分布式读写锁机制
- 读写锁机制可确保同时只有一个线程可以对数据进行修改,并且在数据修改的过程中,读线程需要进行等待
小结
- 总体上来说,每个方案都不完美,需要根据具体需求和场景灵活选择适合的策略,并注意权衡性能、可靠性和成本等因素。