MySQL表锁、行锁、排它锁及共享锁的使用详解


Posted in MySQL onApril 02, 2022

前言

事务隔离级别的实现原理:简单来说就是各种锁机制和MVCC多版本并发控制

我们学习知识的时候,需要了解知识点出现的原因,什么情况下能用到这个知识

我们说到事务,就得说到事务的ACID特性,说到隔离性的时候,事务要能够允许并发执行,并发执行为了同时保证数据的安全性,一致性和并发的效率,就需要设置事务的隔离级别

一、事务隔离机制的选择

  • 如果我们完全不管,使用未提交读的事务隔离机制,任由这些线程并发操作数据库,那就会出现脏读(读取了未commit的数据)、不可重复读(两次查询值不同)、幻读(两次查询数据量不同)等问题,数据的安全性最低,优点是并发效率非常高,一般不会使用
  • 如果我们串行化(靠锁实现),通过锁给所有的事务都排个序,虽然数据的安全性提高了,并发的效率就太低了,一般也不会使用
  • 所以我们一般用的是已提交读、可重复读这两个隔离级别,平衡了数据的安全性,一致性以及并发的效率 ,是由MVCC多版本并发控制实现的(MVCC是已提交读和可重复读的原理,锁是串行化的原理)

二、表级锁&行级锁

表级锁:对整张表加锁。开销小(因为不用去找表的某一行的记录进行加锁,要修改这张表,直接申请加这张表的锁),加锁快,不会出现死锁;锁粒度大,发生锁冲突的概率高,并发度低

行级锁:对某行记录加锁。开销大(需要找到表中相应的记录,有搜表搜索引的过程),加锁慢,会出现死锁;锁定粒度最小,发生锁冲突的概率最低,并发度高

MySQL表锁、行锁、排它锁及共享锁的使用详解

InnoDB存储引擎支持事务处理,表支持行级锁定,并发能力更好

  1. InnoDB行锁是通过给索引上的索引项加锁来实现的,而不是给表的行记录加锁实现的,这就意味者只有通过索引条件检索数据,InnoDB才使用行级锁,否则InnoDB将使用表锁
  2. 由于InnoDB的行锁实现是针对索引字段添加的锁,不是针对行记录加的锁,因此虽然访问的是InnoDB引擎下表的不同行,但如果使用相同的索引字段作为过滤条件,依然会发生锁冲突,只能串行进行,不能并发进行
  3. 即使SQL中使用了索引,但是经过MySQL的优化器后,如果认为全表扫描比使用索引效率高,此时会放弃使用索引,因此也不会
    使用行锁,而是使用表锁,比如对一些很小的表,MySQL就不会去使用索引

三、排它锁(Exclusive)和共享锁(Shared)

  • 排它锁,又称为X锁,写锁
  • 共享锁,又称为S锁,读锁

读读(SS)之间是可以兼容的,但是读写(SX、SX)之间,写写(XX)之间是互斥的

对事务加X和S锁之间有以下的关系:

  • 一个事务对数据对象A加了 S 锁,可以对A进行读取操作但不能进行update操作,加锁期间其它事务能对A加S锁但不能加 X 锁
  • 一个事务对数据对象A加了 X 锁,就可以对A进行读取和更新,加锁期间其它事务不能对A加任何锁

显示加锁:select … lock in share mode强制获取共享锁,select … for update获取排它锁

1. 测试不同事务之间排它锁和共享锁的兼容性

我们先查看表的SQL以及内容

MySQL表锁、行锁、排它锁及共享锁的使用详解

查看隔离级别:

MySQL表锁、行锁、排它锁及共享锁的使用详解

首先开启一个事务,给id=7的数据加上排它锁

MySQL表锁、行锁、排它锁及共享锁的使用详解

在用另一个客户端开启事务

MySQL表锁、行锁、排它锁及共享锁的使用详解

我们用另一个事务的服务线程给id=7的数据加上排它锁,阻塞了

MySQL表锁、行锁、排它锁及共享锁的使用详解

