Mysql学习之各种锁

本文探讨了MySQL中事务的隔离性和锁的实现,重点介绍了InnoDB的行锁、间隙锁、临键锁以及乐观锁和悲观锁的概念,讨论了它们的优缺点、应用场景和死锁处理方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

事务的隔离性由锁来实现
在这里插入图片描述
MySQL并发事务访问相同记录
并发事务访问相同记录的情况大致可以分为3种:
读-读的情况
读-读情况,即并发事务相继读取相同的记录。读取操作本身不会对记录由有任何的影响,并不会引起什么问题,所以允许这种情况的发生。
写-写的情况
写-写情况,即并发事务相继对相同的记录做出改动。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
读-写或写-读情况
读-写或写-读,即一个事务进行读取操作,另一个进行改动操作,这种情况下产生脏读、不可重复读、幻读的问题
怎么解决脏读、不可重复读、幻读这些问题呢?其实有两种可选的解决方案:
方案1:读操作利用多版本并发控制(MVCC) ,写操作利用加锁
在这里插入图片描述
方案2:读、写操作都采用加锁的方式
在这里插入图片描述
锁的不同角度分类
在这里插入图片描述
从数据操作的类型划分:读锁、写锁
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
从数据操作的粒度划分:表级锁、页级锁、行锁
在这里插入图片描述
在这里插入图片描述
表级锁
在这里插入图片描述
意向锁
在这里插入图片描述
意向锁要解决的问题
在这里插入图片描述
在这里插入图片描述
意向锁的并发性
意向锁不会与行级的共享/排他锁互斥!正因为如此,意向锁并不会影响到多个事务对不同数据行加排他锁时的并发性(不然我们直接用普通的表锁就行了)
在这里插入图片描述
自增锁(AUTO-INC锁)
在这里插入图片描述
元数据锁
在这里插入图片描述
在这里插入图片描述

InnoDB中的行锁

行锁(Row Lock)也称为记录锁,顾名思义,就是锁住某一行(某条记录Row)。需要注意的是,Mysql服务器并没有实现行锁机制,行级锁只在存储引擎层实现。
优点:锁定力度小,发生锁冲突概率低,可以实现的并发度高
缺点:对于锁的开销比较大,加锁会比较慢,容易出现死锁
InnoDB与MyISAM的最大不同有两点:一是支持事务(TRANSACTION);二是采用行级锁

记录锁(Record Locks)
在这里插入图片描述在这里插入图片描述
间隙锁(Gap Locks)
Mysql在Repeatable Read 隔离界别下是可以解决幻读问题的,解决方案有两种,可以采用MVCC方案解决,也可以采用加锁方案解决。但是在使用加锁方案解决时有个一大问题,就是事务在第一次执行读取操作的时候,那些幻影记录尚不存在,我们无法给这些幻影记录 加上记录锁。InnoDB提出了一种称之为Gap Locks的锁,官方的类型名称为:LOCK_GAP
在这里插入图片描述
id值为8的记录加了gap锁,意味着不允许别的事务在id值为8的记录前边的间隙插入新的记录
gap锁的提出仅仅是为了防止插入幻影记录而提出的,虽然有共享gap锁独占gap锁的说法,但是他们起到的作用是相同的。而且如果对一条记录加了gap锁(不论是共享gap锁还是独占gap锁),并不会限制其他事务对这条记录加记录锁或者继续加gap锁。
在这里插入图片描述
在这里插入图片描述
间隙锁扩大了锁住的范围,可能会引起死锁
在这里插入图片描述

临键锁(Next-Key Locks)
临键锁=记录锁+间隙锁
在这里插入图片描述
插入意向锁(Insert Intention Locks)
在这里插入图片描述
在这里插入图片描述在这里插入图片描述页锁
在这里插入图片描述

从对待锁的态度划分为:乐观锁、悲观锁

从对待锁的态度来看锁的话,可以将锁分为乐观锁和悲观锁,从名字可以看出这两种锁是两种看待数据并发的思维方式。需要注意的是,乐观锁和悲观锁并不是锁,而是锁的设计思想

悲观锁(数据库的锁层面) 在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
乐观锁(程序层面)

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
两种锁的适用场景
在这里插入图片描述

按照加锁的方式划分:显式锁、隐式锁

隐式锁
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
其他锁之-全局锁
在这里插入图片描述
其他锁之死锁
两个事务都持有对方需要的锁,并且在等待对方释放,并且双方都不会释放自己的锁

在这里插入图片描述

在这里插入图片描述
如何处理死锁
方式1:等待,直到超时(innodb_lock_wait_timeout=50s)
在这里插入图片描述
方式2:使用死锁检测进行死锁处理
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

