mysql-锁
创始人
2025-05-29 00:19:27
0

1.共享与独占

mysql 锁

加锁范围

3
表z的列b是辅助索引,若在会话A中执行下面的SQL语句:

SELECT*FROM z WHERE b=3 FOR UPDATE

很明显,这时SQL语句通过索引列b进行查询,该列不是唯一属性,因此其使用传统的Next-Key Locking技术加锁,并且由于有两个索引,其需要分别进行锁定。对于聚集索引(primay-key a),其仅对列a等于5的索引加上Record Lock。而对于辅助索引b其加上的是Next-Key Lock锁定的范围是(1,3)。特别需要注意的是,InnoDB存储引擎还会对辅助索引下一个键值加上gap lock,即还有一个辅助索引范围为(3,6)的锁

针对 读写 写写 问题 加锁
读锁:共享锁 Share Lock S
写锁:独占锁 Exclusive Lock X

X锁S锁
X锁不兼容不兼容
S锁不兼容兼容

1.1 锁定读

1.1.1 对读锁加S锁

Select ...  LOCK IN SHRAR MODE;

1.1.2 对读锁加X锁

Select .... FOR UPDATE;

1.2 写操作

只有 X锁可以加

  • DELETE
    加X锁,执行DELETE MARK操作
  • UPDATE
    • X锁
  • Insert
    • 一般情况下 不加锁,隐式锁, 没提交前,防止别的数据访问。

2.表及锁

2.1 表锁的S锁 X锁

 LOCK TABLES t READ #:InnoDB存储引擎会对表t加表级别的S锁。LOCK TABLES t WRITE #:InnoDB存储引擎会对表t加表级别的X锁。

3

2.2 意向锁 innodb

意向锁是由存储引擎自己维护的,用户无法手动操作意向锁,在为数据行加共享 / 排他锁之前,InooDB 会先获取该数据行所在数据表的对应意向锁

InnoDB 支持多粒度锁(multiple granularity locking),它允许行级锁与表级锁共存,而 意向锁 就是其中的一种表锁

避免 在添加表锁前 需要一行一行判断是否有锁,引申出意向锁。

