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

缓存过期之后,如果多个线程同时请求对某个数据的访问,会同时去到数据库,导致数据库瞬间负荷增高。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)



  • 6
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
上百节课详细讲解,需要的小伙伴自行百度网盘下载,链接见附件,永久有效。 课程介绍: 讲解一个真实的、复杂的大型企业级亿级高并发项目,是java架构实战课程。 通过本套课程的学习,可以积累大量架构设计经验,迈入架构师行列。 课程特色: 1、完整的大型电商详情页系统架构:不再只是关注电商详情页架构中的缓存架构部分,而是关注全链路、全流程的完整架构,对完整的架构进行设计以及开发,包括了动态渲染系统、OneService系统、前端页面、大型工程运维四个部分。 2、基于更加完整的业务架构来讲解,从最源头的商品服务、价格服务、库存服务开始,从业务数据的变更到缓存数据的生产,将整个商品详情页系统架构串联起来。虽然上游服务的业务还是做了大幅度的简化,但是业务架构更加完整,能够让同学们站在更加完整的角度来学习和理解整个架构。 3、完整的微服务架构的项目实战:微服务完整的架构中,一定是包含了微服务建模/模型设计、基础技术架构、持续交付流水线、容器部署几个环节的,而市面上已有的微服务课程,几乎很少有完全涵盖这些环节的,更不用说微服务架构的实战了。课程中,将讲解完整的微服务架构,包括基于Spring Cloud作为微服务架构的基础技术架构,基于DevOps思想与Jenkins构建持续交付流水线以及自动化测试套件,基于Docker作为容器部署和运行微服务。同时最有价值的地方在于,可以基于完全真实的亿级流量电商详情页系统的项目背景下,来实战这整套微服务架构,相当于是一个真实的微服务架构项目实战。 4、多机房部署架构下的4级缓存架构:大公司里真实的亿级流量高并发系统,都是采取了多个机房的部署架构,以实现高可用以及异地灾备。课程重点讲解,在多机房部署架构下,如何设计和实现高并发系统的4级缓存架构。 5、复杂业务场景下的多层次消息队列架构:在复杂的业务场景下,需要设计多层次的消息队列架构,包括了去重队列、优先级队列、刷数据队列等多个层次的复杂架构设计与实现。 6、后台服务的多线程并发架构设计:对于后台运行的服务,需要采用多线程并发设计大幅度提升系统的资源利用率以及吞吐量。 7、Redis集群的批量数据查询性能优化:对于分布式的Redis集群,数据在多个实例中分布式存储,如果要优化大批量数据的批量查询性能,就需要采用hash tag分片路由+mget单分批大批量读取的优化设计。 8、高可用架构设计:整套大型系统如何实现高可用架构的设计和部署?需要对整个读链路进行多级降级机制的设计,并且还需要进行基于Hystrix的依赖调用隔离 9、基础设施技术涵盖了大型系统中常用的各种技术,包括了:LVS+KeepAlived、Nginx+Lua、Twemproxy+SSDB+Redis(磁盘+内存的分布式与读写分离双KV集群)、RabbitMQ消息中间件 10、直接可以二次开发的代码:本次升级,采取了大型电商网站商品详情页系统完整的全链路架构,包括基础设施如何部署,以及整体代码架构,都是完全按照公司里来做的。虽然本次升级依然是专注于架构,而不是业务,基本还是没有包含什么业务,但是本次课程最后做完产出的架构和代码,都是可以直接拿到手,在架构里填充进你们自己的业务就可以进行二次开发的,工业价值非常高!同时强调一下,本课程不提供电商业务代码的二次开发,因为本课程几乎不包含太多的电商业务代码,主要讲解架构,所谓的代码二次开发,是对架构代码进行二次开发,在架构中填入你们自己的业务!!! 11、大公司的OneService一站式入口服务:基于商品详情页依赖数十个服务的业务特点,深入讲解了如何设计与开发大公司中常见的一站式入口服务,代理后端数十个服务,作为统一入口,打造服务闭环。 12、大型电商网站的前端页面的核心业务逻辑:完整讲解了大型电商网站的前端页面如何与后端整套系统配合的业务逻辑,包括了动态渲染系统直接渲染首屏的商品基本信息,滚屏时Ajax异步加载分段存储的商品介绍,Ajax异步调用OenService系统来加载时效性要求很高的价格、库存等数据。 13、大型电商网站的工程运维实践:在大型系统中,一定是需要对整套工程的运维流程做良好的设计的,包括了线下压测、线上压测、灰度发布、高峰期限流。
如果两个独立的Spring Boot 应用程序同时访问同一个数据库,可能出现并发问题并发问题主要包括以下几个方面: 1. 数据一致性问题:当两个应用程序同时对同一条数据进行修改时,可能导致数据不一致的情况。例如,一个应用程序进行了更新操作,而另一个应用程序也对同一条数据进行了更新,最终只有一个更新操作能够成功,可能导致数据丢失或冲突。 2. 并发读写问题:当一个应用程序正在对某个数据进行写操作时,另一个应用程序可能同时对同一条数据进行读操作,可能读取到未完成的写操作的中间状态的数据,导致数据的不准确性。 3. 竞态条件问题:当多个应用程序同时对同一个数据进行操作,并且操作的顺序和时序不确定时,可能导致意外的结果。例如,两个应用程序同时检查某个资源是否可用,并且根据结果进行操作,但由于时序问题,最终可能导致资源被重复使用或冲突。 为了避免这些并发问题,可以采取以下措施: 1. 数据库事务:使用数据库事务来保证数据的一致性和隔离性。通过对相关操作进行事务管理,可以确保在同一时间只有一个应用程序能够对数据进行修改,并且保证事务的原子性、一致性、隔离性和持久性。 2. 数据库锁机制:使用数据库的锁机制来控制并发访问。可以使用悲观锁或乐观锁来避免并发写入问题,或者使用行级锁或表级锁来保证数据的一致性和隔离性。 3. 缓存机制:使用缓存来减少对数据库并发访问。可以使用缓存来存储常用的数据,减少对数据库的读取操作,从而降低并发读取的问题。 4. 同步机制:在需要同步访问的代码块中使用同步机制,如 synchronized 关键字或 Lock 接口的实现类,确保同一时间只有一个线程能够访问关键代码块,避免并发冲突。 5. 消息队列:使用消息队列来解耦应用程序之间的数据交互。可以将需要同时访问数据库的操作放入消息队列中,由消息队列按序处理,避免并发访问数据库引起的问题。 综上所述,虽然Spring Boot应用程序可以同时访问同一个数据库,但需要采取适当的并发控制措施,以避免并发问题的发生。具体选择哪种措施取决于具体的业务需求和场景。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值