当前位置:网站首页>【Redis实现秒杀业务③】超卖问题之乐观锁具体实现
【Redis实现秒杀业务③】超卖问题之乐观锁具体实现
2022-06-24 20:01:00 【步尔斯特】
版本号法
版本号机制实现的方式常用的也有两种:
使用数据版本(Version)记录机制实现,这是乐观锁最常用的一种实现方式。何谓数据版本?即为数据增加一个版本标识,一般是通过为数据库表增加一个数字类型的 “version” 字段来实现。当读取数据时,将version字段的值一同读出,数据每更新一次,对此version值加一。
当我们提交更新的时候,判断数据库表对应记录 的当前版本信息与第一次取出来的version值进行比对,如果数据库表当前版本号与第一次取出来的version值相等,则予以更新,否则认为是过期数据。
如果更新操作顺序执行,则数据的版本(version)依次递增,不会产生冲突。但是如果发生有不同的业务操作对同一版本的数据进行修 改,那么,先提交的操作会把数据version更新为2,当A在B之后提交更新时发现数据的version已经被修改了,那么A的更新操作会失败。使用时间戳(timestamp)。这种实现方式和第一种差不多,同样是在需要乐观锁控制的table中增加一个字段,名称无所谓,字段类型使用时间戳(timestamp), 和上面的version类似,也是在更新提交的时候检查当前数据库中数据的时间戳和自己更新前取到的时间戳进行对比,如果一致则OK,否则就是版本冲突。

CAS算法
CAS即Compare And Swap(比较与交换),是一种有名的无锁算法。无锁编程,即不使用锁的情况下实现多线程之间的变量同步,也就是在没有线程被阻塞的情况下实现变量的同步,所以也叫非阻塞同步(Non-blocking Synchronization)。
CAS操作包含三个操作数——内存位置的值(V)、预期原值(A)和新值(B)。执行CAS操作的时候,将内存位置的值与预期原值比较,如果相匹配,那么处理器会自动将该位置值更新为新值,否则,处理器不做任何操作。
通过乐观锁+自旋的方式,解决数据更新的线程安全问题,而且锁粒度比互斥锁低,并发性能好。
循环实现:
public void addCount(String goodsId, Integer count) {
while(true) {
Goods goods = dao.selectByGoodsId(goodsId);
if (goods == null) {
throw new Execption("数据不存在");
}
int count = goods.getCount() + count;
goods.setCount(count);
int count = dao.updateCount(goods);
if (count > 0) {
return;
}
}
}
递归实现:
public void addCount(String goodsId, Integer count) {
Goods goods = dao.selectByGoodsId(goodsId);
if (goods == null) {
throw new Execption("数据不存在");
}
int count = goods.getCount() + count;
goods.setCount(count);
int count = dao.updateCount(goods);
if (count == 0) {
addCount(goodsId, count)
}
}
CAS算法的缺点
【1】循环时间长开销很大:自旋 CAS 如果长时间不成功,会给 CPU 带来非常大的执行开销。
【2】只能保证一个共享变量的原子操作:只能保证一个共享变量的原子操作。当对一个共享变量执行操作时,我们可以使用循环 CAS的方式来保证原子操作,但是对多个共享变量操作时,循环 CAS就无法保证操作的原子性,这个时候就可以用锁,或者有一个取巧的办法,就是把多个共享变量合并成一个共享变量来操作。比如有两个共享变量 i=2,j=a,合并一下 ij=2a,然后用CAS 来操作 ij。从 Java1.5 开始 JDK 提供了 AtomicReference类来保证引用对象之间的原子性,你可以把多个变量放在一个对象里来进行 CAS 操作。
【3】ABA 问题:因为 CAS需要在操作值的时候检查下值有没有发生变化,如果没有发生变化则更新,但是如果一个值原来是A,变成了B,又变成了A,那么使用 CAS进行检查时会发现它的值没有发生变化,但是实际上却变化了。ABA问题的解决思路就是使用版本号。在变量前面追加上版本号,每次变量更新的时候把版本号加1,那么A-B-A 就会变成1A-2B-3A。
图解

代码实现
我们可以通过如下这种方式进行处理,如果数量和自己查到的数量相等,就购买
// 如果数量和自己查到的数量相等,就购买
boolean updateBoolean = this.update().setSql("count = count - 1")
.eq("id", id).eq("count", fruits.getId()).update();
if (!updateBoolean) {
return ResponseEntity.status(400).body("水果卖光了,下次再来吧!");
}
但是这样会让其他本应该可以购买的用户买不了,所以改动如下
// 如果数量和自己查到的数量相等,就购买
// boolean updateBoolean = this.update().setSql("count = count - 1")
// .eq("id", id).eq("count", fruits.getId()).update();
// 如果剩余数量大于0,就可以购买
boolean updateBoolean = this.update().setSql("count = count - 1")
.eq("id", id).gt("count", 0).update();
if (!updateBoolean) {
return ResponseEntity.status(400).body("水果卖光了,下次再来吧!");
}
边栏推荐
- Apk slimming compression experience
- What is the difference between one way and two way ANOVA analysis, and how to use SPSS or prism for statistical analysis
- 无需显示屏的VNC Viewer远程连接树莓派
- Decoupling pages and components using lifecycle
- Solution to network access packet loss of Tencent cloud international ECS
- Go crawler framework -colly actual combat (III) -- panoramic cartoon picture capture and download
- Why are life science enterprises on the cloud in succession?
- 2021-11-05
- UE4 WebBrowser chart cannot display problems
- Several ways for wechat applet to jump to the page are worth collecting
猜你喜欢

Im instant messaging development application keeping alive process anti kill
WordPress add photo album function [advanced custom fields Pro custom fields plug-in series tutorial]

The third generation of power electronics semiconductors: SiC MOSFET learning notes (V) research on driving power supply

Technologie des fenêtres coulissantes en octets dans la couche de transmission

Collective example

On the difficulty of developing large im instant messaging system

融合模型权限管理设计方案

The new employee of the Department after 00 is really a champion. He has worked for less than two years. The starting salary of 18K is close to me when he changes to our company

Ansible及playbook的相关操作
@mysql
随机推荐
WordPress add photo album function [advanced custom fields Pro custom fields plug-in series tutorial]
[proteus simulation] example of using timer 0 as a 16 bit counter
Creative SVG ring clock JS effect
Do280openshift access control -- encryption and configmap
无需显示屏的VNC Viewer远程连接树莓派
UE4 WebBrowser chart cannot display problems
Apk slimming compression experience
JDBC —— 数据库连接
ros(24):error: invalid initialization of reference of type ‘xx’ from expression of type ‘xx’
[Solved] Public key for mysql-community-xxx. rpm is not installed
C WinForm maximizes occlusion of the taskbar and full screen display
Garbage collection of C closure
Activity startup process
Transition from digitalization to intelligent manufacturing
In the process of enterprise development, I found that a colleague used the select * from where condition for update
The new employee of the Department after 00 is really a champion. He has worked for less than two years. The starting salary of 18K is close to me when he changes to our company
iNFTnews | 国内NFT发展仅限于数字藏品吗?
Meta&伯克利基于池化自注意力机制提出通用多尺度视觉Transformer,在ImageNet分类准确率达88.8%!开源...
Technology sharing | wvp+zlmediakit realizes streaming playback of camera gb28181
Requests Library