Springboot解决业务并发问题

本文介绍了如何在SpringBoot中解决并发问题,主要探讨了两种解决方案:一是使用`selectForUpdate`配合`@Transactional`实现悲观锁,确保同一时间段只有一个预约请求能成功;二是通过更新数据库状态(update+@Transactional)实现类似乐观锁的效果,通过检查更新是否成功来避免并发冲突。这两种方法都需要深入理解SpringBoot的事务管理和MySQL的锁机制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、业务需求(大家有类似的也可以参考我的解决方案)

实现一种类似于医院预约的预约功能,在同一个时间段,可能大家都看得到这个预约的按钮,大家都点进去了,这个时候真正提交预约信息的时候,就会可能出现多个请求同一个时间段的预约,在service层中,有可能会造成并发问题:几个线程可能都读到时间段的status(数据库中字段)为1,大家都能提交预约了

二、解决方案

方案一:select for update + @Transactional

在对应的service层方法中加上这个注解,手动开启事务,这会导致我们这个方法全部执行完毕之后,才会进行事务的提交,不然则不会提交事务,途中如果有异常,则会rollback,出现状态码500

select for update 这句话会将数据库查到的数据进行加上悲观锁(查到多少就加多少行的锁,会导致更大的问题,大家请精确到一行数据进行加锁),加上锁之后,其他线程想要执行这条语句的时候就会被阻塞的

什么时候会释放锁呢?要等到我们这个方法完成了,事务提交了就会进行释放锁(正常情况下)

这里简单跟大家说select for update加的行级锁知识和该注解的知识

select for update:

for update是一种行级锁,又叫排它锁,一旦用户对某个行施加了行级加锁,则该用户可以查询也可以更新被加锁的数据行,其它用户只能查询但不能更新被加锁的数据行.如果其它用户想更新该表中的数据行,则也必须对该表施加行级锁.即使多个用户对一个表均使用了共享更新,但也不允许两个事务同时对一个表进行更新,真正对表进行更新时,是以独占方式锁表,一直到提交或复原该事务为止。行锁永远是独占方式锁。

只有当出现如下之一的条件,才会释放共享更新锁:
1、执行提交(COMMIT)语句
2、退出数据库(LOG OFF)
3、程序停止运行

@Transactional:

实现原理:
 
1) 事务开始时,通过AOP机制,生成一个代理connection对象,
   并将其放入 DataSource 实例的某个与 DataSourceTransactionManager 相关的某处容器中。
   在接下来的整个事务中,客户代码都应该使用该 connection 连接数据库,
   执行所有数据库命令。
   [不使用该 connection 连接数据库执行的数据库命令,在本事务回滚的时候得不到回滚]
  (物理连接 connection 逻辑上新建一个会话session;
   DataSource 与 TransactionManager 配置相同的数据源)
 
2) 事务结束时,回滚在第1步骤中得到的代理 connection 对象上执行的数据库命令,
   然后关闭该代理 connection 对象。
  (事务结束后,回滚操作不会对已执行完毕的SQL操作命令起作用)

方案二(方案一改进版):update + @Transactional(update数据库表中时间段的status)

 该方法用update直接上独占锁(悲观锁),对比上一个方案,我们的范围缩小了

update完了之后可以返回数字1 or 0,如果1则表示更新成功,0则表示更新失败

0则返回controller,表示提交预约失败,以此来防止我们的并发问题

三、总结

1、得进行学习springboot得一些注解

2、学习进阶mysql锁的知识

3、有时候不进行正式的公司开发流程,真的不知道高级并发处理该咋搞

4、准备学习一下MybatisPlus

5、方案二的做法对比与方案一其实像乐观锁,大家可以细细品一下

6、update完status之后如果update成功的话就会submit我们的预约

### 处理Spring Boot中的高并发问题 #### 优化数据库访问 为了应对高并发场景下的性能挑战,在应用程序层面可以通过多种策略来提高效率。对于数据读取操作频繁的应用程序来说,采用缓存技术能够显著减少直接查询数据库的压力。例如Redis作为分布式键值存储系统,非常适合用来做热点数据的缓存[^1]。 ```java @Cacheable(value="items", key="#id") public Item getItemById(Long id){ return itemRepository.findOne(id); } ``` #### 调整线程池参数 合理配置异步任务执行器(TaskExecutor),根据业务特点调整核心线程数、最大线程数以及队列容量等属性,从而更好地控制资源分配并提升响应速度。此外还可以考虑启用拒绝策略以防止过载情况发生。 ```properties spring.task.execution.pool.core-size=20 spring.task.execution.pool.max-size=50 spring.task.execution.pool.queue-capacity=100 spring.task.execution.thread-name-prefix=Async- spring.task.rejection-policy=CALLER_RUNS ``` #### 实现限流措施 当面对突发流量冲击时,除了依靠硬件扩展外软件层面上也需要采取相应对策。比如利用Hystrix组件实现熔断机制,一旦检测到下游服务不可达则立即返回错误提示而不是继续等待超时;或者是基于令牌桶算法对API接口调用量加以限制,确保不会因为单个恶意请求而拖垮整个平台的服务质量。 ```yaml hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds: 2000 ribbon.ReadTimeout: 3000 ribbon.ConnectTimeout: 1000 ``` #### 异步处理与消息队列 引入Kafka这样的消息中间件可以帮助解耦生产者消费者之间的关系,使得两者不必同步工作而是各自按照自己的节奏发送接收信息。特别是在订单创建这类耗时较长的操作中尤为适用,它允许前端快速反馈给用户成功状态的同时后台慢慢完成后续逻辑处理过程[^2]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值