MySQL锁机制


Posted in MySQL onApril 05, 2021

锁是计算机协调多个线程访问同一共享资源的机制,主要用于解决多线程访问同一共享资源的并发安全问题。
MySQL锁机制

锁分类

(1)从性能分为:乐观锁和悲观锁
MySQL中采用版本号控制,JVM中使用CAS

(2)从数据库操作类型分为:读锁(共享锁)、写锁(排他锁) 【读锁和写锁都属于悲观锁】

  • 读锁(共享锁):针对相同行记录,多个读操作可以同时进行,但任何事务都不能进行
  • 写锁(排他锁):获取排他锁的事务既能读数据,又能修改数据。获取到写锁的事务为完成之前,会阻塞其他事务获取写锁或读锁。

(3)从颗粒度划分为,表锁和行锁

注意:
(1)读锁、写锁都属于行级锁,即事务1 对商品A 获取写锁,和事务2对商品B 获取写锁互相不会阻塞的。
(2)如果sql语句使用了行锁,当SQL未使用索引而使用全表扫描的时候,行级锁会变成表锁。

(3)锁只有在执行commit或者rollback的时候才会释放,并且所有的锁都是在同一时刻被释放。
(4)Innodb引擎执行普通的修改、删除sql语句时,会给修改和删除的行加行锁。

共享锁(读锁,读写互斥,读读互不影响)

事务A使用共享锁获取了某条(或某些)记录时,事务B可以读取这些记录,可以继续添加共享锁,但是不能修改这些记录(当事务C对这些数据修改或删除时,会进入阻塞状态,直至锁等待超时或者事务A提交)

  • 如何使用共享锁和释放共享锁
# 加锁
SELECT ... LOCK IN SHARE MODE
# 释放锁
commit;
rollback;
  • 作用
    SELECT … LOCK IN SHARE MODE在读取的若干行记录加共享锁(share lock),其他事务只能对这些行查询而不能修改这些记录。多个事务可以对相同的行记录加共享锁,因此获取了共享锁的事务不一定能修改行数据

  • 使用场景: 读取结果集的最新版本,同时防止其他事务产生更新该结果集
    例如:并发下对商品库存的操作

排他锁(写锁,独占锁)

select … for update在对读取的行记录加排他锁,只允许获取到该排他锁的事务修改该行记录,防止其他事务对该行进行修改,就像普通的update语句执行时会加行锁一样。

  • 如何使用排他锁
# 加排他锁
select ... for update //排他锁 

# 释放锁
commit;
rollback;

共享锁和排他锁的区别

(1)一旦某个事务获取了排他锁,其他事务就无法再获取排他锁。
多个事务可以对相同行数据加共享锁。
(2)给指定行加了共享锁的事务,不一定能修改该行数据,因为其他事务也可能对该行加了共享锁或排他锁; 给指定行加了排他锁的事务,则可以修改加了排他锁的该行数据

表锁

使用表级锁定的主要是MyISAM,MEMORY,CSV等一些非事务性存储引擎。
表锁一般在数据迁移时使用。

意向锁:意向共享锁和意向排他锁

某行加共享锁的前提是:该行数据所在表会先获取到意向共享锁。
某行加排他锁的前提是:该行数据所在表会先获取到意向排他锁。
注意:意向共享锁和意向排他锁均是表锁,无法手动创建。

为什么需要加入意向锁

意向锁是为了告知mysql该表已经存在数据被加锁,而不需要逐行扫描是否加锁,提搞加锁的效率。

单个表锁定

lock tables saas.goods read,saas.account write;  // 给saas库中的goods表加读锁,account表加写锁
unlock tables;   //解锁

全局表锁定

FLUSH TABLES WITH READ LOCK;   // 所有库所有表都被锁定只读
unlock tables;         //解锁

注意: 在客户端和数据库断开连接时,都会隐式的执行unlock tables。如果要让表锁定生效就必须一直保持连接。

  • 表级锁:开销小,加锁快;不会出现死锁;锁定粒度大,发生锁冲突的概率最高,并发度最低;
    行级锁:开销大,加锁慢;会出现死锁;锁定粒度最小,发生锁冲突的概率最低,并发度也最高;

