一,定义
缓存和数据库的一致性问题指的是在使用缓存来提高数据读取性能的同时,确保缓存中的数据反映了数据库中的最新状态。
二,处理方案
先写数据库再删除缓存
- 读请求第一次查询时,会查询到一个错误的数据,因为写请求还没有更新到缓存,写请求写入 MySQL 成功后会删除缓存中的历史数据。后续读请求查询缓存没有值就会再请求数据库 MySQL 进行重新加载,并将正确的值放到缓存中。
- 优点:实现简单。
- 缺点:可能导致脏读。
同步双写
- 同时更新数据库和缓存。
- 优点:保证了一致性。
- 缺点:复杂,性能开销较大,多线程情况下仍然会出现脏读
先删除缓存再写数据库
假设有两个并发的读写操作,一个是写操作,另一个是读操作。
- 并发读写的情况下,写操作首先删除缓存,接下来需要执行更新数据库操作。
- 读操作发生,由于缓存已经被删除,读操作不得不从数据库中读取数据。然而,由于写操作尚未完成,数据库中的数据仍然是过时的。
- 写操作这时需要更新数据库中的值,更新后 MySQL 数据库是最新的值。
- 读操作将从数据库中查询到的过时数据再回写到缓存。
在这种情况下,读操作获取到的是过时的数据,尽管写操作已经完成。因为缓存被删除,读操作不得不从数据库中读取旧值,而不是最新的值。
延时双删
- 更新数据库前后都删除缓存。
如果说读请求回写缓存在写请求第二次删除缓存之前,那这种技术方案是比较好的,而且也不用引入过多复杂的中间件。
问题就在于,第二次删除缓存,不一定在读请求回写缓存之后。所以我们需要保证第二次删除要在请求回写缓存之后。
假设读请求回写缓存大概需要 300ms,那我们是否可以在写请求第二次删除缓存前进行一个延迟操作,比如睡眠 500ms 后再删除?这样就可以规避读请求回写缓存在第二次删除之后了。这种方案理论上是可以的,不过把这个睡眠操作使用延迟队列或者引入三方消息队列去做。
- 优点:减少脏读,处理竞争条件。
- 缺点:增加了系统复杂性。
异步更新
- 使用消息队列等机制异步更新缓存。
- 优点:减少对性能的影响。
- 缺点:可能有短暂的数据不一致。