数据库:mysql

数据库的乐观锁:一般通过数据表加version来实现,相对于悲观锁的话,更能省数据库性能,废话不多说,直接看代码

第一步:

建立数据库表:

CREATE TABLE `skill_activity` (

`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '活动id',

`name` varchar(20) NOT NULL COMMENT '活动名称',

`num` bigint(10) NOT NULL COMMENT '活动数量限制',

`surplus_num` bigint(10) NOT NULL COMMENT '活动剩余数量',

`person_limit` bigint(10) NOT NULL COMMENT '单人上传限制',

`version` bigint(10) DEFAULT '0',

PRIMARY KEY (`id`)

) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;

CREATE TABLE `skill_activity_order` ( `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键',

`activity_id` bigint(20) NOT NULL COMMENT '活动id',

`thread_id` bigint(20) NOT NULL COMMENT '线程id',

`create_at` datetime DEFAULT NULL COMMENT '创建时间',

`name` varchar(20) NOT NULL COMMENT '活动名称',

`url` varchar(20) DEFAULT NULL, PRIMARY KEY (`id`),

KEY `index_thread_id` (`thread_id`) ) ENGINE=InnoDB AUTO_INCREMENT=106338 DEFAULT CHARSET=utf8

往数据库活动表(skill_activity)插入一条数据

num:商品数量;surplus_num:商品剩余数量;person_limit:单人上传数量限制;version:版本号,解决高并发问题

具体的活动秒杀订单表(skill_activity_order):

activity_id:就是上面活动表的id;thread_id:就是线程id,实际秒杀就是用户id,name和url就是秒杀填写的一些内容,不必关注

第二步:

java代码:

1)2个表对应的mapper.java类

public interfaceSkillActivityMapper {intdeleteByPrimaryKey(Long id);intinsert(SkillActivity record);intinsertSelective(SkillActivity record);

SkillActivity selectByPrimaryKey(Long id);intupdateByPrimaryKeySelective(SkillActivity record);intupdateByPrimaryKey(SkillActivity record);intupdateSkillActivityNum(SkillActivity record);//秒杀修改剩余数量的方法

}

public interface SkillActivityOrderMapper {

int deleteByPrimaryKey(Long id);

int insert(SkillActivityOrder record);

int insertSelective(SkillActivityOrder record);

SkillActivityOrder selectByPrimaryKey(Long id);

List selectBythreadId(Long threadId);

int updateByPrimaryKeySelective(SkillActivityOrder record);

int updateByPrimaryKey(SkillActivityOrder record);

}

2)2个类对应的mapper.xml方法就不一一写出来了,看SkillActivityMapper的updateSkillActivityNum这个方法的sql语句:

update skill_activity

set

surplus_num= #{surplusNum,jdbcType=BIGINT},

version= #{version,jdbcType=BIGINT}+1where id= #{id,jdbcType=BIGINT} and version=#{version,jdbcType=BIGINT} and surplus_num>0

还有SkillActivityOrderMapper.selectBythreadId方法

selectfrom skill_activity_order

where thread_id= #{threadId,jdbcType=BIGINT}

3)version版本号是关键,update成功会导致version加1,而其他线程如果是原先的version就无法update。

4)看一下service代码:

@OverridepublicSkillActivityResponse SkillActivity(SkillActivirtReq req) {

SkillActivityResponse skillActivityResponse=newSkillActivityResponse();int failNum=0;

SkillActivity skillActivity=skillActivityMapper.selectByPrimaryKey(req.getActivityId());

List urls=req.getUrls();if(skillActivity.getSurplusNum()<=0){

skillActivityResponse.setErrorMsg("活动已经结束");

skillActivityResponse.setFailNum(urls.size());

skillActivityResponse.setSucceed(false);returnskillActivityResponse;

}else{//先查询用户上传了多少张

int count=skillActivityOrderMapper.selectBythreadId(req.getThreadId()).size();//查询每个用户上传了多少张if(count>skillActivity.getPersonLimit()){

skillActivityResponse.setErrorMsg("已经超出上传上限,上传失败");

skillActivityResponse.setFailNum(urls.size());

skillActivityResponse.setSucceed(false);returnskillActivityResponse;

}int index=(int) (skillActivity.getPersonLimit()-count);//表示还能上传的数量

if(urls.size()<=index){//都可以上传

}else{//表示只能上传index张图片

urls=urls.subList(0, index);

}//上传订单

for(int i=0;i

skillActivity=skillActivityMapper.selectByPrimaryKey(req.getActivityId());

skillActivity.setSurplusNum(skillActivity.getSurplusNum()-1);if(skillActivity.getSurplusNum()<0){

failNum++;continue;

}int result=skillActivityMapper.updateSkillActivityNum(skillActivity);//这个是关键if(result>0){//上传成功

SkillActivityOrder activityOrder=newSkillActivityOrder();

activityOrder.setActivityId(skillActivity.getId());

activityOrder.setCreateAt(newDate());

activityOrder.setName(skillActivity.getName());

activityOrder.setThreadId(req.getThreadId());

activityOrder.setUrl(urls.get(i));

skillActivityOrderMapper.insertSelective(activityOrder);

}else{//上传失败

failNum++;

}

}

