mysql有哪些锁?
1、在 MySQL 里,根据加锁的范围,可以分为全局锁、表级锁和行锁三类

2、全局锁

2.1、全局锁命令
要使用全局锁,则要执行这条命令:
flush tables with read lock
如果要释放全局锁,则要执行这条命令:
unlock tables
当会话断开了,全局锁会被自动释放。
2.2、全局锁应用场景是什么?
全库逻辑备份

举个例子(如上图)
在全库逻辑备份期间,假设不加全局锁的场景,看看会出现什么意外的情况??
如果在全库逻辑备份期间,有用户购买了一件商品,一般购买商品的业务逻辑是会涉及到多张数据库表的更新,
比如在用户表更新该用户的余额,然后在商品表更新被购买的商品的库存。
那么,有可能出现这样的顺序:
1. 先备份了用户表的数据;
2. 然后有用户发起了购买商品的操作;
3. 接着再备份商品表的数据。
也就是在备份用户表和商品表之间,有用户购买了商品。
这种情况下,备份的结果是用户表中该用户的余额并没有扣除,反而商品表中该商品的库存被减少 了,
如果后面用这个备份文件恢复数据库数据的话,用户钱没少,而库存少了,等于用户白嫖了一 件商品。
所以,在全库逻辑备份期间,加上全局锁,就不会出现上面这种情况了
2.3、加全局锁又会带来什么缺点呢?

加上全局锁,意味着整个数据库都是只读状态。
那么如果数据库里有很多数据,备份就会花费很多的时间,关键是备份期间,业务只能读数据,而不能更新数据,这样会造成业务停滞。
既然备份数据库数据的时候,使用全局锁会影响业务,那有什么其他方式可以避免?
解决办法:
数据库的引擎支持的事务支持可重复读的隔离级别,那么在备份数据库之前先开启事务,会先创建 Read View,
然后整个事务执行期间都在用这个 Read View,而且由于 MVCC 的支持,备份期间业务依然可以对数据进行更新操作。
因为在可重复读的隔离级别下,即使其他事务更新了表的数据,也不会影响备份数据库时的 Read View,
这就是事务四大特性中的隔离性,这样备份期间备份的数据一直是在开启事务时的数据。
备份数据库的工具是 mysqldump,在使用 mysqldump 时加上 –single-transaction 参数的时候, 就会在备份数据库之前先开启事务。
这种方法只适用于支持「可重复读隔离级别的事务」的存储引 擎。
InnoDB 存储引擎默认的事务隔离级别正是可重复读,因此可以采用这种方式来备份数据库。
但是,对于 MyISAM 这种不支持事务的引擎,在备份数据库时就要使用全局锁的方法。
3、表级锁
3.1、表锁
3.1.1、表锁命令

举个例子:
如果我们想对学生表(t_student)加表锁,可以使用下面的命令:
//表共享读锁; //允许当前会话读取被锁定的表,但阻止其他会话对这些表进行写操作。 lock tables t_student read; //表独占写锁; //允许当前会话对表进行读写操作,但阻止其他会话对这些表进行任何操作(读或写)。 lock tables t_stuent write;
//释放锁
unlock tables
读锁不会阻塞其他客户端读,但是会阻塞写。写锁会阻塞其他客户端读和写。
3.2、元数据锁(MDL)

