1.常见问题
-
MySQL支持的锁有哪些?有哪些使用场景?
- 什么是读写锁?什么是排他锁?
- 行锁是什么?有哪些分类,原理是什么?
-
死锁是如何产生的?
- 如何解决死锁?
2.锁的分类
从锁的粒度上分MySQL支持的锁
- 表级锁
- 行级锁(InnoDB)
- 页级锁(BDB)
从锁的操作上可以分为
- 读锁
- 写锁
从实现方式上分
- 乐观锁
- 悲观锁
使用场景
-
修改数据库表结构会自动加表级锁——元数据锁
-
更新数据未使用索引,行级锁会上升为表级别锁
-
更新数据使用索引会使用行级锁
-
select ... for update 会使用行级锁
3.表级锁的使用
MySQL Server 层实现
每次操作锁住整张表。锁定粒度大,发生锁冲突的概率最高,并发度最低
应用在MyISAM,InnoDB,BDB等存储引擎中。
MySQL的表级锁有两种:
- 一种是表锁
- 一种是元数据锁(meta data lock,MDL)
表锁有两种表现形式:
- 表共享读锁(Table Read Lock)
- 表独占写锁(Table Write Lock)
表读锁
表写锁
4.元数据锁
MDL(metaDataLock)元数据:表结构
在MySQL5.5 版本中引入了MDL,当对一个表做增删改查操作的时候,加MDL 读锁;当要对表结构变更操作的时候,加MDL写锁。
元数据读锁
5.行级锁
InnoDB实现
行级锁:每次操作锁住一行数据。锁粒度最小,发生锁冲突的概率最低,并发度最高。应用在InnoDB存储引擎中。
RecordLock锁:锁定单个行记录的锁。RC,RR隔离级别都支持。
GapLock锁:间隙锁,锁定索引记录间隙,确保索引记录的间隙不变。RR隔离级别支持。
Next-key Lock锁:行锁和间隙锁组合,同时锁住数据,并且锁住数据前面的Gap。RR隔离级别支持。
行读锁
行读锁升级为表锁
常见行锁升级为表锁举例
session1:
begin;
update mylock set age=10 where name='y';
commit;
name 没有索引,update会锁定整张mylock表,造成其他的访问mylock的请求全部等待
行写锁
3,session2:select * from mylock where id =1; --可以读 不加锁。是因为MVCC(针对行)机制,多版本读。
4,session2:select * from mylock where id =1 lock in share mode; --加了读锁,就会被阻塞(加写锁 for update 一样会被阻塞,单纯的select 是无锁的,才可以MVCC多版本读)。
6.什么是读写锁?什么是排他锁?
锁是计算机协调多个进程或者线程并发访问某一资源的机制。锁使用独占的方式来保证在只有一个版本的情况下事务之间相互隔离,所以锁可以理解为单版本控制。
引入锁之后就可以支持并行处理事务,如果事务之间涉及到相同的数据项时,会使用排他锁,或叫互斥锁,先进入的事务独占数据项以后,其他事务被阻塞,等待前面的事务释放锁。
读写锁,可以让读和读并行,而读和写,写和读,写和写这几种之间还是要加排他锁。
排他锁
引入锁之后就可以支持并行处理事务,如果事务之间涉及到相同的数据项时,会使用排他锁,或叫互斥锁,先进入的事务独占数据项以后,其他事务被阻塞,等待前面的事务释放锁。
注意,在整个事务1结束之前,锁是不会被释放的,所以,事务2必须等到事务1结束之后开始。
读写锁
读写锁,可以让读和读并行,而读和写,写和读,写和写这几种之间还是要加排他锁 。
如果几个事务之间没有共享数据项,完全可以并行被处理
7.行锁是什么?有那些分类,原理是什么?
InnoDB存储引擎实现,每次操作锁住一行数据。锁定粒度最小,发生锁冲突的概率最低,并发度最高。
InnoDB的行级锁,按照锁定范围来说,分为三种:
-
RecordLock锁:锁定单个行记录的锁。RC,RR隔离级别都是支持。
-
GapLock锁:间隙锁,锁定索引记录间隙,确保索引记录的间隙不变,RR隔离级别支持。
-
Next-key Lock 锁:行锁和间隙锁组合,同时锁住数据,并且锁住数据前面的Gap。RR隔离级别支持。
行级锁分类
按照功能来说分为两种:
-
共享读锁(S):允许一个事务去读一行,阻止其他事务获得相同数据集的排他锁。
-
排他写锁(X):允许获得排他写锁的事务(当前获得锁的事务)更新数据,阻止其他事务取得相同数据集的共享读锁(是指select ... lock in share mode 不是单纯的select读,单纯的select读没有锁, 这里会采用MVCC机制)和排他写锁(DML(insert,update,delete),select ... for update)。
行锁原理
-
主键加锁
加锁行为:仅在id=10的主键索引记录录入加X锁。
-
唯一键加锁
加锁行为:先在唯一索引id上加 id=10的X 锁,再在id=10的主键索引记录上加X锁。
-
非唯一键加锁
加锁行为:对满足id=10条件的记录和主键分别加X锁,然后在(6,c)(10,b),(10,b)(10,d),(10,d)~(11,f)间隙分别加Gap锁。
-
无索引加锁
加锁行为:表里面所有行和间隙均加X锁。由于InnoDB引擎行锁机制是基于索引实现记录锁定的,因此没有索引时会导致全表锁定,这点大家要特别注意。
8.死锁是如何产生的?如何解决?
加锁是实现数据库并发控制的一个非常重要的技术。当两个事务的锁发生冲突,互相等待对方的锁释放,不能继续执行事务逻辑,就会出现死锁,严重影响应用的正常执行。
死锁的现象主要有:
- 表锁死锁
- 行级锁死锁
- 共享锁转换为排他锁
表锁死锁
产生原因:用户A由于用户B已经锁住表B,它必须等待用户B释放表B才能继续,同样用户B要等用户A释放表A才能继续,这样死锁就产生了
解决方案:
- 调整程序的逻辑
- 尽量避免同时锁定两个资源
行级锁死锁
产生原因1:
在事务执行了一条不满足条件的 for update操作,则执行全表扫描,把行级锁上升为表级锁,多个这样的事务执行后,就很容易产生死锁和阻塞。
解决方案:SQL语句不要使用太复杂的关联多表的查询,优化索引。
产生原因2:
两个事务分别想拿到对方持有的锁,互相等待,于是产生死锁。
解决方案:
在同一个事务中,尽可能做到一次锁定所需要的所有资源
按照id对资源排序,然后按照顺序进行处理
采用MVCC机制处理
共享锁转换为排他锁
产生原因:
事务A查询一条记录,然后更新该条记录;
此时事务B也更新该条记录,这时事务B的排他锁由于事务A有共享锁,必须等A释放共享锁后才可以获取,只能排队等待,事务A再执行更新操作时,此处发生死锁,因为事务A需要排他锁来做更新操作。但是,无法授予该锁请求,因为事务B已经有一个排他锁请求,并且正在等待事务A 释放其共享锁。
解决方案:
避免引发同时对同一条记录多次操作
使用乐观锁进行控制