在秒杀系统设计中,超卖是一个经典、常见的问题,任何商品都会有数量上限,如何避免成功下订单买到商品的人数不超过商品数量的上限,这是每个抢购活动都要面临的难点。

1 超卖问题描述

在多个用户同时发起对同一个商品的下单请求时,先查询商品库存,再修改商品库存,会出现资源竞争问题,导致库存的最终结果出现异常。

问题:当商品A一共有库存15件,用户甲先下单10件,用户乙下单8件,这时候库存只能满足一个人下单成功,如果两个人同时提交,就出现了超卖的问题。



可以采用多种方式解决超卖问题。使用synchronized可以保证数据一致性,但是效率低,并且分布式环境下无用;使用数据库锁表会造成数据库性能低下。单体条件下,采用乐观锁是比较合适的方式,集群可以考虑分布式锁

2 乐观锁

2.1 乐观锁介绍

悲观锁,认为数据很容易被其他线程修改,为保证数据正确性,每次获取并修改数据时,对数据加锁。例如Java中的synchronized和Lock相关类。

而乐观锁,认为自己在操作时不会有其他线程干扰,所以不对被操作对象加锁。在更新时会判断修改期间是否有其他线程修改过。如果没被修改过,则表示只有当前线程在操作,正常修改数据。如果数据被其他线程修改过,则会停止刚才的更新,选择执行策略,例如抛弃、报错、重试等。

乐观锁一般使用CAS算法实现。例如Java中的原子类、并发容器。

2.2 没有锁的更新操作

乐观锁,不是数据库功能,是一种数据库实践。假设进行以下操作:从表中获取某行数据,计算数据,更新数据该行数据。

CREATE TABLE theTable(
iD int NOT NULL,
val1 int NOT NULL,
val2 int NOT NULL
)
INSERT INTO theTable (iD, val1, val2) VALUES (1, 2 ,3);

没有锁的处理

-- 查询数据
SELECT iD, val1, val2
FROM theTable
WHERE iD = @theId;
-- 计算新值
-- 更新数据
UPDATE
theTable
SET
val1 = @newVal1,
val2 = @newVal2
WHERE
iD = @theId;
-- 继续执行

2.3 乐观锁的实现方式1--条件控制

--查询数据
SELECT iD, val1, val2
FROM theTable
WHERE iD = @theId;
--计算新值
--更新数据
UPDATE
theTable
SET
val1 = @newVal1,
val2 = @newVal2
WHERE
iD = @theId
AND val1 = @oldVal1
AND val2 = @oldVal2;
--判断影响行数
-- {if AffectedRows == 1 }
-- {继续执行}
-- {else}
-- {数据过期}
-- {endif}

上面操作的关键在于,UPDATE指令的结构与后续受影响的行数检查,从而判断是否有人修改数据。上面所有操作没有使用事务,这也表明乐观锁的关键不在于事务本身。

2.4 扩展:事务的使用

--查询数据
SELECT iD, val1, val2
FROM theTable
WHERE iD = @theId;
--计算新值
--开始事务,更新数据
UPDATE
theTable
SET
val1 = @newVal1,
val2 = @newVal2
WHERE
iD = @theId
AND val1 = @oldVal1
AND val2 = @oldVal2;
--判断影响行数
-- {if AffectedRows == 1 }
-- COMMIT TRANSACTION; // 提交事务
-- {继续执行}
-- {else}
-- ROLLBACK TRANSACTION; // 回滚事务
-- {数据过期}
-- {endif}

使用了事务,便可以回滚修改。通过事务,我们可以确定每次回滚的操作量是多少,在何处放置事务边界以及在何处检查冲突。

对于其他进程在当前事务提交之前,会发生什么,取决于数据库当前的隔离级别。以SQL Server为例,其隔离级别是READ_COMMITTED,更新的行被锁定,直到COMMIT为止,因此“其他进程”无法对该行执行任何操作(保持等待状态),而SELECT(实际上只能执行READ_COMMITTED) 。

2.5 乐观锁的实现方式2--版本号

使用版本号,也是乐观锁常用实现方式。通过在表中增加一个version字段:读取数据时,将version字段值一并读出,数据更新一次,则version值加1。当我们提交更新时,判断表中最新的version值与之前读出的version值是否一致,如果一致,则更新,否则视为过期数据。

--查询数据
SELECT iD,val1,val2,VERSION
FROM theTable
WHERE iD = @theId;
--计算新值
UPDATE
theTable
SET
val1 = @newVal1,
val2 = @newVal2,
VERSION = VERSION + 1
WHERE
iD = @theId
AND VERSION = @oldversion;
--判断影响行数
-- {if AffectedRows == 1 }
-- {继续执行}
-- {else}
-- {数据过期}
-- {endif}

参考资料

https://stackoverflow.com/questions/17431338/optimistic-locking-in-mysql

本文由博客一文多发平台 OpenWrite 发布!

