缓存一致性
什么是缓存一致性
操作缓存比操作Mysql数据库要快,如果流量大的话可以先将数据保存在缓存中,等空闲的时候再将缓存中的数据持久化到数据库中
还有一些热点数据我们也可以预先从数据库中加载到缓存中,那样在流量大的时候就不需要每次都从数据库里面去读取数据了
因为缓存和数据库是两个不同的地方,最容易出现的问题就是缓存的数据和数据库中的数据不一致问题: 可能从缓存中读取的数据是旧值,又或者我们保存在缓存中的数据还没有持久化到Mysql数据库中
为了保证缓存一致性,减少数据不一致的情况,我们必须合理的使用缓存
不更新缓存,而是删除缓存
后端数据库如果改变的话,我们不应该更新缓存而应该删除缓存,直到下一次数据读取的时候再加载数据库中最新的缓存
如果数据改变了,我们如果去更新缓存的话则会出现很多问题
一、线程安全,脏数据问题
下面有两个并发请求A、B同时更新了数据库
- A更新数据库
- B更新数据库
- B更新缓存
- A更新缓存
由于网络原因,虽然A是先于B更新数据库,但是此时A却后于B更新缓存,此时的缓存的数据就是A的数据而不是最新的数据B,这样就造成了数据不一致的问题
如果我们在更新数据时直接删除数据,此下次再来读取的时候加载一次数据库即可,那么加载的数据一定是最新的
二、频繁更新缓存造成不必要的浪费
如果在写多读少的场景下,缓存数据会进行频繁更新,但是读数据却很少,则会造成资源浪费,还不如直接将缓存删除,下次读的时候只需要一次cache miss
的消耗
同时如果数据需要进过复杂的运算和逻辑处理才能写入缓存,那么频繁的更新缓存也会造成不必要的消耗
先操作数据库,再删除缓存
在写数据的时候,我们一般是先写数据库,再删除缓存 这样才能减少数据不一致的问题
先删除缓存,再操作数据库
此方案在并发读写的时候也会造成数据不一致性的问题
假设有线程A、B
- 线程A请求写操作,于是线程A会删除缓存数据再进行数据库写操作
- 此时线程B在A写操作的过程中进行读操作,引发一次
cache miss
- 由于B是在A写事务执行过程中来读取的,于是B加载到缓存中的数据还是之前的老数据
- 此时A写操作完成,这样就造成了缓存和数据库的数据不一致性,如果缓存的数据没有过期时间的话则其他客户端读取到的数据就一直都是脏数据直到下次 发生数据写操作删除缓存
先更新数据库,再删除缓存
这种方案不会引发上面的缓存不一致的问题,一旦数据库的数据更新完毕之后就会立即删除缓存中的数据,则下次再读取时就会引发一次cache miss
就能读取到最新的数据
但是也会有很小的概率会发生数据不一致的状态
假设有两个并发线程A、B
- A请求读取数据,此时缓存凑巧失效了,A引发
cache miss
之后读取数据 - B请求在A读取数据的同时在写入新值到数据库中,并且写入完成之后删除了缓存
- 此时A又将读取到的旧值加载到了缓存中,引发数据不一致的问题
上面的情况发生的概率是比较小的