MySql系列(实战):mysql增加version字段实现乐观锁
前言
今天博主将为大家分享:MySql系列(实战):mysql增加version字段实现乐观锁!不喜勿喷,如有异议欢迎讨论!首先推荐大家去看我的MySql查询优化 和MySql系列:MySQL 之 Explain 输出分析 等系列文章!
多线程条件同行工具简述
有兴趣请查看历史文章:Java系列:多线程条件通行工具——CyclicBarrier 和 Java系列:Java 多线程条件通行工具——CountDownLatch
先说说同一个事务中使用一个乐观锁的情况:
先做查询 【查询时候把version带出来】
<select id="findByUid" parameterType="String" resultType="com.sxd.swapping.domain.GoodsStock">
select
uid uid,
version version,
sale_num saleNum,
stock stock
from
goods_stock
where
uid = #{uid}
</select>
再做更新【更新的时候判断version是不是查出来时候的version,如果是,则更新,更新时顺便version+1即可。否则不更新】
update goods_stock set stock = stock – #{buyNum}, sale_num = sale_num + #{buyNum}, version = version + 1 where uid = #{uid} and version = #{version}
实体对应数据表
/**
*
-
@Description: 低配商品库存表
-
@ClassName: GoodsStock .java
-
@author ChenYongJia
-
@Date 2019年6月13日 晚上21:23
-
@Email chen87647213@163.com
*/
@Entity
@Table
@Getter
@Setter
public class GoodsStock implements Serializable {private String goodsName;//商品名称
private String goodsPrice;//商品价格
private Long buyNum;//购买数量
private Long saleNum;//销售量
private Long stock;//商品库存 库存为-1 代表无限量库存
private Integer version;//版本号 默认初始版本为0
@Transient
private Integer threadCount;//模拟并发访问的线程数量 实际业务中不用这个字段 仅用本次测试接口使用
}
mybatis的mapper.xml
<?xml version=”1.0″ encoding=”UTF-8″ ?>
<update id="updateStock" parameterType="com.cyj.entity.GoodsStock">
update
goods_stock
set
<if test="stock != -1">
stock = stock - #{buyNum},
</if>
sale_num = sale_num + #{buyNum},
version = version + 1
where
uid = #{uid}
and
version = #{version}
</update>
<select id="findByUid" parameterType="String" resultType="com.cyj.entity.GoodsStock">
select
uid uid,
version version,
sale_num saleNum,
stock stock
from
goods_stock
where
uid = #{uid}
</select>
mybatis的mapper.java
package com.cyj.dao.mybatis;
import com.cyj.domain.GoodsStock;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
/**
*
- @Description: 低配版商品库存表dao
- @ClassName: GoodsStockMapper .java
- @author ChenYongJia
- @Date 2019年6月13日 晚上21:18
- @Email chen87647213@163.com
*/
@Mapper
public interface GoodsStockMapper {
/**
* 修改商品库存表信息
*
* @param goodsStock
* @return
*/
int updateStock(GoodsStock goodsStock);
/**
* 根据uid查询低配商品库存表
*
* @param uid
* @return
*/
GoodsStock findByUid(@Param(“uid”) String uid);
}
serviceImpl层代码
@Autowired
GoodsStockMapper mapper;
/**
* 数据库加 version 版本号
*
* 实现 数据库乐观锁
*
* 实现高并发下库存的并发控制机制
*
* 要保证事务一致性,要么都使用mybatis 要么都使用jpa
* @param map
* @param entity
* @param threadNum
* @return
*/
@Override
@Transactional
public void updateStock(Map<Integer,String> map, GoodsStock entity, Integer threadNum) {
String uid = entity.getUid();
Long buyNum = entity.getBuyNum();
String msg = "";
//判断库存是否足够
GoodsStock old = mapper.findByUid(uid);
Long stock = old.getStock();
System.out.println("线程"+threadNum+"---------->正在工作");
if (stock >= buyNum){
old.setBuyNum(buyNum);
if (mapper.updateStock(old) > 0 ){
msg = "库存扣除成功,剩余库存数量:";
}else {
msg = "库存扣除失败,剩余库存数量:";
}
Long nowStock = mapper.findByUid(uid).getStock();
msg +=nowStock;
}else {
msg = "库存不足,剩余库存数量:"+stock;
}
map.put(threadNum,msg);
}
controller层代码:
/**
* uid代表 同一时间 大家都来买这一件东西
* threadCount代表 同时会有多少人在操作
* buyNum代表 同一个人的一次购买量
* @param entity
* @return
*/
@RequestMapping(value = “/concurrentStock”,method = RequestMethod.POST)
public UniVerResponse<Map<Integer,String>> concurrentStock(@RequestBody GoodsStock entity){
UniVerResponse.checkField(entity,“uid”,“threadCount”,“buyNum”);
UniVerResponse<Map<Integer,String>> res = new UniVerResponse<>();
String uid = entity.getUid();
GoodsStock old = service.findByUid(uid);
if (old != null){
//设置一个线程安全的Map记录各个线程是否成功执行
Map<Integer,String> map = new ConcurrentHashMap<Integer, String>();
Integer threadCount = entity.getThreadCount();
//所有线程阻塞,然后统一开始
CountDownLatch begin = new CountDownLatch(1);
//主线程阻塞,直到所有分线程执行完毕
CountDownLatch end = new CountDownLatch(threadCount);
//开始多线程 begin.countDown();
for (Integer i = 0; i < threadCount; i++) {
Runnable runnable = buyGoods(map,entity,i,begin,end);
new Thread(runnable).start();
}
//多个线程都执行结束
try {
end.await();
res.beTrue(map);
} catch (InterruptedException e) {
e.printStackTrace();
res.beFalse("多线程执行失败",UniVerResponse.ERROR_BUSINESS,null);
}
}else {
res.beFalse("商品不存在",UniVerResponse.ERROR_BUSINESS,null);
}
return res;
}
//多线程的方法
public Runnable buyGoods(Map<Integer,String> map, GoodsStock entity, Integer threadNum,CountDownLatch begin,CountDownLatch end){
Runnable runnable = new Runnable() {
@Override
public void run() {
try {
System.out.println("线程"+threadNum+":--------------------->开始工作");
begin.await();
service.updateStock(map,entity,threadNum);
end.countDown();
System.out.println("线程"+threadNum+":--------------------->结束工作");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
return runnable;
}
每当同一时间比如10个人同事发送请求后,线程同行工具执行完毕时,每有一个购买成功时版本号version会+1购买失败不+1
到这里:MySql系列(实战):mysql增加version字段实现乐观锁!,分享完毕了,快去试试吧!
最后
-
更多参考精彩博文请看这里:《陈永佳的博客》
-
喜欢博主的小伙伴可以加个关注、点个赞哦,持续更新嘿嘿!
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/97817.html