MySQL 在高并发下的 订单撮合 系统使用 共享锁 与 排他锁 保证数据一致性
作者:林冠宏 / 指尖下的幽灵
GitHub : https://github.com/af913337456/
腾讯云专栏: https://cloud.tencent.com/developer/user/1148436/activities
虫洞区块链专栏:https://www.chongdongshequ.com/article/1536563643883.html
前序
距离上次择文发表,两月余久。2018年也即将要结束了,目前的工作依然是与区块链应用
相关的,也很荣幸在9月初受邀签约出版暂名为《区块链以太坊DApp实战开发》
一书,预计在明年年初出版。
这次让我有感记录这篇文章的原因是最近在使用Go
语言重写一个原来由PHP
语言编写的交易所订单撮合模块
的时候,发现订单撮合
的部分代码
在撮合的时候,为保证各表数据在并发情况下不出现读写脏乱而采用了全局锁表
的操作。后面我采用了共享锁
的形式进行了修改,于刚刚重写完,并进行了并发单元测试
,表现正常。
目录
- 场景描述
- 解决问题
- 订单撮合实例
- 共享锁 与 排他锁
- 前置知识
- 行锁与表锁
- 两种行锁的特点
- 两种行锁的加锁方式
- 锁的释放
- 操作例子
- 改造代码片段
场景描述
高并发的业务常见是有很多种类的,最常见的例如秒杀抢购
。它们都有一个共同的特点就是数据更新都比较频繁,通常涉及到多张业务表的增改
操作,且表格越多的,要考虑的问题也越多。
订单撮合可以理解为订单买卖,拿这个为例子进行列举一个可能会导致数据错乱的情形。假设现在买卖手机,A用户是要买手机的,B用户是卖手机的。A的买入单
订单1,和B的卖出单
订单2,订单2卖出手机,一台手机卖1000元。此时A的网上的钱包余额是1001元,刚好比手机价格高,是可以成交的。
此时记录用户钱包钱数数量的是一张数据表。每次花费了钱或者增加了钱,都要更新这个表。
当这两笔订单进入到系统里面进行撮合。假设系统的订单撮合运行流程如下图所示:
当判断条件进行A用户的钱包余额判断的时候,发现 1001 > 1000,结果是通过,此时准备进入“进行记录更细”步骤。但是,就在这个过程之中的时间差中,A用户使用了系统的网上提现功能,并成功转出了10元,剩余的是1001 - 10 = 991
元。但是由于撮合系统的余额判断过程以及通过了,导致下面的交易流程依然能进行,最终A用991元买了B的1000元售价的手机。
解决问题
上述的常见问题是一个很简单的模型,现实的系统中往往是更复杂的。但是它所体现出的问题却是真实存在的,对于这类问题,有很多解决方案。其中,就可以考虑使用数据库的锁。
本文要介绍的是MySQL数据库
的共享锁
与 排他锁
,其它的不作说明或引申。
订单撮合实例
下面的截图就是我所重写好的撮合系统原始的PHP
代码,所使用了表锁
的方式来解决前面的并发读写导致数据脏乱的问题。这种方式虽然是解决了问题,但是导致了性能低下
的问题。
共享锁 与 排他锁
前置知识:
- MySQL 是数据库,不是
数据库引擎
- MySQL有两种常用存储引擎:
MyISAM
和InnoDB
MyISAM
不支持事务操作,InnoDB
支持事务操作- MySQL 的锁分有
行锁
和表锁
- MyISAM 只有表锁
- Innodb 行锁,表锁都有
- 行锁中有
共享锁
和排他锁
共享锁
简称 S锁,排他锁
简称 X锁
行锁与表锁
简述:
行锁,锁的是表中对应的行,只限制当前行的读写。
表锁,锁的是整张表,限制的是整张表的数据读写。
比较:
- 行锁,计算机资源开销大,加锁慢;会出现
死锁
;锁定粒度最小,锁冲突的概率最低,并发度
最高,性能高。 - 表锁,计算机资源开销小,加锁快;不会出现
死锁
;锁定粒度大,锁冲突的概率最高,并发度
最低,性能低。
两种行锁的特点
共享锁
A 对数据 B 加了 共享锁,A能读取和修改数据B,C 等其它只
能读取
数据B,但是不能修改
。直至A释放了B的锁。
排他锁
A 对数据 B 加了 排他锁,A能读取和修改数据B,C 等其它不能再对数据B加其它的锁。直观体验是不能修改,不能使用含有加锁动作的
select
读取。
两种行锁的加锁方式
要注意的是:
- 行锁的实现SQL语句中必须要有索引的限制条件,例如含有
where id=xxx
这类语句。 - 行锁的实现SQL语句没有索引限制条件会变成
表锁
InnoDB引擎
默认的修改数据
类SQL语句,update
,delete
,insert
等,都会自动给涉及到的数据加上排他锁。
共享锁
- select 的添加可以使用满足格式:
select ... where 索引限制 lock in share mode
的语句。例如“select name from lgh_user where id = 1 lock in share model” 此时 id 是索引。
排他锁
- 满足格式:
select ... where 索引限制 for update
的语句
锁的释放
非事务(Transaction) 中,语句执行完毕,便释放锁。
行锁在事务 (Transaction) 中,只有等到当前的事务Transaction 进行了 commit 或 roll back,锁才能释放。
操作例子
演示事务 tx 中的例子,文字解析见图。
改造代码片段
撮合中的所有表锁
替换成了共享锁
,运行其它业务读取所锁的行数据,在当前事务的批量操作还没结束之前,不允许修改。
完
MySQL 在高并发下的 订单撮合 系统使用 共享锁 与 排他锁 保证数据一致性的更多相关文章
- 【mysql】mysql增加version字段实现乐观锁,实现高并发下的订单库存的并发控制,通过开启多线程同时处理模拟多个请求同时到达的情况 + 同一事务中使用多个乐观锁的情况处理
mysql增加version字段实现乐观锁,实现高并发下的订单库存的并发控制,通过开启多线程同时处理模拟多个请求同时到达的情况 ==================================== ...
- Mysql共享锁、排他锁、悲观锁、乐观锁及其使用场景
一.相关名词 |--表级锁(锁定整个表) |--页级锁(锁定一页) |--行级锁(锁定一行) |--共享锁(S锁,MyISAM 叫做读锁) |--排他锁(X锁,MyISAM 叫做写锁) |--悲观锁( ...
- [转]MySQL中乐观锁、悲观锁(共享锁、排他锁)简介
InnoDB与MyISAM Mysql 在5.5之前默认使用 MyISAM 存储引擎,之后使用 InnoDB. MyISAM 操作数据都是使用的表锁,你更新一条记录就要锁整个表,导致性能较低,并发不高 ...
- 浅谈Mysql共享锁、排他锁、悲观锁、乐观锁及其使用场景
浅谈Mysql共享锁.排他锁.悲观锁.乐观锁及其使用场景 Mysql共享锁.排他锁.悲观锁.乐观锁及其使用场景 一.相关名词 |--表级锁(锁定整个表) |--页级锁(锁定一页) |--行级锁(锁 ...
- Mysql共享锁、排他锁、悲观锁、乐观锁
一.相关名词 |--表级锁(锁定整个表) |--页级锁(锁定一页) |--行级锁(锁定一行) |--共享锁(S锁,MyISAM 叫做读锁) |--排他锁(X锁,MyISAM 叫做写锁) |--间隙锁( ...
- (8)MySQL进阶篇SQL优化(InnoDB锁-共享锁、排他锁与意向锁)
1.锁的分类 锁(Locking)是数据库在并发访问时保证数据一致性和完整性的主要机制.之前MyISAM锁章节已经讲过锁分类,而InnoDB锁按照粒度分为锁定整个表的表级锁(table-level l ...
- [数据库事务与锁]详解六: MySQL中的共享锁与排他锁
注明: 本文转载自http://www.hollischuang.com/archives/923 在MySQL中的行级锁,表级锁,页级锁中介绍过,行级锁是Mysql中锁定粒度最细的一种锁,行级锁能大 ...
- mysql共享锁与排他锁
mysql锁机制分为表级锁和行级锁,本文就和大家分享一下我对mysql中行级锁中的共享锁与排他锁进行分享交流. 共享锁又称为读锁,简称S锁,顾名思义,共享锁就是多个事务对于同一数据可以共享一把锁,都能 ...
- 【数据库】MySQL中的共享锁与排他锁
转载:http://www.hollischuang.com/archives/923 在MySQL中的行级锁,表级锁,页级锁中介绍过,行级锁是Mysql中锁定粒度最细的一种锁,行级锁能大大减少数据库 ...
随机推荐
- db2 reorg详解
reorgchk,检查table index 是否需要重组.reorg 重组,重新放置数据位置.runstats 统计信息,可以优化查询器 一个完整的日常维护规范可以帮助 DBA 理顺每天需要的操作, ...
- RealTek WiFi 模块 RTL8710AF RTL8711AF RTL8711AM RTL8195AM
瑞昱 8710 是一个完整且自成体系的 WiFi 网络解决方案, 能够独立运行,也可以作为从机搭载于其他主机 MCU 运行. 瑞昱 8710 在搭载应用并作为设备中唯⼀的应⽤处理器时,能够直接从外接闪 ...
- set non-stop on
# Enable the async interface. set target-async 1 # If using the CLI, pagination breaks non-stop. set ...
- adb devices 找不到设备的解决方法
1.开启adb 2.查看设备时的报错 问题1:cannot connect to daemon 解决方法:找到占据5037端口的进城,并在“任务管理器”中依据“PID”查找到,解决进程 问题2:手机 ...
- Collection was modified; enumeration operation may not execute Dictionary 集合已修改;可能无法执行枚举操作
public void ForeachDic() { Dictionary dic = new Dictionary(); dic.Add("1", 10); dic.Add(&q ...
- tp5.1的安装与运行流程
今天安装tp5.1的时候出现问题,我这里使用的是将tp直接解压缩后放在项目里面,结果提示下面的错误:Warning: require(D:\workspace\zhiyuedu\vendor/topt ...
- 命令 上传项目到git中
点击Clone or dowload会出现一个地址,copy这个地址备用. 接下来就到本地操作了,首先右键你的项目,如果你之前安装git成功的话,右键会出现两个新选项,分别为Git Gui Here, ...
- grid - 通过网格线名称设置网格项目位置
使用网格线名称设置网格项目位置和使用网格线号码设置网格项目位置类似. 1.引用网格线名称的时候不应该带方括号 <view class="grid"> <view ...
- 为什么虚拟 dom 会提高性能?
虚拟 dom 相当于在 js 和真实 dom 中间加了一个缓存,利用 dom diff 算法避免了没有必要的 dom 操作,从而提高性能.用 JavaScript 对象结构表示 DOM 树的结构:然后 ...
- CoreGraphics之CGContextSaveGState与UIGraphicsPushContext
转至:https://www.jianshu.com/p/be38212c0f79 CoreGraphics与UIKit 这边从iOS绘图教程 提取一些重要的内容. Core Graphics Fra ...