在Mysql中,使用悲观锁和乐观锁的方式实现商品数减一

1.前言

在学习并发相关的时候,我们会学习到乐观锁和悲观锁;其实,这是一种思想,像悲观锁的话,直接把资源锁住;像乐观锁的话,使用的是CAS的思想 compare and swap;而且,实现分布式锁的话,Mysql也是一种实现方式,但是,只能用在并发量比较小的情况下;
我们假设要做一个秒杀系统,对秒杀10个商品(仅限于举个例子)

2.乐观锁

乐观锁的话,我们知道它的思想是CAS,那么,我们需要和什么进行比较呢?所以,为了确保有一个参考系,所以,需要设置一个版本号Version,每次对这个数据进行更改,就需要在version版本上,进行+1操作;先来建个表;

create table commodity (
id int not null auto_increment,
resource int not null comment '商品数',
version int not null comment '版本信息',
created_time datetime comment '创建时间',
updated_time datetime comment '更新时间',
PRIMARY key (id)
)ENGINE=INNODB default charset=utf8mb4;
复制代码

其中,id表示主键;resource表示剩余的库存,version表示版本号。
具体操作如下:

  • step1:获取库存和版本号 select resource, version from commodity where id = 1;
  • step2:执行业务逻辑
  • step3:更新库存 update commodity set resource = resource – 1,version = version + 1 where = id = 1 and version = oldVersion;

语句更新成功的条件:只有版本号和之前找出来的一样,才能证明这个库存没有被别人更新过,才可以更新成功;

3.悲观锁

使用悲观锁的话,就是一开始找的时候,就先获取写锁,然后,事务提交后,再把锁进行释放;
具体操作如下:

  • step1: 获取锁 select resource, version, updated_time from commodity where id = 1 for update;
  • step2: 执行业务操作,减库存;
  • step3:释放锁 commit;

这里涉及锁相关知识,where id = 1 for update

select resource, version, updated_time from commodity where id = 1 for update; // 加了写锁
select resource, version, updated_time from commodity where id = 1 lock in share mode; // 加了读锁 
复制代码

以MVCC为例子,因为,id是主键索引,是唯一索引,所以,它获取到的是行锁,因为,是唯一的,不能插入id相同的行,没有幻读问题。
如果,是普通索引不是唯一索引,那么加的是间隙锁,加间隙锁就是为了防止幻读问题;
如果,不走索引的话。那么,就是表锁,因为,不能通过索引确定相关的位置;

4.总结

乐观锁
优点:由于,检测冲突的时候,不是通过数据库的锁机制,不需要加锁和减锁过程的消耗。
缺点:加入了version字段,以空间换时间,增加了空间的使用量,并且,传输过程需要传输该字段,增加了网络传输的压力;并且,当并发量比较大的时候,version值变化频繁,会导致大量的请求失败,影响系统的可用性。并且,大量请求都请求同一行的行锁,会对数据库产生很大的写压力。所以,只适合于并发量不高的场景。
悲观锁
优点:每一行数据的访问都是独占的,不需要重试机制;
缺点:每次请求都会额外产生加锁(加一个写锁),会额外产生加锁的开销且为获取锁的请求将阻塞等待锁的获取,在高并发的情况下,容易造成大量请求阻塞,影响系统可用性。

乐观锁只需要加一次锁 (update),而悲观锁需要加锁两次,一次是该线程获取执行权力,另一次是(update)时,获取锁;
其实,不管是悲观锁还是乐观锁实现,都不适合高并发的情况;

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享