行锁

  • 行锁是锁一行或者多行记录
  • MySQL的行锁是基于索引,行锁是加在索引上,而不是加在行记录上的。
    MySQL锁机制
    如上图所示,数据库中有1个主键索引和1个普通索引,图中的sql语句是基于普通索引查询,命中4条记录,此时一把行锁就锁定两条记录,而其他事务修改这两条记录中的任意一条,都会一直阻塞【获取锁的事务没有执行commit之前】,下图就是上图没有执行commit语句时的情况。
    MySQL锁机制

行锁升级为表锁(行锁实际是给索引加锁,如果没用索引而全表扫描,则会给全表加锁)

MySQL锁机制
上图中where条件中,虽然template_name建立普通索引,但使用or关键字,导致template_name的索引失效,从而进行了全表扫描,锁定了整张表。

修改、删除某一行记录,且未提交事务时,该行会一直被行锁锁定

MySQL锁机制
窗口1中删除某一行,但没有提交。窗口2中更新该行会一直处于阻塞中。
MySQL锁机制

记录锁

  • 行锁:行锁是命中索引,一把锁锁的是一张表的一条记录或多条记录
  • 记录锁:记录锁是在行锁的衍生锁,记录锁锁的是表中的某一条记录,记录锁出现的条件必须是:精确命中索引,且索引是唯一索引(比如主键id、唯一索引列)。

间隙锁(Gap Locks)

经典参考文章

间隙锁的作用:防止幻读

间隙锁的目的是为了防止幻读,其主要通过两个方面实现这个目的:
(1)防止止间隙内有新数据被插入
(2)防止范围内已存在的数据被更新

innodb自动使用间隙锁的条件

(1)数据隔离级别必须为可重复读
(2)检索条件必须使用索引(没有使用索引的话,mysql会全表扫描,那样会锁定整张表所有的记录,包括不存在的记录,此时其他事务不能修改不能删除不能添加)

间隙锁锁定的区域

根据检索条件向左寻找最靠近检索条件的记录值A,作为左区间,向右寻找最靠近检索条件的记录值B作为右区间,即锁定的间隙为(A,B)。下图中,where number=5的话,那么间隙锁的区间范围为[4,11];
MySQL锁机制

session 1:
start  transaction ;
触发间隙锁的方式1:
select  * from news where number=4 for update ;
触发间隙锁的方式2:
update news set number=3 where number=4; 

session 2:
start  transaction ;
insert into news value(2,4);#(阻塞)
insert into news value(2,2);#(阻塞)
insert into news value(4,4);#(阻塞)
insert into news value(4,5);#(阻塞)
insert into news value(7,5);#(执行成功)
insert into news value(9,5);#(执行成功)
insert into news value(11,5);#(执行成功)

next-key锁【临键锁】

next-key锁其实包含了记录锁和间隙锁,即锁定一个范围,并且锁定记录本身。InnoDB默认加锁方式是next-key 锁。

select * from news where number=4 for update ;

MySQL锁机制

next-key锁锁定的范围为间隙锁+记录锁,即区间(2,4),(4,5)加间隙锁,同时number=4的记录加记录锁,即next-key锁的锁定的范围为(2,4],(4,5]。

记录锁、间隙锁、临间锁的区别

MySQL锁机制

update news  set number=0 where id>15

sql默认加的是next-key锁。根据上图,next-key锁的区间为(-∞,1],(1,5],(5,9],(9,11],(11,+∞),上面id>15,实际上next-key锁是加在[11,+∞)这个范围内,而不是(15,+∞)这个范围内。注意:需要使用锁的字段必须加索引,因为锁是加在索引上的,没有索引则加的表锁。

