全局锁
什么是全局锁
就是对整个数据库加锁,使整个数据库只能进行读操作,这时其他操作,都会被阻塞
使用全局锁的命令:
flush tables with read lock
释放全局锁的命令:
unlock tables
全局锁什么时候使用:
全局锁主要应用于全局逻辑备份,就是在数据库备份期间使用,用于避免出现因为数据或表结构的更新,而出现备份文件的数据与预期的不一样
使用全局锁会带来什么缺点:
如果数据库里有很多数据,备份就会花费很多的时间,关键是备份期间,业务只能读数据,而不能更新数据,这样会造成业务停滞
既然备份数据库数据的时候,使用全局锁会影响业务,那有什么其他方式可以避免?
有的,如果数据库的引擎支持的事务支持可重复读的隔离级别,那么在备份数据库之前先开启事务,会先创建 Read View,然后整个事务执行期间都在用这个 Read View,而且由于 MVCC 的支持,备份期间业务依然可以对数据进行更新操作。
因为在可重复读的隔离级别下,即使其他事务更新了表的数据,也不会影响备份数据库时的 Read View,这就是事务四大特性中的隔离性,这样备份期间备份的数据一直是在开启事务时的数据
表级锁
mysql中表级锁有以下四种
表锁:
元数据锁(MDL):
意向锁:
AUTO-INC锁:
表锁:
什么是表锁:
表锁就是对数据库中的某张表进行加锁 ,且有两种表锁,第一种就是共享锁(也称读锁),第二种就是独占锁(也称写锁),需要注意的点是,表锁除了会限制别的线程读写外,还会限制本线程接下来的读写操作
如何使用:
使用共享锁: lock tables 表名 read;
使用独占锁:lock tables 表名 write;
两种锁的释放方式相同:unlock tables ;
关闭加锁的线程时也会同时关闭锁
表锁的作用:
表锁是数据库并发控制的重要机制,它通过限制对表的并发访问来维护数据的一致性
表锁的缺点:
因为表锁的颗粒度太大,会影响并发性能。
元数据锁:
什么是元数据锁:
元数据锁,不需要显示调用,因为我们对数据库表进行操作时,会自动给表加上元数据锁,元数据锁有两种
一种是对表进行CRUD(就是增删改查操作),加的是MDL读锁
一种是对表做结构变更操作的时候,加的是MDL写锁
元数据锁的作用:
MDL是为了保证当用户对表执行CRUD操作时,防止其他线程对这个表结构做了变更。
举例:
当有线程在执行select语句(加MDL读锁)的期间,如果有其他线程要更改该表的结构(申请MDL读锁),那么将会被阻塞,直到执行完select语句(释放MDL读锁)
反之当有线程对表结构进行变更(加MDL读锁)的期间,如果有其他线程执行了CRUD操作(申请MDL读锁),那么就会被阻塞,直到表结构变更完成(释放MDL写锁)
注意点:
MDL不需要显示调用,MDL是在事务提交后才会释放,在事务执行期间,MDL是一直持有的
申请MDL锁的操作会形成一个队列,队列种写锁获取优先级高于读锁,一旦出现MDL写锁等待,会阻塞后续该表的所有CRUD操作
意向锁(暂时不太清除)
什么是意向锁:
意向锁是数据库中用于实现层次化锁定协议的一种锁机制。它们InnoDB存储引擎特有的锁类型,用于表示打算在某个数据项上加锁,但尚未实际加锁的状态.意向锁的存在使得数据库
系统能够有效的管理不同层级的锁定操作,同时保证锁的兼容性和一致性
意向锁的作用:
意向共享锁和意向独占锁都是表级锁,不会和行级锁的共享锁和独占锁发生冲突,而意向锁之间也不会发生冲突,只会和共享表锁和独占表锁发生冲突。
表锁和行锁是满足读读共享,读写互斥,写写互斥
AUTO-INC锁
什么是AUTO-INC锁
表里面的主键通常都会设置成自增的,这是通过对主键字段声明AUTO-INCREMENT属性实现的。AUTO-INC锁是特殊的表锁机制,锁不是再一个事务提交后才释放,而是在执行完插入语句后就会立即释放
在插入数据时,会加一个表级别的AUTO-INC锁,等插入语句执行完成后,才会把AUTO-INC锁释。
缺点:
那么,一个事务在持有AUTO-INC锁的过程中,其他事务的如果要向该表插入语句都会被阻塞,从而保证插入数据时,被AUTO-INCREMENT修饰的字段值是连续递增的。
但是,AUTO-INC锁再对大量数据进行插入的时候,会影响插入性能,因为另一个事务中的插入会被阻塞。
解决办法:
在mysql5.1.22版本开始,InooDB存储引擎提供了一种轻量级的锁来实现自增。
工作时间,都是在插入数据时,都是为被AUTO_INCREMENT修饰的字段加锁,但是锁的释放时间不同,轻量级的锁,在为该字段(被AUTO_INCREMENT修饰的字段)
赋值一个自增的值,就把这个轻量级锁释放了,而不需要等待整个插入语句执行完后才释放锁
如何选择使用轻量级锁还是AUTO-INC 锁:
InnoDB存储引擎提供了个innodb_autonic_lock_mode的系统变量,用来控制
innodb_autoinc_lock_mode =0,就采用AUTO-INC锁,语句执行结束后才释放;
innodb_autoinc_lock_mode =2,就采用轻量级锁,申请自增主键后就释放锁,不需要等待语句执行后才释放
innodb_autoinc_lock_mode=1:
普通insert 语句,自增锁在申请之后就马上释放;
类似insert .....select 这样的批量插入数据的语句,自增锁还是要等语句结束后才被释放
当 innodb_autoinc_lock_mode = 2 是性能最高的方式,但是当搭配 binlog 的日志格式是 statement 一起使用的时候,在「主从复制的场景」中会发生数据不一致的问题
举个例子,考虑下面场景:
session A 往表 t 中插入了 4 行数据,然后创建了一个相同结构的表 t2,然后两个 session 同时执行向表 t2 中插入数据。
如果 innodb_autoinc_lock_mode = 2,意味着「申请自增主键后就释放锁,不必等插入语句执行完」。那么就可能出现这样的情况:
- session B 先插入了两个记录,(1,1,1)、(2,2,2);
- 然后,session A 来申请自增 id 得到 id=3,插入了(3,5,5);
- 之后,session B 继续执行,插入两条记录 (4,3,3)、 (5,4,4)。
可以看到,session B 的 insert 语句,生成的 id 不连续。
行级锁
分为三类:
Record Lock ,记录锁,也就是仅仅把一条记录锁上
Gap Lock,间隙锁,锁定一个范围,但是不包含记录本身
Next-Key Lock:Record Lock+Gap Lock的组合,锁定一个范围,并且锁定记录本身
Record Lock
Record Lock 称为记录锁,锁住的是一条记录。而且记录锁是有S锁和X锁之分的:
当一个事务对一条记录加了S型记录锁后,其他事务也可以继续对该记录加S型锁记录(s型和s锁兼容),但是不可以对该记录加x型记录锁
当一个事务对一条记录加了X型记录锁后,其他事务即不可以对该记录加s型锁记录(s型与x锁不兼容),也不可以对该记录加x型记录锁(x型与x锁不兼容)
举个例子,当一个事务执行了下面这条语句:
mysql > begin; mysql > select * from t_test where id = 1 for update;
就是对 t_test 表中主键 id 为 1 的这条记录加上 X 型的记录锁,这样其他事务就无法对这条记录进行修改了。
当事务执行 commit 后,事务过程中生成的锁都会被释放。
Gap Lock
Gap Lock称为间隙锁,只存在于重复读隔离级别,目的是为了解决可重复读隔离级别下幻读的现象。
间隙锁虽然存在x型间隙锁和s型间隙锁,但是并没有什么区别,间隙锁之间是兼容的,即两个事务可以同时持有包含共同间隙范围的间隙锁,并不存在互斥关系,因为间隙锁的目的是防止插入幻读记录而提出的
Next-Key Lock
Next-Key Lock称为临建锁,是Record Lock +Gap Lock的组合,锁定一个范围,并且锁定记录本身
Mysql是怎么加行级锁的?
什么sql语句会加行级锁:
普通的select语句是不会对记录加锁的(除了串行化隔离级别),因为它属于快照读,是通过MVCC(多版本并发控制)实现的。
如果要在查询时对记录加行级锁,可以使用下面这两个方式,这两种查询会加锁的语句称为锁定读
//对读取的记录加共享锁(S型锁) select ... lock in share mode; //对读取的记录加独占锁(X型锁) select ... for update;
事务提交,锁就释放了
还有update和delete操作都会加行级锁,且锁的类型都是独占锁(X型锁)
//对操作的记录加独占锁(X型锁) update table .... where id = 1; //对操作的记录加独占锁(X型锁) delete from table where id = 1;
行级锁有哪些种类
行级锁的种类在不同的隔离级别下,是不同的
在读已提交隔离界别下,行级锁的种类只有记录锁
在可重复读隔离级别下,行级锁的种类除了有记录锁,还有间隙锁(目的是为了避免幻读),所以行级锁
的种类主要有三类:
- Record Lock,记录锁,也就是仅仅把一条记录锁上;
- Gap Lock,间隙锁,锁定一个范围,但是不包含记录本身;
- Next-Key Lock:Record Lock + Gap Lock 的组合,锁定一个范围,并且锁定记录本身。
MySQL是如何加行级锁的
行级锁的加锁规则比较复杂,不同的场景,加锁的形式是不同的。
加锁对象是索引,加锁的基本单位是next-key lock
在能使用记录锁或者间隙锁就能避免幻读现象的场景下,next-key lock就会退化成记录锁或间隙锁
唯一索引等值查询
使用唯一索引进行等值查询的时候,查询记录不存在,加锁的规则也会不同,下面的例子是使用的主键索引
当查询的记录是「存在」的,在索引树上定位到这一条记录后,将该记录的索引中的 next-key lock 会退化成「记录锁」。
当查询的记录是「不存在」的,在索引树找到第一条大于该查询记录的记录后,将该记录的索引中的 next-key lock 会退化成「间隙锁」
唯一索引范围查询
当唯一索引进行范围查询时,会对每一个扫描到的索引加next-key锁,然后如果遇到下面这些情况,会退化成记录锁或间隙锁