缓存数据的更新机制
1.先更新数据库,再删缓存 ---最常用的
2.先更新数据库,再更新缓存
3.先更新缓存,在更新数据库
4.先删缓存,再更新数据库
5.写回。更新数据的时,只更新缓存不更新数据库。缓存异步更新到数据库
各种方式的优缺点
-
先更新库在删除缓存:读操作来说,先去查询缓存,命中了缓存,就直接返回,说明写操作的新值已经更新到了缓存中。没有命中缓存,说明已经写入db也成功删除了缓存,则取db读数据 这时的读到的数据与db的数据一致。 写操作来说,写入成功就删除缓存 。
为什么这种思路存在这么明显的问题,却还具有那么广泛的应用呢?因为这个case实际上出现的概率非常低,产生这个case需要具备如下4个条件 a/读操作读缓存失效,b/有个并发的写操作 c/写操作比读操作更快 d/读操作早于写操作进入数据库,晚于写操作更新缓存。而实际上数据库的写操作会比读操作慢得多,而且还要锁表,而读操作必需在写操作前进入数据库操作,而又要晚于写操作更新缓存,所有的这些条件都具备的概率基本并不大。并且即使出现这个问题还有一个缓存过期时间来自动兜底。
-
先更新库 在更新缓存,有更好的读性能。 比如:有2个请求做写操作,请求1 写入10,请求2写入20,但是更新缓存的顺序不是按照写入顺序更新的。先把请求2的数据更新到缓存中为20,在把写请求1的数据更新到缓存中。 那么 最好db落库的数据为20,但更新到缓存中数的数据为10. 导致缓存和db中的数据不一致,出现读脏数据的可能 。 2个并发写操作 最终缓存的数据会是2个写操作中先完成写入的数据
-
更新缓存 更新数据库 Read/Write Through套路是把更新数据库(Repository)的操作由缓存自己代理了,所以,对于应用层来说,就简单很多了。可以理解为,应用认为后端就是一个单一的存储,而存储自己维护自己的Cache。数据库由缓存代理,缓存未命中时由缓存加载数据库数据然后应用从缓存读,写数据时更新完缓存后同步写数据库。应用只感知缓存而不感知数据库。
-
先删除缓存 在更新数据库,原db和缓存数据均为10. 比如1个写操作,写入20,1个读操作,写操作后先把旧缓存10删除,读操作无法命中缓存则去db读数据,此时写操作的数据20还未更新到db中,读操作读取的数据是之前的旧数据10,读取到数据后在把该旧数据10写入到缓存中,此时写操作提交数据20到db,db完成落库但是不在去更新缓存。所以缓存数据仍未10,但db数据为20. 第二次写请求30,删除旧缓存10,在去读db数据20,写入缓存,缓存数据为20,db落库数据为30 . 还是造成数据不一致的问题。这种方式会造成 缓存数据落后db数据一个版本 。数据不一致 并且会一直脏下去直到缓存过期或发起新的更新操作
写操作 -更新 读操作-查询 删缓存10 无法命中 读db数据10 写入db数据 20 写入缓存10 删缓存10 无法命中 读db数据20 写入db数据30 写入缓存20 -
更新数据的时候,只更新缓存,不更新数据库,缓存会异步批量更新到数据库。优点:进行读操作时,会命中缓存,直接从缓存中拿数据,保证数据是最新的。且不对数据库构成大压力。异步更新 可以对多次的写操作合并 只做1次更新,提高性能。缺点:更新到数据库时容易丢失数据,缓存里的数据是存储在内存中的 容易丢失数据。 所以写操作建议采用消息队列kafka MQ的方式,从队列里消费数据写入数据库里。消息队列的优点 保证数据一致性 数据不丢失。