使用MySQL乐观锁解决超卖问题的更多相关文章

  1. redis分布式锁解决超卖问题

    redis事务 redis事务介绍:    1. redis事务可以一次执行多个命令,本质是一组命令的集合. 2.一个事务中的所有命令都会序列化,按顺序串行化的执行而不会被其他命令插入 作用:一个队列 ...

  2. 使用mysql乐观锁解决并发问题

    案例说明: 银行两操作员同时操作同一账户.比如A.B操作员同时读取一余额为1000元的账户,A操作员为该账户增加100元,B操作员同时为该账户扣除50元,A先提交,B后提交.最后实际账户余额为1000 ...

  3. 使用mysql乐观锁解决并发问题思路

    本文摘自网络,仅供个人学习之用 案例说明: 银行两操作员同时操作同一账户.比如A.B操作员同时读取一余额为1000元的账户,A操作员为该账户增加100元,B操作员同时为该账户扣除50元,A先提交,B后 ...

  4. 使用mysql悲观锁解决并发问题

    最近学习了一下数据库的悲观锁和乐观锁,根据自己的理解和网上参考资料总结如下: 悲观锁介绍(百科): 悲观锁,正如其名,它指的是对数据被外界(包括本系统当前的其他事务,以及来自外部系统的事务处理)修改持 ...

  5. MySQL 乐观锁与悲观锁

    悲观锁 悲观锁(Pessimistic Lock),顾名思义,就是很悲观,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会block直到它拿到锁. 悲观锁: ...

  6. mysql乐观锁总结和实践--转

    原文地址:http://chenzhou123520.iteye.com/blog/1863407 上一篇文章<MySQL悲观锁总结和实践>谈到了MySQL悲观锁,但是悲观锁并不是适用于任 ...

  7. 【转】MySQL乐观锁在分布式场景下的实践

    背景 在电商购物的场景下,当我们点击购物时,后端服务就会对相应的商品进行减库存操作.在单实例部署的情况,我们可以简单地使用JVM提供的锁机制对减库存操作进行加锁,防止多个用户同时点击购买后导致的库存不 ...

  8. MySQL乐观锁在分布式场景下的实践

    背景 在电商购物的场景下,当我们点击购物时,后端服务就会对相应的商品进行减库存操作.在单实例部署的情况,我们可以简单地使用JVM提供的锁机制对减库存操作进行加锁,防止多个用户同时点击购买后导致的库存不 ...

  9. mysql乐观锁总结和实践(转)

    原文:mysql乐观锁总结和实践 上一篇文章<MySQL悲观锁总结和实践>谈到了MySQL悲观锁,但是悲观锁并不是适用于任何场景,它也有它存在的一些不足,因为悲观锁大多数情况下依靠数据库的 ...

随机推荐

  1. Mac系统使用Parallels Desktop安装Win10

    1.Parallels Desktop破解版下载 2.原版Windows 10 2004 X64位 (原版安装)2020 11 Windows 系统镜像必须为原版,ghost版不行.亲测ghost版本 ...

  2. 这次齐了!Java面向对象、类的定义、对象的使用,全部帮你搞定

    概述 Java语言是一种面向对象的程序设计语言,而面向对象思想是一种程序设计思想,我们在面向对象思想的指引下, 使用Java语言去设计.开发计算机程序. 这里的对象泛指现实中一切事物,每种事物都具备自 ...

  3. 2020阿里Java面试题目大汇总,看看你离阿里还有多远,附答案!

    前言 首先说一下情况,我大概我是从去年12月份开始看书学习,到今年的6月份,一直学到看大家的面经基本上百分之90以上都会,我就在5月份开始投简历,边面试边补充基础知识等.也是有些辛苦.终于是在前不久拿 ...

  4. 如何使用iMazing编辑iOS设备的备份

    乍一看,编辑iPhone或iPad的备份似乎是一个奇怪的命题,但实际上这样做的原因有很多,例如在备份数据损坏时进行修复,又如合并来自不同设备的数据. iMazing对备份文件编辑的支持非常全面,即使备 ...

  5. python 如何跳过异常继续执行

    使用try...except...语句,类似于if...else...,可以跳过异常继续执行程序,这是Python的优势 用法如下: 1 2 3 4 5 6 try:            # 可能会 ...

  6. [转载]Windows环境下 Hadoop Error: JAVA_HOME is incorrectly set. 问题

    最近尝试在windows开发MR程序并且提交Job,在解压缩好hadoop,配置好环境变量后, 打开cmd 输入hadoop version 的时候出现以下错误: Error: JAVA_HOME i ...

  7. js 实现textarea剩余字数统计

    1 针对textarea剩余字数统计 2 <div class="fankui-textarea"> 3 <span>留言:</span> &l ...

  8. php8.0正式版新特性和性能优化学习

    前言 PHP团队宣布PHP8正式GA(链接).php的发展又开启了新的篇章,PHP8.0.0版本引入了一些重大变更及许多新特性和性能优化机制.火速学习下~ JIT(Just in Time Compi ...

  9. 【AcWing 113】【交互】特殊排序——二分

    (题面来自AcWing) 有N个元素,编号1.2..N,每一对元素之间的大小关系是确定的,关系不具有传递性. 也就是说,元素的大小关系是N个点与N*(N-1)/2条有向边构成的任意有向图. 然而,这是 ...

  10. 一张图彻底理解Spring如何解决循环依赖!!

    写在前面 最近,在看Spring源码,看到Spring解决循环依赖问题的源码时,不得不说,源码写的太烂了.像Spring这种顶级的项目源码,竟然存在着这种xxx的代码.看了几次都有点头大,相信很多小伙 ...