MySQL 相关文章推荐
MySql学习笔记之事务隔离级别详解
May 12 MySQL
Mysql基础知识点汇总
May 26 MySQL
MySQL中distinct与group by之间的性能进行比较
May 26 MySQL
一文读懂navicat for mysql基础知识
May 31 MySQL
详细谈谈MYSQL中的COLLATE是什么
Jun 11 MySQL
分析mysql中一条SQL查询语句是如何执行的
Jun 21 MySQL
MySQL空间数据存储及函数
Sep 25 MySQL
MySQL8.0升级的踩坑历险记
Nov 01 MySQL
MySQL创建定时任务
Jan 22 MySQL
MySQL派生表联表查询实战过程
Mar 20 MySQL
Mysql超详细讲解死锁问题的理解
Apr 01 MySQL
MySQL分布式恢复进阶
Jul 23 MySQL
MySQL基础(一)
Apr 05 #MySQL
MySQL基础(二)
MySQL学习总结-基础架构概述
MySQL令人咋舌的隐式转换
Apr 05 #MySQL
mysql知识点整理
Apr 05 #MySQL
浅析InnoDB索引结构
Apr 05 #MySQL
MySQL入门命令之函数-单行函数-流程控制函数
Apr 05 #MySQL
You might like
php中PDO方式实现数据库的增删改查
2015/05/17 PHP
PHP调用存储过程返回值不一致问题的解决方法分析
2016/04/26 PHP
Laravel 5.4因特殊字段太长导致migrations报错的解决
2017/10/22 PHP
php获取微信共享收货地址的方法
2017/12/21 PHP
js性能优化 如何更快速加载你的JavaScript页面
2012/03/17 Javascript
JS实现将人民币金额转换为大写的示例代码
2014/02/13 Javascript
jQuery中noconflict函数的实现原理分解
2015/02/03 Javascript
分离与继承的思想实现图片上传后的预览功能:ImageUploadView
2016/04/07 Javascript
Node.js connect ECONNREFUSED错误解决办法
2016/09/15 Javascript
javascript之with的使用(阿里云、淘宝使用代码分析)
2016/10/11 Javascript
js正则表达式最长匹配(贪婪匹配)和最短匹配(懒惰匹配)用法分析
2016/12/27 Javascript
vue模板语法-插值详解
2017/03/06 Javascript
Three.js利用性能插件stats实现性能监听的方法
2017/09/25 Javascript
使用ajax的post同步执行(实现方法)
2017/12/21 Javascript
jquery+php后台实现省市区联动功能示例
2019/05/23 jQuery
vue 中 elment-ui table合并上下两行相同数据单元格
2019/12/26 Javascript
JavaScript实现轮播图片完整代码
2020/03/07 Javascript
ng-alain的sf如何自定义部件的流程
2020/06/12 Javascript
javascript实现时间日期的格式化的方法汇总
2020/08/06 Javascript
python实现去除下载电影和电视剧文件名中的多余字符的方法
2014/09/23 Python
Python实现的弹球小游戏示例
2017/08/01 Python
python脚本生成caffe train_list.txt的方法
2018/04/27 Python
Python 编码规范(Google Python Style Guide)
2018/05/05 Python
Python对象中__del__方法起作用的条件详解
2018/11/01 Python
python 用for循环实现1~n求和的实例
2019/02/01 Python
python list数据等间隔抽取并新建list存储的例子
2019/11/27 Python
使用keras框架cnn+ctc_loss识别不定长字符图片操作
2020/06/29 Python
python七种方法判断字符串是否包含子串
2020/08/18 Python
EJB的基本架构
2016/09/22 面试题
药学职务聘任书
2014/03/29 职场文书
门市房租房协议书
2014/12/04 职场文书
学习保证书100字
2015/02/26 职场文书
JavaScript组合继承详解
2021/11/07 Javascript
20180830晚上第一届KSL半决赛 雨神vs解冻(二龙 三炮解说)
2022/04/01 星际争霸
Python OpenCV实现图像模板匹配详解
2022/04/07 Python
win10重装系统后上不了网怎么办 win10重装系统网络故障的解决办法
2022/07/23 数码科技