- 对一张表进行 CRUD 操作时,加的是 MDL 读锁;
- 对一张表做结构变更操作的时候,加的是 MDL 写锁;
MDL 是为了保证当用户对表执行 CRUD 操作时,防止其他线程对这个表结构做了变更。
当有线程在执行 select 语句( 加 MDL 读锁)的期间,如果有其他线程要更改该表的结构( 申请 MDL 写锁),那么将会被阻塞,直到执行完 select 语句( 释放 MDL 读锁)。
反之,当有线程对表结构进行变更( 加 MDL 写锁)的期间,如果有其他线程执行了 CRUD 操作( 申请 MDL 读锁),那么就会被阻塞,直到表结构变更完成( 释放 MDL 写锁)。
不需要显示调用,那它是在什么时候释放的?
MDL 是在事务提交后才会释放,这意味着事务执行期间,MDL 是一直持有的。 那如果数据库有一个长事务(所谓的长事务,就是开启了事务,但是一直还没提交),
那在对表结构做变更操作的时候,可能会发生意想不到的事情,
比如下面这个顺序的场景:
1. 首先,线程 A 先启用了事务(但是一直不提交),然后执行一条 select 语句,此时就先对该表加 上 MDL 读锁;
2. 然后,线程 B 也执行了同样的 select 语句,此时并不会阻塞,因为「读读」并不冲突;
3. 接着,线程 C 修改了表字段,此时由于线程 A 的事务并没有提交,也就是 MDL 读锁还在占用着,
这时线程 C 就无法申请到 MDL 写锁,就会被阻塞, 那么在线程 C 阻塞后,后续有对该表的 select 语句,就都会被阻塞,
如果此时有大量该表的 select 语句的请求到来,就会有大量的线程被阻塞住,这时数据库的线程很快就会爆满了。
为什么线程 C 因为申请不到 MDL 写锁,而导致后续的申请读锁的查询操作也会被阻塞?
这是因为申请 MDL 锁的操作会形成一个队列,队列中写锁获取优先级高于读锁,一旦出现 MDL 写 锁等待,会阻塞后续该表的所有 CRUD 操作。
所以为了能安全的对表结构进行变更,在对表结构变更前,先要看看数据库中的长事务,是否有事务已经对表加上了 MDL 读锁,如果可以考虑 kill 掉这个长事务,然后再做表结构的变更。

3.3、意向锁

在使用 InnoDB 引擎的表里对某些记录加上「共享锁」之前,需要先在表级别加上一个「意向共享锁」;
在使用 InnoDB 引擎的表里对某些纪录加上「排他锁」之前,需要先在表级别加上一个「意向排他锁」;

也就是,当执行插入、更新、删除操作,需要先对表加上「意向排他锁」,然后对该记录加排他锁。
而普通的 select 是不会加行级锁的,普通的 select 语句是利用 MVCC 实现一致性读,是无锁的。
不过,select 也是可以对记录加共享锁和独占锁的,具体方式如下:
意向共享锁和意向排他锁是表级锁,不会和行级的共享锁和排他锁发生冲突,而且意向锁之间也不会发生冲突,只会和共享锁(lock tables ... read)和排他锁(lock tables ... write)发生冲突。
表锁和行锁是满足读读共享、读写互斥、写写互斥的。
如果没有「意向锁」,那么加「排他锁」时,就需要遍历表里所有记录,查看是否有记录存在排他锁,这样效率会很慢。
那么有了「意向锁」,由于在对记录加排他锁前,先会加上表级别的意向排他锁,那么在加「排他锁」时,直接查该表是否有意向排他锁,
如果有就意味着表里已经有记录被加了排他锁,这样就 不用去遍历表里的记录。
所以,意向锁的目的是为了快速判断表里是否有记录被加锁。

4、行级锁
InnoDB 引擎是支持行级锁的,而 MyISAM 引擎并不支持行级锁。

前面也提到,普通的 select 语句是不会对记录加锁的,因为它属于快照读。如果要在查询时对记录加行锁,可以使用下面这两个方式,这种查询会加锁的语句称为锁定读。
上面这两条语句必须在一个事务中,因为当事务提交了,锁就会被释放,所以在使用这两条语句的时候,要加上 begin、start transaction 或者 set autocommit = 0。
共享锁(S锁)满足读读共享,读写互斥。排他锁(X锁)满足写写互斥、读写互斥。
行级锁的类型主要有三类:
- Record Lock,行锁,也就是仅仅把一条记录锁上;
- Gap Lock,间隙锁,锁定一个范围,但是不包含记录本身;
- Next-Key Lock:Record Lock + Gap Lock 的组合,锁定一个范围,并且锁定记录本身。
4.1、行锁

- 当一个事务对一条记录加了 S 型行锁后,其他事务也可以继续对该记录加 S 型行锁(S 型与 S 锁兼容),但是不可以对该记录加 X 型行锁(S 型与 X 锁不兼容);
- 当一个事务对一条记录加了 X 型行锁后,其他事务既不可以对该记录加 S 型行锁(S 型与 X 锁不兼容),也不可以对该记录加 X 型行锁(X 型与 X 锁不兼容)。

4.1.1、行锁出现的类型情况


4.2、间隙锁

间隙锁只存在于可重复读隔离级别,目的是为了解决可重复读隔离级别下幻读的现象。

4.3、临建锁

浙公网安备 33010602011771号