全局锁的使用
flush tables with read lock
在执行全局锁之后,整个数据库就处于只读状态,其他线程在执行数据的更新或对表结构作更新操作时会发生阻塞.
释放全局锁
unlock tables
全局锁主要适用于全库逻辑备份.在对整个库进行备份时,全局锁能够保证在备份期间不会因为数据或表的更新使备份数据和预期数据不一致.
全局锁的缺点是在加上全局锁时,整个数据库处于只读状态,在备份期间会造成业务停滞. 解决方案是开启支持可重复读隔离级别的事务.开启可重复读会在开启事务时创建Read View,整个事务过程中操作的是Read View,因此即使其他事务对数据进行更新也不影响该事务的进行.
表级锁:对非索引字段加锁,对当前操作的整张表加锁.
表锁
元数据锁
意向锁
AUTO-INC锁
加锁方式
//表级别的共享锁,读锁
lock tables 表名 read
//表级别的独占锁,写锁
lock tables 表名 write
表锁不仅会阻塞其他线程,同样也会阻塞该线程下的操作
元数据锁就是对表进行加锁.保证在事务执行期间,表的结构不会因为其他事务而发生改变.
元数据锁主要是针对DDL(针对表结构)和DML(针对记录)去进行加锁的,对DDL加写锁,对DML加读锁
元数据锁默认是开启状态,在事务开启时开启,在事务提交后释放
元数据锁可能造成线程堆积:当某个**长事务(事务开启后一直没有提交)**开启元数据读锁时,其他线程在执行写操作时会发生阻塞,但执行读操作不会发生阻塞,但当写操作阻塞后,如果再对数据进行读操作时就会发生阻塞,因为元数据锁会维护一个阻塞队列,在该队列中,写操作的优先级要高于读操作.
表锁和行锁之间会发生冲突,在添加表锁时需要检测该表中每个记录是否有添加行锁,如果添加了行锁,则不能再添加表锁.而如果每次添加表锁时都对表中的记录遍历,效率会降低.因此引出了意向锁
意向锁也是一种表级锁.在InnoDB中,在对记录加共享锁之前,需要先在表级别加上一个意向共享锁;在对记录加独占锁之前,需要先在表级别加上一个意向独占锁
在添加表锁之前先判断是否存在意向锁,如果存在则放弃加表锁,否则进行等待.
普通的select是利用MVCC实现一致读,不涉及锁.
AUTO-INCREMENT是基于AUTO-INC锁实现自增的.在执行插入语句时会添加AUTO-INC锁,在插入语句结束后会释放AUTO-INC锁.
一个事务在持有AUTO-INC锁时,其他事务在对数据进行更新操作时会发生阻塞.InnoDB为了提高效率,引入了一种轻量级锁.在插入数据时,InnoDB会为AUTO-INCREMENT修饰的字段添加轻量级锁,然后赋予一个自增的值,之后无须等待整个插入语句执行完就可以释放该轻量级锁
行级锁是针对索引字段加的锁,只针对当前操作的记录进行加锁
行级锁的类型
Record Lock:记录锁,锁的范围是一条记录
Gap Lock:间隙锁,锁定一个范围,但是不包含记录本身
Next-Key Lock:上述两种锁的组合,锁定一个范围,包括记录本身
对记录加锁的基本单位是next-key lock,由记录锁和间隙锁组成.next-key lock是前开后闭区间.间隙锁是前开后开区间
使用唯一索引等值查询时,当记录存在时next-key lock会退化成记录锁;当记录不存在时next-key lock会退化成间隙锁
按照唯一索引的退化原则对范围内的数据一条一条查询,一条一条加锁,当某条记录存在时,退化成记录锁,只锁这一个记录,当记录不存在是,退化成间隙锁,锁住两个记录之间的间隙
使用非唯一索引等值查询时,当记录存在时,会加上next-key lock,并且额外加上一把间隙锁;当记录不存在时,只会加next-key lock并且会退化成间隙锁
当记录存在时,首先先对查到的记录的普通索引加next-key lock锁,间隙锁加锁的规则是向下遍历第一个不符合条件的值停止.因此或有两个加锁的范围.
当记录不存在时,会给普通索引加next-key lock,同时next-key lock会退化成间隙锁
与唯一索引范围查询类似.不同的是非唯一索引范围查询不会像唯一索引范围查询一样退化成记录锁或间隙锁,会对每条遍历的记录加next-key lock
当使用update语句时where子句的条件中没有索引列时,会对表中所有记录加next-key lock,因此相当于对整个表加锁,但不是加表锁
Insert语句正常执行时不会生成锁结构,它是靠聚簇索引自带的trx_id隐藏列来作为隐式锁
隐式锁:是InnoDB的一种延迟加锁机制,当前锁如果不会发生冲突就不添加锁,当锁冲突时将隐式锁转为显式锁
隐式锁转为显式锁的情况
上述情景下会发生什么情况
会发生阻塞,事务A会阻塞在time3时刻,事务B会阻塞在time4状态.
首先time1时刻事务A对id=25的记录执行更新操作,对记录操作时,会触发next-key lock,由于表中没有id=25的记录,所以会退化成间隙锁,锁住(20,30)
然后time2时刻事务B对id=26的记录执行更新操作,与time1相同,事务B会对(20,30)的记录加间隙锁
time3时刻执行插入语句(25),在插入前判断待插入的下一条记录间是否有间隙锁(防止发生幻读),如果没有则正常插入,如果有,等待对方释放锁后再插入,同时会加上插入意向锁.
time4时刻与time3时刻一样.
因此事务A和事务B都处于等待对方释放间隙锁的状态,从而造成了死锁