作者:志波同学来源:https://mp.weixin.qq.com/s/8uH-7TD9ZRG4NCya80mOHg

Mysql InnoDB 排他锁

用法:select … for update;

例如:select * from goods where id = 1 for update;

排他锁的申请前提:没有线程对该结果集中的任何行数据使用排他锁或共享锁,否则申请会阻塞。

for update仅适用于InnoDB,且必须在事务块(BEGIN/COMMIT)中才能生效。在进行事务操作时,通过“for update”语句,MySQL会对查询结果集中每行数据都添加排他锁,其他线程对该记录的更新与删除操作都会阻塞。排他锁包含行锁、表锁。

场景分析

假设有一张商品表 goods,它包含 id,商品名称,库存量三个字段,表结构如下:

一、数据一致性

假设有A、B两个用户同时各购买一件 id=1 的商品,用户A获取到的库存量为 1000,用户B获取到的库存量也为 1000,用户A完成购买后修改该商品的库存量为 999,用户B完成购买后修改该商品的库存量为 999,此时库存量数据产生了不一致。

有两种解决方案:

悲观锁方案:每次获取商品时,对该商品加排他锁。也就是在用户A获取获取 id=1 的商品信息时对该行记录加锁,期间其他用户阻塞等待访问该记录。悲观锁适合写入频繁的场景。

begin;select * from goods where id = 1 for update;update goods set stock = stock - 1 where id = 1;commit;

乐观锁方案:每次获取商品时,不对该商品加锁。在更新数据的时候需要比较程序中的库存量与数据库中的库存量是否相等,如果相等则进行更新,反之程序重新获取库存量,再次进行比较,直到两个库存量的数值相等才进行数据更新。乐观锁适合读取频繁的场景。

#不加锁获取 id=1 的商品对象select * from goods where id = 1begin;#更新 stock 值,这里需要注意 where 条件 “stock = cur_stock”,只有程序中获取到的库存量与数据库中的库存量相等才执行更新update goods set stock = stock - 1 where id = 1 and stock = cur_stock;commit;

如果我们需要设计一个商城系统,该选择以上的哪种方案呢?

查询商品的频率比下单支付的频次高,基于以上我可能会优先考虑第二种方案(当然还有其他的方案,这里只考虑以上两种方案)。

二、行锁与表锁

1、只根据主键进行查询,并且查询到数据,主键字段产生行锁。

begin;select * from goods where id = 1 for update;commit;

2、只根据主键进行查询,没有查询到数据,不产生锁。

begin;select * from goods where id = 1 for update;commit;

3、根据主键、非主键含索引(name)进行查询,并且查询到数据,主键字段产生行锁,name字段产生行锁。

begin;select * from goods where id = 1 and name='prod11' for update;commit;

4、根据主键、非主键含索引(name)进行查询,没有查询到数据,不产生锁。

begin;select * from goods where id = 1 and name='prod12' for update;commit;

5、根据主键、非主键不含索引(name)进行查询,并且查询到数据,如果其他线程按主键字段进行再次查询,则主键字段产生行锁,如果其他线程按非主键不含索引字段进行查询,则非主键不含索引字段产生表锁,如果其他线程按非主键含索引字段进行查询,则非主键含索引字段产生行锁,如果索引值是枚举类型,mysql也会进行表锁,这段话有点拗口,大家仔细理解一下。

begin;select * from goods where id = 1 and name='prod11' for update;commit;

6、根据主键、非主键不含索引(name)进行查询,没有查询到数据,不产生锁。

begin;select * from goods where id = 1 and name='prod12' for update;commit;

7、根据非主键含索引(name)进行查询,并且查询到数据,name字段产生行锁。

begin;select * from goods where name='prod11' for update;commit;

8、根据非主键含索引(name)进行查询,没有查询到数据,不产生锁。

begin;select * from goods where name='prod11' for update;commit;

9、根据非主键不含索引(name)进行查询,并且查询到数据,name字段产生表锁。

begin;select * from goods where name='prod11' for update;commit;

10、根据非主键不含索引(name)进行查询,没有查询到数据,name字段产生表锁。

begin;select * from goods where name='prod11' for update;commit;

11、只根据主键进行查询,查询条件为不等于,并且查询到数据,主键字段产生表锁。

begin;select * from goods where id <> 1 for update;commit;

12、只根据主键进行查询,查询条件为不等于,没有查询到数据,主键字段产生表锁。

begin;select * from goods where id <> 1 for update;commit;

13、只根据主键进行查询,查询条件为 like,并且查询到数据,主键字段产生表锁。

begin;select * from goods where id like '1' for update;commit;

14、只根据主键进行查询,查询条件为 like,没有查询到数据,主键字段产生表锁。

begin;select * from goods where id like '1' for update;commit;

测试环境

数据库版本:5.1.48-community

数据库引擎:InnoDB Supports transactions, row-level locking, and foreign keys

数据库隔离策略:REPEATABLE-READ(系统、会话)

总结

1、InnoDB行锁是通过给索引上的索引项加锁来实现的,只有通过索引条件检索数据,InnoDB才使用行级锁,否则,InnoDB将使用表锁。

2、由于MySQL的行锁是针对索引加的锁,不是针对记录加的锁,所以虽然是访问不同行的记录,但是如果是使用相同的索引键,是会出现锁冲突的。应用设计的时候要注意这一点。

3、当表有多个索引的时候,不同的事务可以使用不同的索引锁定不同的行,另外,不论是使用主键索引、唯一索引或普通索引,InnoDB都会使用行锁来对数据加锁。