我们尝试给id=7的数据加上共享锁,还是阻塞了

总结:不同事务之间对于数据的锁,只有SS锁可以共存,XX、SX、XS都不能共存

2. 测试行锁加在索引项上

其实行锁是加在索引树上的

MySQL表锁、行锁、排它锁及共享锁的使用详解

用表的无索引字段作为过滤条件

MySQL表锁、行锁、排它锁及共享锁的使用详解

事务2现在同样想获取这条记录的排它锁,可想而知地失败了;那现在事务2获取chenwei的记录的排它锁,试试能不能成功

MySQL表锁、行锁、排它锁及共享锁的使用详解

InnoDB是支持行锁的,刚才以主键id为过滤条件时,事务1和事务2获取不同行的锁是可以成功的。然而现在我们发现获取name为chenwei的排它锁也获取不到了,这是为什么?我们解释一下:

InnoDB的行锁是通过给索引项加锁来实现的,而不是给表的行记录加锁实现的

而我们用name作为过滤条件没有用到索引,自然就不会使用行锁,而是使用表锁。这就意味着只有通过索引检索数据,InnoDB才使用行级锁,否则InnoDB都将使用表锁!!!

我们给name字段加上索引

MySQL表锁、行锁、排它锁及共享锁的使用详解

MySQL表锁、行锁、排它锁及共享锁的使用详解

我们发现,给name加上索引后,两个事务可以获取到不同行的排它锁(for update),再一次证明了InnoDB的行锁是加在索引项上的

MySQL表锁、行锁、排它锁及共享锁的使用详解

因为现在name走的是索引, 通过zhangsan在辅助索引树上找到它所在行记录的id是7,然后到主键索引树上,获取对应行记录的排他锁(个人猜测应该是辅助索引树和主键索引树相应的记录都加了锁)

四、串行化隔离级别测试

(所有的事务都使用排它锁或共享锁,不需要用户手动加锁)

设置串行化隔离级别

MySQL表锁、行锁、排它锁及共享锁的使用详解

两个事务可以同时获取共享锁(SS共存)

MySQL表锁、行锁、排它锁及共享锁的使用详解


现在让事务2插入数据

MySQL表锁、行锁、排它锁及共享锁的使用详解

此时由于insert需要加排它锁,但由于事务1已经对整张表添加了共享锁,事务2无法再对表成功加锁(SX不共存)

rollback一下

MySQL表锁、行锁、排它锁及共享锁的使用详解

因为我们给name加上了索引,以上的select相当于给name为zhangsan的数据加上了行共享锁

事务2 update

MySQL表锁、行锁、排它锁及共享锁的使用详解

事务2不能update,因为此时已经被事务1的共享锁锁住了整个表

事务2在辅助索引树上找zhangsan,找到对应的主键值,然后去主键索引树找到相应的记录,但是发现这行记录已经被共享锁锁住了,事务2可以获取共享锁,但是不能获取排他锁

MySQL表锁、行锁、排它锁及共享锁的使用详解

我们用主键索引id试试能不能update

MySQL表锁、行锁、排它锁及共享锁的使用详解

依然阻塞住了,虽然我们where后面的字段现在使用的id而不是name,但是name也是通过辅助索引树找到对应的主键,再到主键索引树上找相应的记录,而主键索引树上的记录加了锁(个人猜想应该是辅助索引树和主键索引树对应的数据都加了锁)

我们update id=8的数据,成功了。因为我们select的时候,只是给id=7的数据加上了行锁,我们操作id=8的数据当然可以成功

MySQL表锁、行锁、排它锁及共享锁的使用详解

有索引,则使用行锁;没有索引,则使用表锁。

表级锁还是行级锁说的是锁的粒度,共享锁和排他锁说的是锁的性质,不管是表锁还是行锁,都有共享锁和排他锁的区分

总结

到此这篇关于MySQL表锁、行锁、排它锁及共享锁使用的文章就介绍到这了,更多相关MySQL表锁、行锁、排它锁和共享锁内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木!

