MySQL锁问题

tech2025-06-11  4

锁机制出现的背景

最大限度的利用数据库的并发访问每个用户以一致的方式读取和修改数据

什么是锁

用于管理对共享资源的并发访问,从而保证数据的完整性和一致性

锁结构

一个事务想对某条记录做改动时,首先会看看内存中有没有与这条记录关联的锁结构,当没有的时候就会在内存中生成一个锁结构与之关联。 比方说事务T1要对这条记录做改动,就需要生成一个锁结构与之关联:

trx信息:代表这个锁结构由哪个事务生成的is_waiting:代表当前事务是否等待

几种加锁说法

不加锁:即不生成锁结构加锁成功:生成锁结构,is_waiting是false加锁失败: 生成锁结构,is_waiting是true

MySQL全局锁、表锁、行锁

MySQL全局锁、表锁、行锁

InnoDB存储引擎中的锁

行锁

共享锁(S锁):允许不同事务读同一行数据排他锁(X锁):允许单个事务更新或者删除一行数据

只有同时是共享锁(S锁)的时候兼容,其他组合均互斥,即排他锁(X锁)与 任何锁都不兼容。

表锁

InnoDB 支持多粒度的锁,这种锁允许事务在行级锁和表级锁同时存在。

为支持在不同粒度上进行加锁操作,InnoDB存储引擎支持一种额外的锁方式,称之为意向锁(Intention Lock), 为表级别的锁。

意向共享锁(IS Lock): 当事务需要在某条记录上S锁的时候,需要在表级别加一个IS锁。意向排他锁(IX Lock): 当事务需要在某条记录上X锁的时候,需要在表级别加一个IX锁。

IS、IX锁是表级锁,它们的提出仅仅为了在之后加表级别的S锁和X锁时可以快速判断表中的记录是否被上锁,以避免用遍历的方式来查看表中有没有上锁的记录,也就是说其实IS锁和IX锁是兼容的,IX锁和IX锁是兼容的,本质上只是一种标记。

解决脏读、不可重复读、幻读问题的两种方案

1.读操作通过MVCC进行、写操作进行加锁

MySQL事务隔离级别详解及MVCC实现原理

2.读写操作均采用加锁方式

通过加锁方式,实现操作的排队执行。由于对某行记录加读写锁之后,不能同时修改,所以解决了脏读问题、不可重复读问题,幻读问题,可以通过加间隙锁的方式来解决。

两种读

一致性非锁定读

事务利用MVCC进行的读取操作称之为一致性读,或者一致性无锁读,有的地方也称之为快照读。

所有普通的SELECT语句(plain SELECT)在READ COMMITTED、REPEATABLE READ隔离级别下都算是一致性读,是不会对表中任何记录加锁的。

如果读取的行正在执行DELETE或UPDATE操作,这时读取操作不会因此去等待行上锁的释放

一致性锁定读

InnoDB存储引擎的SELECT操作使用一致性非锁定读,但在某些情况下,用户需要显式地对数据库读取操作进行加锁以保证数据逻辑的一致性。

锁定读的语句:

对读取记录加一个S锁,其他事务只能加S锁,不能加X锁

SELECT ... LOCK IN SHARE MODE;

对读取记录加一个X锁,其他事务不能对已锁定的行加任何锁

SELECT ... FOR UPDATE;

常见写操作

DELETE:定位待删除记录在B+树中位置的过程看成是一个获取X锁的锁定读。UPDATE:键值、空间不变,X锁,键值不变,空间变,首先定位为X锁,插入用隐式锁,键值变,相当于删除,后插入INSERT:新插入一条记录的操作并不加锁,通过隐式锁来保护这条新插入的记录在本事务提交前不被别的事务访问

锁的算法

三种行锁的算法

Record Lock:单个行记录上的锁,总是会锁定索引Gap Lock:间隙锁,锁定一个范围,但不包含记录本身Next-Key Lock∶Gap Lock+Record Lock,锁定一个范围,并且锁定记录本身Insert Intention Locks:插入意向锁,当插入位置被别的事务加了Gap锁的时候,内存中生成的一个锁结构,表明有事务想在某个间隙插入数据,正在等待。不会阻止别的事务继续获取该记录上任何类型的锁。

隐式锁:一般情况下INSERT操作是不加锁的,一个事务对新插入的记录可以不显式的加锁(生成一个锁结构),但是由于事务id这个牛逼的东东的存在,相当于加了一个隐式锁。别的事务在对这条记录加S锁或者X锁时,由于隐式锁的存在,会先帮助当前事务生成一个锁结构,然后自己再生成一个锁结构后进入等待状态。

例如一个索引有10,11,13和20这四个值,那么该索引可能被Next-Key Locking的区间为:(负无穷,10],(10,11] ,(11,13],(13,20],(20,正无穷)

当查询的索引是唯一属性的时候,InnoDB存储引擎会对Next-Key Lock优化,将其降级为Record Lock。

解决幻读问题

MySQL InnoDB的可重复读并不保证避免幻读,需要应用使用加锁读来保证,在可重复度隔离级别下,且禁用innodb_locks_unsafe_for_binlog的情况下,InnoDB存储引擎采用Next-Key Locking机制来避免幻读问题。

InnoDB锁的内存结构

阻塞

因为不同锁之间的兼容性关系,在有些时刻一个事务中的锁需要等待另一个事务中的锁释放它所占用的资源,这就是阻塞。阻塞并不是一件坏事,其是为了确保事务可以并发且正常地运行。

死锁

两个或两个以上的事务在执行过程中,因争夺资源而造成的一种互相等待的现象。

解决死锁问题

超时

等待图

注意:

read uncommitted 读不加锁,写加排他锁,并到事务结束之后释放。
最新回复(0)