博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
缓存的Cache Aside模式
阅读量:6835 次
发布时间:2019-06-26

本文共 5935 字,大约阅读时间需要 19 分钟。

本文主要讲述下缓存的Cache Aside模式。

Cache Aside

有两个要点:

  • 应用程序先从cache取数据,没有得到,则从数据库中取数据,成功后,放到缓存中。

  • 更新是先更新数据库,成功后,让缓存失效.为什么不是写完数据库后更新缓存?主要是怕两个并发的写操作导致脏数据。

public V read(K key) {  V result = cache.getIfPresent(key);  if (result == null) {    result = readFromDatabase(key);    cache.put(key, result);  }  return result;}public void write(K key, V value) {  writeToDatabase(key, value);  cache.invalidate(key);};

脏数据

一个是读操作,但是没有命中缓存,然后就到数据库中取数据,此时来了一个写操作,写完数据库后,让缓存失效,然后,之前的那个读操作再把老的数据放进去,所以,会造成脏数据。

这个case理论上会出现,不过,实际上出现的概率可能非常低,因为这个条件需要发生在读缓存时缓存失效,而且并发着有一个写操作。而实际上数据库的写操作会比读操作慢得多,而且还要锁表,而读操作必需在写操作前进入数据库操作,而又要晚于写操作更新缓存,所有的这些条件都具备的概率基本并不大。

maven

com.github.ben-manes.caffeine
caffeine
2.5.5
com.google.guava
guava
22.0

代码复现

这里使用代码复现一下这个脏数据场景。

  • 读操作进来,发现没有cache,则触发loading,获取数据,尚未返回

  • 写操作进来,更新数据源,invalidate缓存

  • loading获取的旧数据返回,cache里头存的是脏数据

