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 18 MySQL
MySQL中使用or、in与union all在查询命令下的效率对比
May 26 MySQL
MySQL不使用order by实现排名的三种思路总结
Jun 02 MySQL
mysql配置SSL证书登录的实现
Sep 04 MySQL
面试被问select......for update会锁表还是锁行
Nov 11 MySQL
MySQL表类型 存储引擎 的选择
Nov 11 MySQL
MySQL之MyISAM存储引擎的非聚簇索引详解
Mar 03 MySQL
MySQL 分区表中分区键为什么必须是主键的一部分
Mar 17 MySQL
MySQL磁盘碎片整理实例演示
Apr 03 MySQL
深入理解MySQL中MVCC与BufferPool缓存机制
May 25 MySQL
MySQL解决Navicat设置默认字符串时的报错问题
Jun 16 MySQL
MySQL中dd::columns表结构转table过程及应用详解
Sep 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操作Access类(PHP+ODBC+Access)
2007/01/02 PHP
使用php批量删除数据库下所有前缀为prefix_的表
2014/06/09 PHP
PHP中把有符号整型转换为无符号整型方法
2015/05/27 PHP
PHP格式化MYSQL返回float类型的方法
2016/03/30 PHP
PHP实现基本留言板功能原理与步骤详解
2020/03/26 PHP
php连接mysql之mysql_connect()与mysqli_connect()的区别
2020/07/19 PHP
仿中关村在线首页弹出式广告插件(jQuery版)
2012/05/03 Javascript
浏览器的JavaScript引擎的识别方法
2013/10/20 Javascript
jquery实现点击弹出层效果的简单实例
2014/03/03 Javascript
javascript if条件判断方法小结
2014/05/17 Javascript
jQuery插件开发详细教程
2014/06/06 Javascript
基于jQuery实现选取月份插件附源码下载
2015/12/28 Javascript
BootStrap智能表单实战系列(七)验证的支持
2016/06/13 Javascript
将鼠标焦点定位到文本框最后(代码分享)
2017/01/11 Javascript
js+html获取系统当前时间
2017/11/10 Javascript
Vue.directive 自定义指令的问题小结
2018/03/04 Javascript
react-native封装插件swiper的使用方法
2018/03/20 Javascript
vue生命周期和react生命周期对比【推荐】
2018/09/19 Javascript
React 使用recharts实现散点地图的示例代码
2018/12/07 Javascript
关于layui时间回显问题的解决方法
2019/09/24 Javascript
[00:11]战神迅矛
2019/03/06 DOTA
在服务器端实现无间断部署Python应用的教程
2015/04/16 Python
使用python读取txt文件的内容,并删除重复的行数方法
2018/04/18 Python
代码详解django中数据库设置
2019/01/28 Python
手机使用python操作图片文件(pydroid3)过程详解
2019/09/25 Python
Django中Q查询及Q()对象 F查询及F()对象用法
2020/07/09 Python
python爬虫利用selenium实现自动翻页爬取某鱼数据的思路详解
2020/12/22 Python
P D PAOLA意大利官网:西班牙著名的珠宝首饰品牌
2019/09/24 全球购物
金蝶的一道SQL笔试题
2012/12/18 面试题
公积金转移接收函
2014/01/11 职场文书
超市5.1促销活动
2014/01/15 职场文书
手工社团活动方案
2014/02/17 职场文书
教师中国梦演讲稿
2014/04/23 职场文书
离婚协议书格式
2015/01/26 职场文书
父亲节感言
2015/08/03 职场文书
nginx作grpc的反向代理踩坑总结
2021/07/07 Servers