skillActivityResponse.setFailNum(failNum);

skillActivityResponse.setSucceed(true);returnskillActivityResponse;

}

}

5)使用spring的junit来单元测试

@RunWith(SpringJUnit4ClassRunner.class)

@ContextConfiguration(locations={"classpath:spring/applicationContext.xml"})public classSkillActivityServieTest {

@AutowiredprivateSkillActivityService skillActivityService;class MyRun implementsRunnable{privateCyclicBarrier barrier;privateCountDownLatch countDownLatch;privateLong threadId;publicMyRun(CyclicBarrier barrier, CountDownLatch countDownLatch,

Long threadId) {super();this.barrier =barrier;this.countDownLatch =countDownLatch;this.threadId =threadId;

}

@Overridepublic voidrun() {

System.err.println("线程"+threadId+"准备完毕");try{

barrier.await();

SkillActivirtReq req=newSkillActivirtReq();

req.setActivityId(1L);

req.setThreadId(threadId);

req.setUrls(Lists.newArrayList("url1","url2","url3","url4","url5","url6","url7","url7","url8","url9","url10"));

SkillActivityResponse skillActivityResponse=skillActivityService.SkillActivity(req);if(skillActivityResponse.isSucceed()){

System.err.println("线程:"+threadId+",failNum:"+skillActivityResponse.getFailNum());

}else{

System.err.println("线程:"+threadId+",errmsg:"+skillActivityResponse.getErrorMsg());

}

}catch (InterruptedException |BrokenBarrierException e) {//TODO Auto-generated catch block

e.printStackTrace();

}finally{

countDownLatch.countDown();

}

}

}

@Testpublic void test01() throwsInterruptedException{

CyclicBarrier barrier= new CyclicBarrier(20000);//让20000个线程同时进行操作 调用20000次方法await() 才会让20000个线程同时执行

CountDownLatch countDownLatch=new CountDownLatch(20000);//统计耗时

ExecutorService executorService=Executors.newCachedThreadPool();long start=System.currentTimeMillis();for(int i=1;i<=20000;i++){

executorService.submit(new MyRun(barrier, countDownLatch, new Long(i+"")));

}

executorService.shutdown();

countDownLatch.await();

System.err.println("耗时:"+(System.currentTimeMillis()-start)+"ms");try{

Thread.sleep(Integer.MAX_VALUE); //防止线程结束

}catch(InterruptedException e) {

e.printStackTrace();

}

}

}

6)运行junit

会看见20000线程同时准备完毕后才会同时去秒杀商品,这个就是CyclicBarrier的作用

然后可以看见耗时:160945ms也就是160秒多一点

7)看看数据库表:

surplus_num的数量:0,version:2000,在多运行就几次也是这个结果,通过使用数据库的乐观锁来实现高并发下的秒杀

总结:数据库的乐观锁一般使用version版本号结合业务来实现,CyclicBarrier和CountDownLatch也是高并发下常用的工具类,CyclicBarrier的作用:就是让多个线程同时去操作,

CountDownLatch一般可以用来统计总耗时,由于作者水平有限,如有不足请见谅.