@Test    public void testCacheDirty() throws InterruptedException, ExecutionException {        AtomicReference
db = new AtomicReference<>(1); LoadingCache
cache = CacheBuilder.newBuilder() .build( new CacheLoader
() { public Integer load(String key) throws InterruptedException { LOGGER.info("loading reading from db ..."); Integer v = db.get(); LOGGER.info("loading read from db get:{}",v); Thread.sleep(1000L); //这里1秒才返回,模拟引发脏缓存 LOGGER.info("loading Read from db return : {}",v); return v; } } ); Thread t2 = new Thread(() -> { try { Thread.sleep(500L); } catch (InterruptedException e) { e.printStackTrace(); } LOGGER.info("Writing to db ..."); db.set(2); LOGGER.info("Wrote to db"); cache.invalidate("k"); LOGGER.info("Invalidated cached"); }); t2.start(); //这里在t2 invalidate 之前 先触发cache loading //loading那里增加sleep,确保在invalidate之后,cache loading才返回 //此时返回的cache就是脏数据了 LOGGER.info("fire loading cache"); LOGGER.info("get from cache: {}",cache.get("k")); t2.join(); for(int i=0;i<3;i++){ LOGGER.info("get from cache: {}",cache.get("k")); } }

输出

15:54:05.751 [main] INFO com.example.demo.CacheTest - fire loading cache15:54:05.772 [main] INFO com.example.demo.CacheTest - loading reading from db ...15:54:05.772 [main] INFO com.example.demo.CacheTest - loading read from db get:115:54:06.253 [Thread-1] INFO com.example.demo.CacheTest - Writing to db ...15:54:06.253 [Thread-1] INFO com.example.demo.CacheTest - Wrote to db15:54:06.253 [Thread-1] INFO com.example.demo.CacheTest - Invalidated cached15:54:06.778 [main] INFO com.example.demo.CacheTest - loading Read from db return : 115:54:06.782 [main] INFO com.example.demo.CacheTest - get from cache: 115:54:06.782 [main] INFO com.example.demo.CacheTest - get from cache: 115:54:06.782 [main] INFO com.example.demo.CacheTest - get from cache: 115:54:06.782 [main] INFO com.example.demo.CacheTest - get from cache: 1

使用caffeine

@Test    public void testCacheDirty() throws InterruptedException, ExecutionException {        AtomicReference
db = new AtomicReference<>(1); com.github.benmanes.caffeine.cache.LoadingCache
cache = Caffeine.newBuilder() .build(key -> { LOGGER.info("loading reading from db ..."); Integer v = db.get(); LOGGER.info("loading read from db get:{}",v); Thread.sleep(1000L); //这里1秒才返回,模拟引发脏缓存 LOGGER.info("loading Read from db return : {}",v); return v; }); Thread t2 = new Thread(() -> { try { Thread.sleep(500L); } catch (InterruptedException e) { e.printStackTrace(); } LOGGER.info("Writing to db ..."); db.set(2); LOGGER.info("Wrote to db"); cache.invalidate("k"); LOGGER.info("Invalidated cached"); }); t2.start(); //这里在t2 invalidate 之前 先触发cache loading //loading那里增加sleep,确保在invalidate之后,cache loading才返回 //此时返回的cache就是脏数据了 LOGGER.info("fire loading cache"); LOGGER.info("get from cache: {}",cache.get("k")); t2.join(); for(int i=0;i<3;i++){ LOGGER.info("get from cache: {}",cache.get("k")); } }

输出

16:05:10.141 [main] INFO com.example.demo.CacheTest - fire loading cache16:05:10.153 [main] INFO com.example.demo.CacheTest - loading reading from db ...16:05:10.153 [main] INFO com.example.demo.CacheTest - loading read from db get:116:05:10.634 [Thread-1] INFO com.example.demo.CacheTest - Writing to db ...16:05:10.635 [Thread-1] INFO com.example.demo.CacheTest - Wrote to db16:05:11.172 [main] INFO com.example.demo.CacheTest - loading Read from db return : 116:05:11.172 [main] INFO com.example.demo.CacheTest - get from cache: 116:05:11.172 [Thread-1] INFO com.example.demo.CacheTest - Invalidated cached16:05:11.172 [main] INFO com.example.demo.CacheTest - loading reading from db ...16:05:11.172 [main] INFO com.example.demo.CacheTest - loading read from db get:216:05:12.177 [main] INFO com.example.demo.CacheTest - loading Read from db return : 216:05:12.177 [main] INFO com.example.demo.CacheTest - get from cache: 216:05:12.177 [main] INFO com.example.demo.CacheTest - get from cache: 216:05:12.177 [main] INFO com.example.demo.CacheTest - get from cache: 2

这里可以看到invalidate的时候,loading又重新触发了一次,然后脏数据就清除了

doc

转载地址:http://knxkl.baihongyu.com/

你可能感兴趣的文章
eyoucms上传不了logo,重试总是失败
查看>>
确认下眼神,这是你需要的MES软件吗?
查看>>
PTGUI全景合成软件使用教程之蒙版的使用
查看>>
虚拟机windows7及安装系统
查看>>
Altas 2.2.1 在 Ubuntu 14.04 LTS 下编译安装
查看>>
电影下载网站收集
查看>>
linux用户管理
查看>>
安装CentOS6网络配置问题
查看>>
JDK中的设计模式应用实例
查看>>
刘知远:让计算机听懂人话
查看>>
什么是DevOps?
查看>>
基于Spring AOP实现可控的请求日志保存,自定义注解
查看>>
secureCRT,永久设置,保护眼睛,配色方案
查看>>
[note]wordpress上线准备
查看>>
TFT working sequence
查看>>
Inside Cisco IOS Software Architecture(第一章,系统基础知识)
查看>>
java.lang.UnsupportedClassVersionError: Bad version number in .class file
查看>>
理解 Neutorn LBaaS - 每天5分钟玩转 OpenStack(120)
查看>>
CentOS上面的MRTG快速配置
查看>>
SCCM 2016 配置管理系列(Part3)
查看>>