锁的内存结构

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
type_mode的信息

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
锁监控

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

视频链接:B站

<think>我们根据用户提供的引用资料,重点展示MySQL中各种的实际SQL操作示例。用户意图是通过SQL示例学习机制的使用,因此我们将以实际可运行的SQL语句演示为主,并配合必要的解释。 注意:以下示例基于InnoDB存储引擎(MySQL 5.7+),并在REPEATABLE READ隔离级别下测试(默认级别)。</think>### MySQL机制SQL操作示例 以下通过具体SQL演示各类的使用场景和效果,基于InnoDB引擎(默认隔离级别REPEATABLE READ)[^1][^2]。 --- #### 一、表级操作 **1. 手动加表** ```sql -- 会话1:加读(允许其他会话读,禁止写) LOCK TABLES user READ; -- 成功定[^1] -- 会话2尝试写入(被阻塞) UPDATE user SET mobile_num=123456 WHERE id=1; -- 状态:Waiting for table write lock [^3] ``` **2. 写独占演示** ```sql -- 会话1:加写(完全独占) LOCK TABLES user WRITE; -- 会话2所有操作均被阻塞 SELECT * FROM user; -- 状态:Waiting for table read lock ``` **释放** ```sql UNLOCK TABLES; -- 所有会话释放表[^1] ``` --- #### 二、行级操作 **1. 共享(S Lock)** ```sql -- 会话1:对id=1加共享 SELECT * FROM user WHERE id=1 LOCK IN SHARE MODE; -- 成功[^2] -- 会话2可读但不可写 SELECT * FROM user WHERE id=1; -- 允许 UPDATE user SET mobile_num=111 WHERE id=1; -- 阻塞! ``` **2. 排他(X Lock)** ```sql -- 会话1:对user_id=100加排他 SELECT * FROM user WHERE user_id=100 FOR UPDATE; -- 通过唯一索引定[^2] -- 会话2所有请求均失败 SELECT * FROM user WHERE user_id=100 LOCK IN SHARE MODE; -- 阻塞 UPDATE user SET mobile_num=222 WHERE user_id=100; -- 阻塞 ``` --- #### 三、间隙演示 **表结构及数据** ```sql CREATE TABLE `score` ( `id` INT PRIMARY KEY, `score` INT, KEY `idx_score` (`score`) ); -- 数据:score值(60,70,80,90) ``` **定区间防止幻读** ```sql -- 会话1:定70~90的间隙 SELECT * FROM score WHERE score BETWEEN 70 AND 90 FOR UPDATE; -- 定(70,80),(80,90)两个区间[^2] -- 会话2插入被阻塞(即使记录不存在) INSERT INTO score VALUES (5,75); -- 阻塞在间隙(70,80) ``` --- #### 四、元数据(MDL)冲突 ```sql -- 会话1:长查询(持有MDL读) SELECT SLEEP(30) FROM user; -- 模拟长查询 -- 会话2修改表结构被阻塞 ALTER TABLE user ADD COLUMN age INT; -- 状态:Waiting for table metadata lock[^3] ``` --- #### 五、死产生与解决 **死场景** ```sql -- 会话1 START TRANSACTION; UPDATE user SET mobile_num=111 WHERE id=1; -- 持有id=1的X -- 会话2 START TRANSACTION; UPDATE user SET mobile_num=222 WHERE id=2; -- 持有id=2的X -- 会话1尝试id=2 UPDATE user SET mobile_num=333 WHERE id=2; -- 等待会话2释放 -- 会话2尝试id=1 UPDATE user SET mobile_num=444 WHERE id=1; -- 死!自动回滚 ``` **检测死** ```sql SHOW ENGINE INNODB STATUS; -- 查看LATEST DETECTED DEADLOCK ``` --- ### 使用建议 1. **索引优化**: ```sql -- 无索引时行升级为表 UPDATE user SET mobile_num=999 WHERE mobile_num=555888999; -- 非索引字段 ``` 2. **控制事务时长**: ```sql SET SESSION innodb_lock_wait_timeout=30; -- 设置等待超时 ``` 3. **避免间隙影响**: ```sql SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED; -- 切换隔离级别 ``` > **关键提示**:行依赖索引有效性,表会破坏并发性,长事务是冲突的主因[^1][^2]。 --- ### 相关问题 1. 如何监控MySQL中的等待情况? 2. 在READ COMMITTED隔离级别下间隙的行为有何变化? 3. 如何安全地解除长时间阻塞的`FLUSH TABLES WITH READ LOCK`? 4. 无主键表对InnoDB机制有什么影响? 5. 如何通过性能Schema分析争用问题? [^1]: 表级操作语法 [^2]: 行与间隙实现原理 [^3]: 等待状态识别
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值