MySQL 相关文章推荐
MySql学习笔记之事务隔离级别详解
May 12 MySQL
MYSQL(电话号码,身份证)数据脱敏的实现
May 28 MySQL
MYSQL 无法识别中文的永久解决方法
Jun 03 MySQL
MySQL 5.7常见数据类型
Jul 15 MySQL
MySQL连接控制插件介绍
Sep 25 MySQL
MySQL中rank() over、dense_rank() over、row_number() over用法介绍
Mar 23 MySQL
详解Mysq MVCC多版本的并发控制
Apr 29 MySQL
MySQL数据库实验实现简单数据库应用系统设计
Jun 21 MySQL
MySQL控制流函数(-if ,elseif,else,case...when)
Jul 07 MySQL
MySQL池化框架学习接池自定义
Jul 23 MySQL
MySQL使用IF语句及用case语句对条件并结果进行判断 
Sep 23 MySQL
详解MySQL的内连接和外连接
May 08 MySQL
navicat 连接Ubuntu虚拟机的mysql的操作方法
MySQL中IO问题的深入分析与优化
mysql的Buffer Pool存储及原理
一次Mysql update sql不当引起的生产故障记录
Mysql超详细讲解死锁问题的理解
Nebula Graph解决风控业务实践
MySQL实现配置主从复制项目实践
You might like
PHP PDO函数库详解
2010/04/27 PHP
php防止站外远程提交表单的方法
2014/10/20 PHP
PHP输入流php://input实例讲解
2015/12/22 PHP
PHP类和对象相关系统函数与运算符小结
2016/09/28 PHP
PHP8.0新功能之Match表达式的使用
2020/07/19 PHP
基于php+MySql实现学生信息管理系统实例
2020/08/04 PHP
网络之美 JavaScript中Get和Set访问器的实现代码
2010/09/19 Javascript
jQuery选择器全集详解
2014/11/24 Javascript
JS实现仿京东淘宝竖排二级导航
2014/12/08 Javascript
JavaScript实现常用二级省市级联下拉列表的方法
2015/03/25 Javascript
基于jQuery Circlr插件实现产品图片360度旋转
2015/09/20 Javascript
JavaScript多图片上传案例
2015/09/28 Javascript
jquery自动补齐功能插件flexselect用法示例
2016/08/06 Javascript
jQuery 翻页组件yunm.pager.js实现div局部刷新的思路
2016/08/11 Javascript
vue.js入门教程之计算属性
2016/09/01 Javascript
浅谈JS读取DOM对象(标签)的自定义属性
2016/11/21 Javascript
Webpack+Vue如何导入Jquery和Jquery的第三方插件
2017/02/20 Javascript
Vue.js教程之axios与网络传输的学习实践
2017/04/29 Javascript
vue-cli项目使用mock数据的方法(借助express)
2019/04/15 Javascript
JavaScript中的函数申明、函数表达式、箭头函数
2019/12/06 Javascript
Vue filter 过滤当前时间 实现实时更新效果
2019/12/20 Javascript
微信小程序通过websocket实时语音识别的实现代码
2020/08/19 Javascript
[02:53]2018年度DOTA2最佳战队-完美盛典
2018/12/17 DOTA
使用Python编写一个简单的tic-tac-toe游戏的教程
2015/04/16 Python
Python中集合的内建函数和内建方法学习教程
2015/08/19 Python
Python如何获取系统iops示例代码
2016/09/06 Python
Python随机函数random()使用方法小结
2018/04/29 Python
Python子类继承父类构造函数详解
2019/02/19 Python
Python devel安装失败问题解决方案
2020/06/09 Python
解决Pycharm 中遇到Unresolved reference 'sklearn'的问题
2020/07/13 Python
幼儿园教师国培感言
2014/02/02 职场文书
大班下学期幼儿评语
2014/12/30 职场文书
2015年护士工作总结范文
2015/03/31 职场文书
2015年客房服务员工作总结
2015/05/15 职场文书
市级三好生竞选稿
2015/11/21 职场文书
python实战之一步一步教你绘制小猪佩奇
2021/04/22 Python