2.2.0 分类

  • 意向共享锁 (intention shared lock, IS):事务有意向对表中的某些行加 共享锁 (S锁

    -- 事务要获取某些行的 S 锁,必须先获得表的 IS 锁。 
    -- 会自动加,不用管
    SELECT column FROM table ... LOCK IN SHARE MODE;
    
  • 意向排他锁 (intention exclusive lock, IX):事务有意向对表中的某些行加 排他锁 (X锁)

    -- 事务要获取某些行的 X 锁,必须先获得表的 IX 锁。
    -- 会自动加,不用管
    SELECT column FROM table ... FOR UPDATE;
    

2.2.1 解决的问题

现在有两个事务,分别是T1和T2,其中T2试图在该表级别上应用共享或排它锁,如果没有意向锁存在,那么T2就需要去检查各个页或行是否存在锁;如果存在意向锁,那么此时就会受到由T1控制的表级别意向锁的阻塞。T2在锁定该表前不必检查各个页或行锁,而只需检查表上的意向锁。简单来说就是给更大一级别的空间示意里面是否已经上过锁。

在数据表的场景中,如果我们给某一行数据加上了排它锁,数据库会自动给更大一级的空间,比如数据页或数据表加上意向锁,告诉其他人这个数据页或数据表已经有人上过排它锁了(不这么做的话,想上表锁的那个程序,还要遍历有没有航所),这样当其他人想要获取数据表排它锁的时候,只需要了解是否有人已经获取了这个数据表的意向排他锁即可。

2.2.2 小结

意向锁是怎么解决这个问题的呢?首先,我们需要知道意向锁之间的兼容互斥性,如下所示。
因为具体 行级别的 x锁 可以加在不同的行,那么就有多个不同的意向排它锁,兼容

意向共享锁(lS)意向排他锁(IX)
意向共享锁(IS)兼容兼容
意向排他锁(IX)兼容兼容

意向锁之间是互相兼容的,虽然意向锁和自家兄弟互相兼容,但是它会与普通的排他/共享锁互斥。

意向共享锁(lS)意向排他锁(IX)
共享锁(S)表兼容互斥
排他锁(X)表互斥互斥

2.3 元数据锁 MDL

当对一个表做增删改查的时候,加MDL读锁,当要对表做结构的变更操作的时候,加MDL写锁。

读锁之间不互斥,因此你可以有多个线程同时对一张表增删改查。读写锁之间、写锁之间是互斥的,用来保证变更表结构操作的安全性,解决了DML和DDL操作之间的一致性问题。不需要显式使用,在访问一个表的时候会被自动加上。

3.行锁

3.1 记录锁

记录锁也就是仅仅把一条记录锁上
3

3.2 间隙锁 gap locks

gap锁的提出仅仅是为了防止插入幻影记录而提出的

意味着 不允许别的事务在id值为8的记录前边的间隙插入新记录 ,其实就是
id列的值(3, 8)这个区间的新记录是不允许立即插入的。

两个有界之间的区间

间隙锁解决mvcc
MySQL在REPEATABLE READ隔离级别下是可以解决幻读问题的,解决方案有两种,可以使用MVCC方案解决,也可以采用加锁方案解决。但是在使用加锁方案解决时有个大问题,就是事务在第一次执行读取操作时,那些幻影记录尚不存在,我们无法给这些幻影记录加上记录锁。InnoDB提出了一种称之为Gap Locks的锁,官方的类型名称为:LOCK_GAP,我们可以简称为gap锁。比如,把id值为 8 的那条记录加一个gap锁的示意图如下。
3
图中id值为 8 的记录加了gap锁,意味着不允许别的事务在id值为 8 的记录前边的间隙插入新记录,其实就是id列的值( 3 , 8 )这个区间的新记录是不允许立即插入的。比如,有另外一个事务再想插入一条id值为 4 的新记录,它定位到该条新记录的下一条记录的id值为 8 ,而这条记录上又有一个gap锁,所以就会阻塞插入操作,直到拥有这个gap锁的事务提交了之后,id列的值在区间( 3 , 8 )中的新记录才可以被插入。

3.2.2 问 间隙锁可以叠加

虽然有共享gap锁独占gap锁这样的说法,但是它们起到的作用是相同的。而且如果对一条记录加了gap锁(不论是共享gap锁还是独占gap锁),并不会限制其他事务对这条记录加记录锁或者继续加gap锁

mysql> select * from student;
+----+--------+--------+
| id | name   | class  |
+----+--------+--------+
|  1 | 张三   | 一班   |
|  3 | 李四   | 一班   |
|  8 | 王五   | 二班   |
| 15 | 赵六   | 二班   |
| 20 | 钱七   | 三班   |
+----+--------+--------+
5 rows in set (0.01 sec)
session 1session 2
select *from student where id =5 lock in share mode;
select * from student where id =5 for update;

这里session 2并不会被堵住。因为表里并没有id=5这个记录,因此session 1加的是间隙锁(3,8)。而session 2也是在这个间隙加的间隙锁。它们有共同的目标,即:保护这个间隙,不允许插入值。但,它们之间是不冲突的

3.2.3 可能导致死锁

gap 区间 (3,8)

select * from user where id=5 lock in share  mode; # 1. 间隙锁
insert into user (id,name) values(7,'lisi'); # 4 因为2的间隙锁(锁定 3-8),阻塞
select * from user where id=5 for update; # 2 间隙锁
insert into user (id,name) values(6,'张三'); # 3 应为1的间隙锁 (锁定 3-8), 阻塞住

导致了死锁。

3.3 临键锁 默认

记录锁和间隙锁的合体
有时候我们既想锁住某条记录,又想阻止其他事务在该记录前边的间隙插入新记录,所以InnoDB就提出了一种称之为Next-Key Locks的锁,官方的类型名称为:LOCK_ORDINARY,我们也可以简称为next-key锁。Next-Key Locks是在存储引擎innodb、事务级别在可重复读的情况下使用的数据库锁,innodb默认的锁就是Next-Key locks。

# 区间(3,8]
begin;
select * from student where id <= 8 and id > 3 for update;

111
区间 (8,15]
3

222
2
阻塞

3.4 插入意向锁 事物在等待时也需要锁

3

我们说一个事务在插入一条记录时需要判断一下插入位置是不是被别的事务加了gap锁next-key锁也包含gap锁),如果有的话,插入操作需要等待,直到拥有gap锁的那个事务提交。但是 **InnoDB规定事务在等待的时候也需要在内存中生成一个锁结构** ,表明有事务想在某个间隙插入新记录,但是现在在等待。InnoDB就把这种类型的锁命名为Insert Intention Locks,官方的类型名称为:LOCK_INSERT_INTENTION,我们称为插入意向锁。插入意向锁是一种Gap锁,不是意向锁,在insert
操作时产生。

4.悲观锁与乐观锁

select ..... for update 是悲观锁
因为innodb默认是 临键锁,会把所有扫描的行都加锁。

乐观锁
适用于读多场景。

  1. 版本号机制
    在表中设计一个版本字段 version,第一次读的时候,会获取 version 字段的取值。然后对数据进行更新或删除操作时,会执行UPDATE ... SET version=version+1 WHERE version=version。此时如果已经有事务对这条数据进行了更改,修改就不会成功。
  2. 时间戳机制
    时间戳和版本号机制一样,也是在更新提交的时候,将当前数据的时间戳和更新之前取得的时间戳进行
    比较,如果两者一致则更新成功,否则就是版本冲突。

秒杀案例

乐观锁版本号
2
问题和修改
3
条件库存作为条件
2

5. 显式锁 隐式锁