4、即便在条件中使用了索引字段,但是否使用索引来检索数据是由MySQL通过判断不同执行计划的代价来决定的,如果MySQL认为全表扫描效率更高,比如对一些很小的表,它就不会使用索引,这种情况下InnoDB将使用表锁,而不是行锁。因此,在分析锁冲突时,别忘了检查SQL的执行计划,以确认是否真正使用了索引。

5、检索值的数据类型与索引字段不同,虽然MySQL能够进行数据类型转换,但却不会使用索引,从而导致InnoDB使用表锁。通过用explain检查两条SQL的执行计划,我们可以清楚地看到了这一点。

参考资料

http://www.2cto.com/database/201208/145888.html

http://www.jb51.net/article/50047.htm

http://blog.csdn.net/kingo0/article/details/43194681

http://www.cnblogs.com/Lawson/archive/2015/11/30/5008741.html

http://www.cnblogs.com/Bob-FD/p/3352216.html

http://www.linuxidc.com/Linux/2014-01/95376.htm

mysql begin end 用法_数据库:Mysql中“select ... for update”排他锁分析相关推荐

  1. mysql update 排他锁_数据库:Mysql中“select ... for update”排他锁分析

    Mysql InnoDB 排他锁 用法: select - for update; 例如:select * from goods where id = 1 for update; 排他锁的申请前提:没 ...

  2. 数据库:Mysql中“select ... for update”排他锁分析

    Mysql InnoDB 排他锁 用法: select - for update; 例如:select * from goods where id = 1 for update; 排他锁的申请前提:没 ...

  3. mysql单价乘以数量_数据库字段中存储的数据数量乘以不同单价的和的算法示例...

    数据库字段中存储的数据数量乘以不同单价的和的算法,适用于记账本程序的计件数据记录,和商品记录等场景. 代码示例如下: //模拟数据库结果集 $arr = array( array( "id& ...

  4. mysql 设计动态字段_数据库设计中动态列的设计方法

    问题提出: 在进行数据库开发过程中,我们往往会碰到这种情形: 如在劳资计算中,对每个人有很多薪酬项目需要汇总计算出工资,如基本工资,计件工资,计时工资,奖金,补贴,代扣等等,但我们在建表的过程中既不能 ...

  5. mysql 分区指定路径_[数据库]MySQL 指定各分区路径

    [数据库]MySQL 指定各分区路径 0 2016-11-08 18:00:44 介绍 可以针对分区表的每个分区指定各自的存储路径,对于innodb存储引擎的表只能指定数据路径,因为数据和索引是存储在 ...

  6. mysql 与 xls 连接_数据库MySQL与xls文件的互导

    最近的一个项目需要将xls表导入到MySQL数据库中和将MySQL数据表导出到xls表中,在网上搜了很多资料,经过多次尝试终于实现了功能,废话不多说,在这粘贴出代码,希望可以帮到需要的朋友. 一.将. ...

  7. mysql权限层级体系_数据库mysql有哪些权限?层级有哪些?

    我们想要运行一个文件时,有时候会出现只有管理员才能打开这类软件,这就是权限对于用户的限制.那么在我们最近学习的数据库mysql中,也有这样的权限需要我们去注意吗?小编想说当然有,而且还不少!今天就数据 ...

  8. mysql begin end 用法_超实用的Mysql动态更新数据库脚本的示例讲解(推荐)

    今天小编为大家分享一篇关于Mysql动态更新数据库脚本的示例讲解,具体的upgrade脚本如下: 动态删除索引 DROP PROCEDURE IF EXISTS UPGRADE;DELIMITER $ ...

  9. mysql语句成绩等级_数据库mysql中case如何给成绩划分等级?

    在表格中如果我们要给不同分段的成绩评选等级,只需要使用筛选功能输入评定的条件即可.那么在mysql中我们该如何输出跟表格一样结果呢?这里有的小伙伴说可以用我们刚学习的case函数来尝试.但是作为最基础 ...

最新文章

  1. CentOS 7 命令
  2. 2019年软件测试现状调查
  3. python在运行调试程序时常出现的错误有_Python 如何调试程序崩溃错误
  4. 温度自动调节 c语言编写,室内温度自动调节控制系统课程设计(26页)-原创力文档...
  5. 【笔记】jstree插件的基本使用
  6. java post 提交数据_使用Post方式提交数据到Tomcat服务器的方法
  7. Oracle 小技巧
  8. Unity SRP自定义渲染管线 -- 5.Directional Shadows
  9. SOJ 2800_三角形
  10. C++,string类的指针,string类数组的指针 string *s
  11. 为什么说边缘计算的发展比5G更重要?
  12. QTP报错“缺少对象WScript”
  13. layui时间选择30分钟为单位_如何集中注意力,不妨试试番茄工作法 | 五色时间管理法...
  14. [vSphere培训实录]8G内存笔记本搭建vSphere测试环境
  15. 每周.NET前沿技术文章摘要(2017-05-10)
  16. c语言编写一个用户登录界面,怎么用C语言编写个登陆界面?
  17. CPLEX导出数据到EXCEL
  18. SiamFC 文章阅读
  19. 微信h5棋牌以及下载类推广如何在微信中做好防封防屏蔽技术
  20. j2me之诺基亚S40模拟器

热门文章

  1. 常用报文的解析与相互转换
  2. 高数学习笔记:计算方向导数
  3. 【POJ2774】Long Long Message,第一次的后缀数组
  4. 2017.10.8 志愿者招募 失败总结
  5. 【英语学习】【Daily English】U02 Daily Routine L02 I go to the gym every other day
  6. python80行代码写一个文件整理软件
  7. 计算机网络体系结构物流层,计算机网络体系结构58029.ppt
  8. python个人微信支付接口_Python实现微信小程序支付功能
  9. OpenSSL的Heartbleed漏洞原理及简单模拟
  10. Unity5.X打包与加载AssetBundle简单示例