原创 爱他就关注他---> Java分享客栈 2023-10-23 12:25 发表于湖北
收录于合集
#java95个
#秒杀2个
#乐观锁1个
点击关注公众号,Java干货及时送达👇
Java分享客栈
分享IT互联网主流技术,包括Java、SpringBoot、SpringCloud-alibaba、Redis缓存、MQ队列、网络编程、websocket通信、netty、docker、k8s等技术及多年工作经验分享和感悟。
96篇原创内容
公众号
最近脖子后颈里面长了东西,正在医院检查,想必是熬夜太多导致的,具体情况还不清楚。
再次呼吁大家,保护好自己的身体,程序员的累不是传统意义上的累,有时候随着年纪增长会体现在心理上。
希望大家都顺顺利利,健健康康的。
更新可能会没那么准时了,希望大家谅解并继续支持哈。
前言
首先要明确一点,那就是解决超卖问题最直接且最稳妥的方式就是数据库,而非代码控制,代码中的逻辑很难去控制这种场景。
只有利用数据库的锁机制和唯一索引才能彻底保证不出现超卖。
场景解释
举两个例子解释下高并发场景:
1)、秒杀过程中,库存有10个商品,当还剩下1个的时候,同时有2个请求访问者1个商品,结果导致数据库中的库存变成-1;
2)、秒杀过程中,1个用户同时发出2个请求,结果导致秒杀订单生成2个,等于说1个用户秒杀到了2个商品。
方案提供
两种方式:
1、使用数据库自带行锁机制;
2、使用version版本号实现乐观锁。
第1种方式属于悲观锁,其实完全可以解决,性能也不是无法接受,但从技术的角度上而言,还是推荐第2种方式,一来性能更高,二来不会被击穿库存。
比如:200个请求访问100个库存,大概会秒杀成功60多个或70多个,还剩余一小部分库存可供用户重试,容错率更高。
方案说明
1、行锁方式
这种方式就很简单了,只要在mapper中的语句加上 库存 > 0 的条件判断即可,这样就能触发数据库行锁机制。
示例:
update meite_seckill set inventory=inventory - 1 where seckill_id = #{seckillId} and inventory > 0;
使用数据库自带的行锁机制,假如有200个请求访问100个库存,那么会有100个秒杀成功。
2、version方式
这种方式的原理就是在数据库中,针对秒杀库存业务关联的表中新增一个version字段。
乐观锁通常有以下几个步骤:
1) 在数据库表中添加一个用于记录版本号或时间戳的字段;
2) 当事务要读取数据时,会获取数据的版本号或时间戳并保存下来;
3) 当事务要更新数据时,会检查该数据的版本号或时间戳是否与之前保存的一致。
如果一致,则表示没有其他事务对该数据进行过修改,可以继续执行更新操作,并将版本号或时间戳加一。
如果不一致,则表示有其他事务对该数据进行了修改,当前事务需要根据业务逻辑进行相应的处理,如放弃操作或重新尝试。
示例:
update meite_seckill set inventory = inventory - 1, version = version + 1 where seckill_id = #{seckillId} and inventory > 0 and version = #{version};
使用数据库自带的行锁机制,假如有200个请求访问100个库存,那么可能会有60-70个秒杀成功,这就极大的增加了容错率。
总结
行锁机制和乐观锁机制各有优劣,适用于不同的场景和需求:
-
行锁机制适合对并发控制要求严格的场景,可以避免并发修改数据造成的问题,但可能导致性能下降。
-
版本号的乐观锁机制适用于并发量较高、冲突概率较小的场景,可以在保证一致性的同时提高系统的并发性能。
如何使用还是要根据具体业务场景来决定,但还是以前提过的那句话,类似问题的处理方式学习大厂的经验即可,那就是结合使用。
好了,今天的小知识,你学会了吗?