trx_id roll_pox row_id 三个隐藏字段,是行格式的真实数据,只在聚簇索引中有,
二级索引无

6 死锁

如何处理死锁

查看死锁日志
原因:
相同表的 行锁冲突
间隙锁 死锁 ,降低隔离级别,rr->rc,避免间隙锁。

解决死锁:

  • 超时等待,事物超时自动回滚(innodb_lock_wait_timeout 默认50s)
  • 主动死锁检测,事物请求锁的时候采用 wait-for graph 等待图的方式进行死锁检测(innodb_deadlock_detect 默认on)
show engine innodb status;

2

监控分析锁

# 查询InnoDB锁的整体情况
# 可以重点查看Innodb_row_lock_waits和Innodb_row_lock_time_avg这两个值
# 如果数值较大,说明锁之间的竞争大
show status like 'innodb_row_lock%';mysql> show status like 'innodb_row_lock%';
+-------------------------------+-------+
| Variable_name | Value |
+-------------------------------+-------+
| Innodb_row_lock_current_waits | 0 |
| Innodb_row_lock_time | 0 |
| Innodb_row_lock_time_avg | 0 |
| Innodb_row_lock_time_max | 0 |
| Innodb_row_lock_waits | 0 |
+-------------------------------+-------+
5 rows in set (0.01 sec)
  • Innodb_row_lock_current_waits:当前正在等待锁定的数量;
  • Innodb_row_lock_time:从系统启动到现在锁定总时间长度;(等待总时长)
  • Innodb_row_lock_time_avg:每次等待所花平均时间;(等待平均时长)
  • Innodb_row_lock_time_max:从系统启动到现在等待最常的一次所花的时间;
  • Innodb_row_lock_waits:系统启动后到现在总共等待的次数;(等待总次数)

#可以通过INNODB_TRX、INNODB_LOCKS、INNODB_LOCK_WAITS这三个表
#分析可能存在的锁的问题
select * from information_schema.INNODB_TRX; # 查看所有事物
select * from information_schema.INNODB_LOCKS; # 查看锁
select * from information_schema.INNODB_LOCK_WAITS; # 查看锁等待

相关内容

热门资讯

linux入门---制作进度条 了解缓冲区 我们首先来看看下面的操作: 我们首先创建了一个文件并在这个文件里面添加了...
C++ 机房预约系统(六):学... 8、 学生模块 8.1 学生子菜单、登录和注销 实现步骤: 在Student.cpp的...
JAVA多线程知识整理 Java多线程基础 线程的创建和启动 继承Thread类来创建并启动 自定义Thread类的子类&#...
【洛谷 P1090】[NOIP... [NOIP2004 提高组] 合并果子 / [USACO06NOV] Fence Repair G ...
国民技术LPUART介绍 低功耗通用异步接收器(LPUART) 简介 低功耗通用异步收发器...
城乡供水一体化平台-助力乡村振... 城乡供水一体化管理系统建设方案 城乡供水一体化管理系统是运用云计算、大数据等信息化手段࿰...
程序的循环结构和random库...   第三个参数就是步长     引入文件时记得指明字符格式,否则读入不了 ...
中国版ChatGPT在哪些方面... 目录 一、中国巨大的市场需求 二、中国企业加速创新 三、中国的人工智能发展 四、企业愿景的推进 五、...
报名开启 | 共赴一场 Flu... 2023 年 1 月 25 日,Flutter Forward 大会在肯尼亚首都内罗毕...
汇编00-MASM 和 Vis... Qt源码解析 索引 汇编逆向--- MASM 和 Visual Studio入门 前提知识ÿ...
【简陋Web应用3】实现人脸比... 文章目录🍉 前情提要🌷 效果演示🥝 实现过程1. u...
前缀和与对数器与二分法 1. 前缀和 假设有一个数组,我们想大量频繁的去访问L到R这个区间的和,...
windows安装JDK步骤 一、 下载JDK安装包 下载地址:https://www.oracle.com/jav...
分治法实现合并排序(归并排序)... 🎊【数据结构与算法】专题正在持续更新中,各种数据结构的创建原理与运用✨...
在linux上安装配置node... 目录前言1,关于nodejs2,配置环境变量3,总结 前言...
Linux学习之端口、网络协议... 端口:设备与外界通讯交流的出口 网络协议:   网络协议是指计算机通信网...
Linux内核进程管理并发同步... 并发同步并发 是指在某一时间段内能够处理多个任务的能力,而 并行 是指同一时间能够处理...
opencv学习-HOG LO... 目录1. HOG(Histogram of Oriented Gradients,方向梯度直方图)1...
EEG微状态的功能意义 导读大脑的瞬时全局功能状态反映在其电场结构上。聚类分析方法一致地提取了四种头表面脑电场结构ÿ...
【Unity 手写PBR】Bu... 写在前面 前期积累: GAMES101作业7提高-实现微表面模型你需要了解的知识 【技...