图解MySQL中乐观锁扣减库存原理

目录
  • 1 基础知识
    • 1.1 共享锁与排它锁
    • 1.2 当前读与快照读
  • 2 乐观锁原理
  • 3 扣减库存原理

1 基础知识

在电商系统中扣减库存是一步非常关键的操作,例如秒杀系统中一定要防止超卖情况出现,如果商家设置了100件库存但是最后卖出1000件,这样就会产生资金损失。在扣减库存时一般使用如下语句:

udpate goods set stock = stock - #{acquire}
where sku_id = #{skuId} and stock - #{acquire} >= 0

这条语句可以保护库存资源防止超卖,我们不妨分析这条语句为什么生效。本文使用MySQL Innodb引擎进行演示,隔离级别为可重复读。

1.1 共享锁与排它锁

共享锁(share Lock)又被称为读锁,实现共享锁语句如下:

select lock in share mode

排它锁(exclusive Lock)又被称为写锁,实现排它锁语句如下:

select for update
update
delete
insert

共享锁与排它锁兼容关系如下表:

我们通过实例分析上述兼容关系,首先建一张测试表并写入测试数据:

CREATE TABLE `test_account` (
  `id` bigint(20) NOT NULL,
  `name` varchar(20) DEFAULT NULL,
  `account` bigint(20) DEFAULT NULL,
  `version` bigint(20) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

insert  into `test_account`(`id`,`name`,`account`,`version`) values (1,'A',100,1);
insert  into `test_account`(`id`,`name`,`account`,`version`) values (2,'B',200,1);
insert  into `test_account`(`id`,`name`,`account`,`version`) values (3,'C',300,1);

(1) 读读兼容

共享锁与共享锁之间兼容,在如下实例中session1在t3时刻,session2在t4时刻执行查询均可以获取预期结果:

(2) 读写互斥

共享锁与排它锁之间互斥,在如下实例中session1在t3时刻加共享锁,可以正确读取结果,但是session2在t4时刻尝试加排它锁,但是此时锁被session1占有,session2需要等待,当session1长时间不释放锁时,session2抛出锁超时异常:

(3) 写写互斥

排它锁与排它锁之间互斥,在如下实例中session1在t3时刻加排它锁,可以正确读取结果,但是session2在t4时刻尝试加排它锁,但是此时锁被session1占有,session2需要等待,当session1长时间不释放锁时,session2抛出锁超时异常:

1.2 当前读与快照读

MySQL Innodb存储引擎实现基于多版本并发控制协议MVCC,在MVCC并发控制中读操作可以分成快照读与当前读。

快照读不需要加锁,读取的是记录可见版本,有可能是历史版本。可以类比订单快照,用户下单之后商品价格发生了变化,但是订单快照不会改变。实现当前读语句如下:

select

当前读需要加锁,读取的是记录最新版本,加锁保证了在读取时,当前记录不会被其它事务修改。实现当前读语句如下:

select lock in share mode
select for update
update
delete
insert

我们通过一个实例分析快照读和当前读,session2在t4时刻修改记录并在t5时刻提交,session1在t6时刻进行了快照读,读取的是本事务开始时结果100,在t7时刻进行了当前读,读取的是记录最新版本结果101:

当前读流程是怎么样的呢?我们以update为例进行分析当前读流程:

第一次程序实例发出当前读请求,存储引擎返回满足where条件的第一条记录并加锁,程序实例再发出更新请求,存储引起操作完成响应成功。依次执行直到所有满足where条件记录执行完成为止。

这里我们做一些引申,RR级别提供了两种机制避免幻读问题:第一种方式是快照读,读取的是当前事务开启时的快照。第二种方式针对当前读,防止幻读依赖Next-Key Lock机制。

2 乐观锁原理

我们通过一个问题将上述知识整合起来:有两个线程在同一时刻执行如下语句,请问id=1这条记录account值会不会成功扣减两次?

update test_account set account = account - 100, version = version + 1
where id = 1 and version = 1

上述语句使用了乐观锁,我们知道乐观锁就是对资源进行保护的,所以答案是不会扣减两次,但是不能就此止步,需要结合第一章节知识进行进一步分析:

t2时刻session1和session2同时执行update操作,由于update会加排它锁,所以两者只能有一个成功:session1成功,session2阻塞等待排它锁释放。

t3时刻session1提交事务释放排它锁,此时session2获取到锁进行当前读,但是此时id=1记录version值已经变成了2,执行语句已经查询不到待更新数据,所以没有记录发生更新。

3 扣减库存原理

如果理解了第二章节乐观锁原理,那么扣减库存原理已经显而易见,我们假设商品只剩下1件库存,如果两个线程同时执行扣减库存,会发生超卖的情况吗?

t2时刻session1和session2同时执行updatek扣减库存,由于update会加排它锁,所以两者只能有一个成功:session1成功,session2阻塞等待排它锁释放。

t3时刻session1提交事务释放排它锁,此时session2获取到锁进行当前读,但是此时商品1库存已经变为0,已经不满足(where stock - 1 >= 0)条件,执行语句已经查询不到待更新数据,所以没有记录发生更新。

以上就是图解MySQL中乐观锁扣减库存原理的详细内容,更多关于MySQL乐观锁扣减库存的资料请关注我们其它相关文章!

(0)

相关推荐

  • 实例讲解MySQL中乐观锁和悲观锁

    数据库管理系统中并发控制的任务是确保在多个事务同时存取数据库中同一数据不破坏事务的隔离性和统一性以及数据库的统一性 乐观锁和悲观锁式并发控制主要采用的技术手段 悲观锁 在关系数据库管理系统中,悲观并发控制(悲观锁,PCC)是一种并发控制的方法.它可以阻止一个事务以影响其他用户的方式来修改数据.如果一个事务执行的操作的每行数据应用了锁,那只有当这个事务锁释放,其他事务才能够执行与该锁冲突的操作 悲观并发控制主要应用于数据争用激烈的环境,以及发生并发冲突时使用锁保护数据的成本要低于回滚事务的成本环境

  • mysql 乐观锁和悲观锁的具体使用

    目录 悲观锁介绍(百科): 1如果不采用锁,那么操作方法如下: 2使用悲观锁来实现: 补充:MySQL select…for update的Row Lock与Table Lock 乐观锁介绍: 使用举例:以MySQL InnoDB为例 悲观锁介绍(百科): 悲观锁,正如其名,它指的是对数据被外界(包括本系统当前的其他事务,以及来自外部系统的事务处理)修改持保守态度,因此,在整个数据处理过程中, 将数据处于锁定状态.悲观锁的实现,往往依靠数据库提供的锁机制(也只有数据库层提供的锁机制才能真正保证数

  • MySQL乐观锁和悲观锁具体实现

    目录 前言 锁分类 表结构 悲观锁 乐观锁 适用场景 总结 前言 对于MySQL中的乐观锁和悲观锁,可能很多的开发者还不是很熟悉,并不知道其中具体是如何实现的.本文就针对这个问题做一个实际案例演示,让你彻底明白这两种锁的区别. 锁分类 MySQL的中锁按照范围主要分为表锁.行锁和页面锁.其中myisam存储引擎只支持表锁,InnoDB不仅仅支持行锁,在一定程度上也支持表锁.按照行为可以分为共享锁(读锁).排他锁(写锁)和意向锁.按照思想分为乐观锁和悲观锁. 今天的文章演示一下实际中的乐观锁和悲观

  • mysql 悲观锁与乐观锁的理解及应用分析

    本文实例讲述了mysql 悲观锁与乐观锁.分享给大家供大家参考,具体如下: 悲观锁与乐观锁是人们定义出来的概念,你可以理解为一种思想,是处理并发资源的常用手段. 不要把他们与mysql中提供的锁机制(表锁,行锁,排他锁,共享锁)混为一谈. 一.悲观锁 顾名思义,就是对于数据的处理持悲观态度,总认为会发生并发冲突,获取和修改数据时,别人会修改数据.所以在整个数据处理过程中,需要将数据锁定. 悲观锁的实现,通常依靠数据库提供的锁机制实现,比如mysql的排他锁,select .... for upd

  • MySQL悲观锁与乐观锁的实现方案

    目录 前言 实战 1.无锁 2.悲观锁 3.乐观锁 总结 前言 悲观锁和乐观锁是用来解决并发问题的两种思想,在不同的平台有着各自的实现.例如在Java中,synchronized就可以认为是悲观锁的实现(不严谨,有锁升级的过程,升级到重量级锁才算),Atomic***原子类可以认为是乐观锁的实现. 悲观锁 具有强烈的独占和排他特性,在整个处理过程中将数据处于锁定状态,一般是通过系统的互斥量来实现.当其他线程想要获取锁时会被阻塞,直到持有锁的线程释放锁. 乐观锁 对数据的修改和访问持乐观态度,假设

  • Mysql中悲观锁与乐观锁应用介绍

    目录 1.锁 2.悲观锁 3.乐观锁 4.如何选择 1.锁 ​ 生活中:锁在我们身边无处不在,比如我出门玩去了需要把门锁上,比如我需要把钱放到保险柜里面,必须上锁以保证我财产的安全. 代码中:比如多个线程需要同时操作修改共享变量,这时需要给变量上把锁(syncronized),保证变量值是对的. 数据库表:当多个用户修改表中同一数据时,我们可以给该行数据上锁(行锁). sql脚本 CREATE TABLE `sys_user` ( `id` bigint(20) NOT NULL COMMENT

  • 图解MySQL中乐观锁扣减库存原理

    目录 1 基础知识 1.1 共享锁与排它锁 1.2 当前读与快照读 2 乐观锁原理 3 扣减库存原理 1 基础知识 在电商系统中扣减库存是一步非常关键的操作,例如秒杀系统中一定要防止超卖情况出现,如果商家设置了100件库存但是最后卖出1000件,这样就会产生资金损失.在扣减库存时一般使用如下语句: udpate goods set stock = stock - #{acquire} where sku_id = #{skuId} and stock - #{acquire} >= 0 这条语句

  • 详解MySQL中事务隔离级别的实现原理

    前言 说到数据库事务,大家脑子里一定很容易蹦出一堆事务的相关知识,如事务的ACID特性,隔离级别,解决的问题(脏读,不可重复读,幻读)等等,但是可能很少有人真正的清楚事务的这些特性又是怎么实现的,为什么要有四个隔离级别. 今天我们就先来聊聊MySQL中事务的隔离性的实现原理,后续还会继续出文章分析其他特性的实现原理. 当然MySQL博大精深,文章疏漏之处在所难免,欢迎批评指正. 说明 MySQL的事务实现逻辑是位于引擎层的,并且不是所有的引擎都支持事务的,下面的说明都是以InnoDB引擎为基准.

  • 详解MySQL中事务的持久性实现原理

    前言 说到数据库事务,大家脑子里一定很容易蹦出一堆事务的相关知识,如事务的ACID特性,隔离级别,解决的问题(脏读,不可重复读,幻读)等等,但是可能很少有人真正的清楚事务的这些特性又是怎么实现的,为什么要有四个隔离级别. 在之前的文章我们已经了解了MySQL中事务的隔离性的实现原理,今天就继续来聊一聊MySQL持久性的实现原理. 当然MySQL博大精深,文章疏漏之处在所难免,欢迎批评指正. 说明 MySQL的事务实现逻辑是位于引擎层的,并且不是所有的引擎都支持事务的,下面的说明都是以InnoDB

  • MySQL中MVCC机制的实现原理

    目录 前言 什么是当前读和快照读? MVCC的实现原理 前言 MVCC全称为Multi Version Concurrency Control,直译为多版本并发控制,是MySQL中一种并发控制的方法,他主要是为了提高数据库的读写性能,用更好的方式去处理读写冲突. 什么是当前读和快照读? 在讲述MVCC机制实现原理之前,我们先了解一下当前读和快照读 当前读: 其实也很容易理解,当前读指的就是读取的是最新的记录,读取的时候还需要保证其他事务不能修改当前记录,所以会对读取的记录进行加锁处理 例如:se

  • Mysql中事务ACID的实现原理详解

    引言 照例,我们先来一个场景~ 面试官:"知道事务的四大特性么?" 你:"懂,ACID嘛,原子性(Atomicity).一致性(Consistency).隔离性(Isolation).持久性(Durability)!" 面试官:"你们是用mysql数据库吧,能简单说说innodb中怎么实现这四大特性的么?" 你:"我只知道隔离性是怎么做的balabala~~" 面试官:"还是回去等通知吧~" OK,回到正题

  • mysql中的日期相减的天数函数

    目录 mysql日期相减的天数函数 语法 实例 例子 1 例子 2 mysql中日期相减的部分函数 两个日期相减,得到相差的分钟 两个日期相减的到相差的小时 总结 mysql日期相减的天数函数 DATEDIFF() 函数返回两个日期之间的天数. 语法 DATEDIFF(date1,date2) date1 和 date2 参数是合法的日期或日期/时间表达式. 注释:只有值的日期部分参与计算. 实例 例子 1 使用如下 SELECT 语句: SELECT DATEDIFF('2008-12-30'

  • 一文搞懂Mysql中的共享锁、排他锁、悲观锁、乐观锁及使用场景

    目录 一.常见锁类型 二.Mysql引擎介绍 三.常用引擎间的区别 四.共享锁与排他锁 五.排他锁的实际应用 六.共享锁的实际应用 七.死锁的发生 八.另一种发生死锁的情景 九.死锁的解决方式 十.意向锁和计划锁 十一.乐观锁和悲观锁 总结 一.常见锁类型 表级锁,锁定整张表 页级锁,锁定一页 行级锁,锁定一行 共享锁,也叫S锁,在MyISAM中也叫读锁 排他锁,也叫X锁,在MyISAM中也叫写锁 悲观锁,抽象性质,其实不真实存在 乐观锁,抽象性质,其实不真实存在 常见锁类型 二.Mysql引擎

  • MySQL中的悲观锁与乐观锁

    在关系型数据库中,悲观锁与乐观锁是解决资源并发场景的解决方案,接下来将详细讲解

  • MySQL中的乐观锁,悲观锁和MVCC全面解析

    前言 在数据库的实际使用过程中,我们常常会遇到不希望数据被同时写或者读的情景,例如秒杀场景下,两个请求同时读到系统还有库存1个,然后又先后把库存更新为0,这时候就会出现超卖的情况,这时候货物的实际库存和我们的记录就会对应不上了. 为了解决这种资源竞争导致的数据不一致等问题,我们需要有一种机制来进行保证数据的正确访问和修改,而在数据库中,这种机制就是数据库的并发控制.其中乐观并发控制,悲观并发控制和多版本并发控制是数据库并发控制主要采用的技术手段. 悲观并发控制 本质 维基百科:在关系数据库管理系

随机推荐