@Transactional
@Service
@Transactional
@Slf4j
public class OrderServiceImpl implements OrderService
{
//校验库存
Stock stock = checkStock(id);
//更新库存
updateSale(stock);
//创建订单
return createOrder(stock);
}
当然了你也可以用Spring自带的事务注解来实现悲观锁的操作,因为用了@Transactional就可以实现通过事务来控制,要么全部成功,要么全部失败,用事务时有两点需注意:
- 尽可能将MySQL执行语句往方法体后面靠,因为MySQL事务的commit语句是在第一次执行MySQL相关语句开始,一直到方法的结束。
- 设置事务的超时时间,如果不设置默认是-1是无限长。并且事务中设置的耗时timeout = 最后一个MySQL语句耗时 + 以及最后一个MySQL之前的所有耗时。
需注意:悲观锁状态下会保证商品卖出去,如果没拿到锁的线程会阻塞的等待拿锁。但是他的阻塞也会给用户带来非常不良好的体验。
4 第3版-乐观锁
我们为每个数量的已售数据配备个版本号,在Service层调用时获得用户的已售数跟对应版本号,然后更新时将已售数跟版本号同时更新。因为 MySQL在更新时会自带乐观加速机制,如果更新成功则表示抢购成功,更新失败则表示抢购失败,此时你会发现不是手速越快就一定能抢到的哦,但起码保证了不会超卖。
update 库存表 set
已售数=已售数+1,版本号=版本号+1
where 秒杀id =#{id} and 版本号 = #{version}
需注意:乐观锁状态下,由于是随机性的秒杀失败,所以可能活动结束后还会有几个没售出去的!
5 第4版-限流
最核心的超卖问题已经解决了,接下来就是各种优化手段了。在高并发请求中如果不对接口限流会对后台服务器造成极大压力,所以一般秒杀系统为了不影响其他业务会单独部署到个某个服务器上,同时还会设置好限流。
常用的限流方法有我们在 Redis 中曾经说过,主要有漏桶算法、令牌桶算法。而Google开源项目Guava中RateLimiter使用的就是令牌桶控制算法。在开发高并发系统时有三把利器用来保护系统:缓存、降级、限流
- 缓存:缓存的目的是提升系统访问速度和增大系统处理容量。
- 降级:降级是当服务器压力剧增的情况下,根据当前业务情况及流量对一些服务和页面有策略的降级,以此释放服务器资源以保证核心任务的正常运行。
- 限流:限流的目的是通过对并发访问/请求进行限速,或者对一个时间窗口内的请求进行限速来保护系统,一旦达到限制速率则可以拒绝服务、排队或等待、降级等处理。
5.1 漏桶算法
漏桶算法思路:把水比作是请求,漏桶比作是系统处理能力极限,水先进入到漏桶里,漏桶里的水按一定速率流出,当流出的速率小于流入的速率时,由于漏桶容量有限,后续进入的水直接溢出(拒绝请求),以此实现限流。
5.2 令牌桶算法
令牌桶算法原理:可以理解成医院的挂号看病,只有拿到号以后才可以进行诊病。
流程大致:
- 所有的请求在处理之前都需要拿到一个可用的令牌才会被处理。
- 根据限流大小,设置按照一定的速率往桶里添加令牌。
- 设置桶最大可容纳值,当桶满时新添加的令牌就被丢弃或者拒绝。
- 请求达到后首先要获取令牌桶中的令牌,拿着令牌才可以进行其他的业务逻辑,处理完业务逻辑之后,将令牌直接删除。
- 如果用户无法获得令牌可以选择一直阻塞等待,也可以选择设置好timeout机制。
- 令牌桶有最低限额,当桶中的令牌达到最低限额的时候,请求处理完之后将不会删除令牌,以此保证足够的限流。
工程中一般用令牌桶算法为多,一般用Google的Guava 中 RateLimiter 即可。
//创建令牌桶实例
private RateLimiter rateLimiter = RateLimiter.create(20);
// 阻塞式获得令牌才继续往下执行
rateLimiter.acquire();
// 就等3秒看是否可以获得令牌,返回Boolean值。
rateLimiter.tryAcquire(3, TimeUnit.SECONDS)
6 第5版- 细节优化
有了乐观锁跟限流,接下来再思考写细节问题。
- 秒杀要有时间范围限制的,不能再任意时刻都可以接受秒杀请求,要实行限时抢购。
- 如果有懂IT人员通过抓包获取了秒杀接口地址,在秒杀开始时,不通过按钮,直接通过脚本秒杀咋办?要实行秒杀接口隐藏。
- 每个用户单位时间内访问次数要做频率限制。
6.1 限时抢购
很简单,将秒杀商品放入Redis并设置超时,比如我们以kill + 商品id作为key,以商品id作为value,设置180秒超时。
127.0.0.1:6379> set kill1 1 EX 180
OK
加入时间校验:
public Integer createOrder(Integer id) {
//redis校验抢购时间
if(!stringRedisTemplate.hasKey("kill" + id)){
throw new RuntimeException("秒杀超时,活动已经结束啦!!!");
}
//校验库存
Stock stock = checkStock(id);
//扣库存
updateSale(stock);
//下订单
return createOrder(stock);
}
6.2 秒杀接口隐藏
- 用户秒杀前先通过getMd5方法获得一个请求秒杀URL的MD5值。
- 请求getMd5算法,Key = 商品id + 用户id,value = 商品id + 用户id + 盐 。将KV存入redis并且设置过期时间,最终返回value作为md5值。
- 用户请求秒杀URL的时候需携带MD5值,然后Service层会根据商品id + 用户id从redis中获取下对应的value,看这个value跟MD5值是否一致,绝对下一步操作。
// 根据商品id 跟 用户id生成个md5。
@Override
public String getMd5(Integer id, Integer userid) {
//检验用户的合法性
User user = userDAO.findById(userid);
if(user==null)throw new RuntimeException("用户信息不存在!");
# 最后的内容
在开头跟大家分享的时候我就说,面试我是没有做好准备的,全靠平时的积累,确实有点临时抱佛脚了,以至于我自己还是挺懊恼的。(准备好了或许可以拿个40k,没做准备只有30k+,你们懂那种感觉吗)
**如何准备面试?**
**1、前期铺垫(技术沉积)**
程序员面试其实是对于技术的一次摸底考试,你的技术牛逼,那你就是大爷。大厂对于技术的要求主要体现在:基础,原理,深入研究源码,广度,实战五个方面,也只有将原理理论结合实战才能把技术点吃透。
下面是我会看的一些资料笔记,希望能帮助大家由浅入深,由点到面的学习Java,应对大厂面试官的灵魂追问,**有需要的话就戳这里:[蓝色传送门](https://gitee.com/vip204888/java-p7)打包带走吧。**
> 这部分内容过多,小编只贴出部分内容展示给大家了,见谅见谅!
* Java程序员必看《Java开发核心笔记(华山版)》
![](https://img-blog.csdnimg.cn/img_convert/521d234d845eb0c9b3a42ff00aeacbe6.png)
* Redis学习笔记
![](https://img-blog.csdnimg.cn/img_convert/a1a297cad91562c644124d297d444c5b.png)
* Java并发编程学习笔记
四部分,详细拆分并发编程——并发编程+模式篇+应用篇+原理篇
![](https://img-blog.csdnimg.cn/img_convert/6766d89090d98c70a0293d75fe4dd4c8.png)
* Java程序员必看书籍《深入理解 ava虚拟机第3版》(pdf版)
![](https://img-blog.csdnimg.cn/img_convert/e695fb8d438a83d3f952c730c09020fa.png)
* 大厂面试必问——数据结构与算法汇集笔记
![](https://img-blog.csdnimg.cn/img_convert/ba599631339061d81fb06e870c808b31.png)
其他像Spring,SpringBoot,SpringCloud,SpringCloudAlibaba,Dubbo,Zookeeper,Kafka,RocketMQ,RabbitMQ,Netty,MySQL,Docker,K8s等等我都整理好,这里就不一一展示了。
![](https://img-blog.csdnimg.cn/img_convert/7b961b7102d59f53107bd49ed74dc4a3.png)
**2、狂刷面试题**
技术主要是体现在平时的积累实用,面试前准备两个月的时间再好好复习一遍,紧接着就可以刷面试题了,下面这些面试题都是小编精心整理的,贴给大家看看。
①大厂高频45道笔试题(智商题)
![](https://img-blog.csdnimg.cn/img_convert/fd471fb7a44d7fb1af140046d90a5583.png)
②BAT大厂面试总结(部分内容截图)
![](https://img-blog.csdnimg.cn/img_convert/974fa55c6d7b5000ac4e9a495d7d7836.png)
![](https://img-blog.csdnimg.cn/img_convert/1d0c1a0960d28c710b8ad9394605a863.png)
③面试总结
![](https://img-blog.csdnimg.cn/img_convert/cda98b7e8fab0f77103b6a7e159ce41e.png)
![](https://img-blog.csdnimg.cn/img_convert/48e405ead1754e6bb3a307058423505c.png)
**3、结合实际,修改简历**
程序员的简历一定要多下一些功夫,尤其是对一些字眼要再三斟酌,如“精通、熟悉、了解”这三者的区别一定要区分清楚,否则就是在给自己挖坑了。当然不会包装,我可以将我的简历给你参考参考,如果还不够,那下面这些简历模板任你挑选:
![](https://img-blog.csdnimg.cn/img_convert/c80633bc5857388c43f82420e13673ab.png)
以上分享,希望大家可以在金三银四跳槽季找到一份好工作,但千万也记住,技术一定是平时工作种累计或者自学(或报班跟着老师学)通过实战累计的,千万不要临时抱佛脚。
另外,面试中遇到不会的问题不妨尝试讲讲自己的思路,因为有些问题不是考察我们的编程能力,而是逻辑思维表达能力;最后平时要进行自我分析与评价,做好职业规划,不断摸索,提高自己的编程能力和抽象思维能力。
Lxc-1628300081642)]
以上分享,希望大家可以在金三银四跳槽季找到一份好工作,但千万也记住,技术一定是平时工作种累计或者自学(或报班跟着老师学)通过实战累计的,千万不要临时抱佛脚。
另外,面试中遇到不会的问题不妨尝试讲讲自己的思路,因为有些问题不是考察我们的编程能力,而是逻辑思维表达能力;最后平时要进行自我分析与评价,做好职业规划,不断摸索,提高自己的编程能力和抽象思维能力。
**以上文章中,提及到的所有的笔记内容、面试题等资料,均可以免费分享给大家学习,有需要的话就[戳这里打包带走](https://gitee.com/vip204888/java-p7)吧。**