缓存如何更新

在看很多博客上介绍如何更新缓存,都会说先删缓存再更新数据库,但是仔细一想会出现数据不一致的问题,比如一个线程A更新数据,一个线程B查询数据,那么就会出现下面的步骤:

  1. A线程先删除缓存
  2. B去请求缓存时,发现没有缓存,就去数据库请求数据
  3. B将请求到的数据回写到缓存中
  4. A更新完数据库

此时数据库和缓存的数据就发生不一致了。
所以这种方式肯定不可取。还有的人说先更新数据库再更新缓存,这也是不行的,容易出现脏数据。

下面正确的方式是:Cache Aside Pattern
它是使用的最多的一种更新缓存的方式,具体的逻辑如下:

  • 失效:应用程序先从cache取数据,没有得到,则从数据库中取数据,成功后,放到缓存中。
  • 命中:应用程序从cache中取数据,取到后返回。
  • 更新:先把数据存到数据库中,成功后,再让缓存失效。

总结一句话就是先更新数据库再删缓存
还是这个场景,一个线程A更新数据,一个线程B查询数据:
首先,没有了删除cache数据的操作了,而是先更新了数据库中的数据,此时,缓存依然有效,所以,并发的查询操作拿的是没有更新的数据,但是,更新操作马上让缓存的失效了,后续的查询操作再把数据从数据库中拉出来。而不会像先删缓存再更新数据库方式产生的问题,后续的查询操作一直都在取老的数据。
但是Cache Aside Pattern真的十全十美吗?
不是的,比如一个是读操作,但是没有命中缓存,然后就到数据库中取数据,此时来了一个写操作,写完数据库后,让缓存失效,然后,之前的那个读操作再把老的数据放进去,所以,会造成脏数据。
但,这个case理论上会出现,不过,实际上出现的概率可能非常低,因为这个条件需要发生在读缓存时缓存失效,而且并发着有一个写操作。而实际上数据库的写操作会比读操作慢得多,而且还要锁表,而读操作必需在写操作前进入数据库操作,而又要晚于写操作更新缓存,所有的这些条件都具备的概率基本并不大。
那么如何解决上面case呢?
最简单的方式就是给缓存设置有效时间。

还有一个很重要的问题就是,如果删除缓存失败咋办,这必然会引发不一致。可以提供一个保障的重试机制即可解决删除缓存失败的问题
可以使用数据库的binlog订阅方式:
在这里插入图片描述
流程如下:

  1. 更新数据库数据
  2. 数据库会将操作信息写入binlog日志当中
  3. 订阅程序提取出所需要的数据以及key
  4. 另起一段非业务代码,获得该信息
  5. 尝试删除缓存操作,发现删除失败
  6. 将这些信息发送至消息队列
  7. 重新从消息队列中获得该数据,重试操作。
相关推荐
©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页