文档章节

使用Spring4.3解决缓存过期后多线程并发访问数据库的问题

孟飞阳
 孟飞阳
发布于 2017/02/16 14:29
字数 475
阅读 24
收藏 0

 

缓存过期之后,如果多个线程同时请求对某个数据的访问,会同时去到数据库,导致数据库瞬间负荷增高。Spring4.3为@Cacheable注解提供了一个新的参数“sync”(boolean类型,缺省为false),当设置它为true时,只有一个线程的请求会去到数据库,其他线程都会等待直到缓存可用。这个设置可以减少对数据库的瞬间并发访问。
不过不一定所有的缓存系统都支持这个配置。经过验证,Guava Cache是支持的。验证过程如下:

1、Guava Cache配置,参考:http://blog.csdn.net/clementad/article/details/51250472

2、创建从数据库获取数据的类和方法,该方法使用@Cacheable注解:

@Service  
public class UserServiceCacheablesImpl implements UserServiceCacheables{  
    private final static Logger logger = LoggerFactory.getLogger(UserServiceCacheablesImpl.class);  
  
    @Autowired  
    UserDAO userDAO;  
      
    @Override  
    @Cacheable(value="getPhoneNoByUserId")  
    public String getPhoneNoByUserId(int userId) {  
        logger.debug("getting data from database, userId={}", userId);  
        return userDAO.getPhoneNoByUserId(userId);  
    }  
}  

3、创建多线程并发的单元测试代码:

@RunWith(SpringRunner.class)  
@SpringBootTest  
public class AdminApplicationTests {  
    protected final Logger logger = LoggerFactory.getLogger(this.getClass());  
  
    @Autowired  
    UserServiceCacheables userServiceCacheables;  
      
    /** 
     * 多线程并发测试 
     */  
    @Test  
    public void multiThreads() throws Exception{  
        int number = 3; //线程数  
        ExecutorService executorService = Executors.newFixedThreadPool(number);  
          
        List<Future<String>> results = new ArrayList<Future<String>>();  
        int userId = 26358;  
          
        for (int i=0; i < number; i++) { //非阻塞地启动number个线程,由Future接收结果  
            Future<String> future = executorService.submit(new MyThread(userId));  
            //Thread.sleep(300);  
            results.add(future);  
        }  
          
        for(Future<String> f : results){ //从Future中获取结果,打印出来  
            String phoneNo = f.get();  
            logger.debug(phoneNo);  
        }  
    }  
      
    class MyThread implements Callable<String>{  
        int userId;  
          
        public MyThread(int userId) {  
            this.userId = userId;  
        }  
          
        @Override  
        public String call() throws Exception {  
            return userServiceCacheables.getPhoneNoByUserId(userId);  
        }  
    }  
}  


4、测试结果:
当设置3个并发线程的时候,会出现3个log:“getting data from database, userId=26358”,说明访问了3次数据库。
当修改注解如下之后,只出现一次“getting data from database, userId=26358”,说明只访问了1次数据库,另外两次命中了缓存:

@Cacheable(value="getPhoneNoByUserId", sync=true)  

 

本文转载自:http://blog.csdn.net/clementad/article/details/52452119

共有 人打赏支持
孟飞阳
粉丝 204
博文 964
码字总数 543203
作品 5
朝阳
个人站长
ASP.NET Core 中的缓存

目录 缓存的基本概念 缓存原理 缓存设计 分布式缓存 Memcache 与 Redis 的比较 缓存穿透,缓存击穿,缓存雪崩解决方案 数据一致性 使用内置 MemoryCache 使用分布式缓存 Redis 使用 Stackexc...

RoyZShare
08/14
0
0
缓存写法总结

基本写法 为了方便演示,这里使用Runtime.Cache做缓存容器,并定义个简单操作类。如下: 简单读取: 在项目中,有不少这样写法,这样写并没有错,但在并发量上来后就容易出问题。 缓存雪崩 ...

吞吞吐吐的
2017/09/26
0
0
Redis系列十:缓存雪崩、缓存穿透、缓存预热、缓存更新、缓存降级

一、缓存雪崩 缓存雪崩我们可以简单的理解为:由于原有缓存失效,新缓存未到期间(例如:我们设置缓存时采用了相同的过期时间,在同一时刻出现大面积的缓存过期),所有原本应该访问缓存的请求...

Forande
08/27
0
0
缓存相关——缓存穿透、缓存并发、缓存失效、缓存预热、缓存雪崩、缓存算法

一、缓存穿透 我们在项目中使用缓存通常都是先检查缓存中是否存在,如果存在直接返回缓存内容,如果不存在就直接查询数据库然后再缓存查询结果返回。这个时候如果我们查询的某一个数据在缓存...

空云万里晴
2016/06/16
1K
0
「架构技术专题」9种高性能高可用高并发的技术架构(5)

每一个模式描述了一个在我们周围不断重复发生的问题及该问题解决方案的核心。这样,你就能一次又一次地使用该方案而不必做重复工作。 所谓网站架构模式即为了解决大型网站面临的高并发访问、...

java进阶架构师
07/23
0
0

没有更多内容

加载失败,请刷新页面

加载更多

为什么Java大神,都在看Spring Boot和Spring Cloud的书?

如果你是一名Java开发人员,并且最近正打算学习Spring Boot和Spring Cloud框架并寻找一些关于它们的最好的书籍,那么,你今天就来对地方了。 本文,我们将讨论一些学习Spring Boot和Spring ...

Java小铺
15分钟前
2
0
springboot logback日志配置

springboot 如果不使用外部tomcat的话,日志是需要自己配置的,不然的话就只有控制台的日志,但是日志又是我们在项目上了生产环境,出问题时,检查问题的唯一途径,所以我们要配置详细的日志...

曾大大胖
15分钟前
2
0
Linux服务器集体篡改时间的方法

Red Hat 虚拟化课程RH318,中小型公司使用的话,感觉可以匹敌OpenStack。手头上有一个VMware的映像,RHEV 3.5版的,只能把系统时间调整到2016年才能使用。Red Hat的RHEV已经更新到4.1版,不过...

大别阿郎
15分钟前
1
0
Tomcat启动异常:java.lang.ClassNotFoundException

警告: Name = mysqlDataSource Property maxActive is not used in DBCP2, use maxTotal instead. maxTotal default value is 8. You have set value of "100" for "maxActive" property, wh......

hengbao5
17分钟前
1
0
GO错误的一些处理(defer,recover,panic)

package main import("fmt""errors")func main() {num := 10fmt.Printf("num的类型%T, num的值%v, num的地址%v\n", num, num, &num)num2 := new(int) //返回一个指针//num...

汤汤圆圆
25分钟前
0
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部