mysql乐观锁 秒杀_使用数据库乐观锁解决高并发秒杀问题,以及如何模拟高并发的场景,CyclicBarrier和CountDownLatch类的用法...相关推荐

  1. MySQL数据库的锁(什么是数据库的锁?什么是乐观锁和悲观锁?什么是死锁?如何避免?)

    数据库的锁 什么是数据库的锁? 数据库的锁与隔离级别的关系? 数据库锁的类型有哪些? MySQL中InnoDB引擎的行锁模式及其是如何实现的? 什么是数据库的乐观锁和悲观锁,如何实现? 什么是死锁?如 ...

  2. mysql和oracle的锁_关于数据库行锁与表锁的认识

    MySQL MySQL(InnoDB存储引擎)默认是自动提交事务的,所以这个测试,需要先将MySQL的autocommit设置为0,关闭自动提交,需要自己手动提交事务 -- 关闭自动提交 set au ...

  3. MySQL 锁与MVCC :数据库的锁、MVCC、当前读、快照读、锁算法、死锁

    文章目录 lock与latch 锁的类型 MVCC 一致性非锁定读(快照读) 一致性锁定读(当前读) 锁算法 死锁 锁升级 lock与latch 在了解数据库锁之前,首先就要区分开lock和latch ...

  4. 【MySQL 第17章_其他数据库日志】

    第17章_其他数据库日志 1.MySQL支持的日志 1.1日志类型 1.2日志的弊端 2.慢查询日志(slow query log) 3.1问题场景 3.2 查看当前状态 3.3 启动日志 3.4 查 ...

  5. mysql导出数据意义_导出数据库的意义

    {"moduleinfo":{"card_count":[{"count_phone":1,"count":1}],&q ...

  6. 创建mysql制定字符集语句_创建数据库指定字符集语句

    {"moduleinfo":{"card_count":[{"count_phone":1,"count":1}],&q ...

  7. mysql导入 内存溢出_导入数据库内存溢出

    {"moduleinfo":{"card_count":[{"count_phone":1,"count":1}],&q ...

  8. 阿里mysql表命名规范_阿里数据库命名规范

    {"moduleinfo":{"card_count":[{"count_phone":1,"count":1}],&q ...

  9. mysql 二进制转字符串_将数据库的二进制转换为字符串

    {"moduleinfo":{"card_count":[{"count_phone":1,"count":1}],&q ...

最新文章

  1. 设计模式之美:Adapter(适配器)
  2. STM32 RTC BKP备份数据区数据丢失问题的讨论
  3. 基于R-CNN的物体检测-CVPR 2014
  4. spring容器注入一个接口的两个实现类
  5. python绘制折线图显示数据_漂亮图表也可用python信手拈来!一文教你学会用Python绘制堆积折线图...
  6. 让sky Driver成为你的可见硬盘
  7. AN EMPIRICAL STUDY OF EXAMPLE FORGETTING DURING DEEP NEURAL NETWORK LEARNING 论文笔记
  8. 敏捷方法开发总结的点评记录
  9. egon说一切皆对象--------面向对象进阶紫禁之巅
  10. 【激活函数】深度学习领域最常用的10个激活函数,一文详解数学原理及优缺点...
  11. SQL Server 2012 查看数据库属性
  12. 利用python selenium+cv2破解qq空间登录滑动验证码(无法获得完整图情况)(上)
  13. base64深入理解与判断、base32、base16
  14. learning ddr tRP and tRP tRTP CL tRAS
  15. 为什么会“道可道非常道,名可名非常名”?
  16. 外汇基础知识学习3--平仓
  17. mysql运行语句时出现 FUNCTION *** does not exist
  18. 《精益数据分析》-第二部分概括笔记
  19. css网页网格线条背景
  20. 工作常用的工具类JS+reset.css

热门文章

  1. DeepMind新AI可生成逼真视频
  2. 我又被当当骗了!!!
  3. 最不该减负的,是孩子
  4. 120W快充!Redmi Note 11系列1199元起
  5. NUCLEO-F767ZI以太网功能实现笔记本电脑不开盖开机
  6. 生活像一把无情的刻刀,让人情何以堪
  7. Java小程序——模仿Win系统画板
  8. C语言人机大战之三字棋博弈
  9. 函数计算FC助力游戏群采集营销数据滴水不漏
  10. phpgif图片包_php实现处理动态GIF图片和GIF动画的