4. 锁

  • mysql

一、全局锁

flush tables with read lock

执行后,整个数据库就处于只读状态了,这时其他线程执行以下操作,都会被阻塞:

  • 对数据的增删改操作,比如 insert、delete、update等语句;
  • 对表结构的更改操作,比如 alter table、drop table 等语句

如果要释放全局锁,则要执行这条命令:

unlock tables

二、表级锁

  • 表锁;
  • 元数据锁(MDL);
  • 意向锁;
  • AUTO-INC 锁

2.1 表锁

--表级别的共享锁,也就是读锁;
lock tables t_student read;

--表级别的独占锁,也就是写锁;
lock tables t_stuent write;

--释放表锁
unlock tables

2.2 元数据锁(MDL)

我们不需要显示的使用 MDL,因为当我们对数据库表进行操作时,会自动给这个表加上 MDL:

  • 对一张表进行 CRUD 操作时,加的是 MDL 读锁;
  • 对一张表做结构变更操作的时候,加的是 MDL 写锁

2.3 意向锁

InnoDB支持多粒度的锁共存,它允许表级锁、行级锁共存。

意向锁的存在就是为了协调行锁和表锁的关系,支持行锁表锁共存,它是一种不与行锁冲突的表级锁。

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

通俗的讲,就是在给行级别的数据加锁时,会自动的给表加一个意向锁,目的是为了告诉其它事务,这张表里的一些数据被上了锁,省去了遍历数据去得知数据有没有加锁的操作,达到快速判断表里是否有记录被加锁。

2.4 AUTO-INC 锁

表里的主键通常都会设置成自增的,这是通过对主键字段声明 AUTO_INCREMENT 属性实现的。

AUTO-INC 锁是特殊的表锁机制,锁不是再一个事务提交后才释放,而是再执行完插入语句后就会立即释放。 但是,在进行大量数据插入的时候,会影响插入性能,因为另一个事务中的插入会被阻塞。 因此, 在 MySQL 5.1.22 版本开始,InnoDB 存储引擎提供了一种轻量级的锁来实现自增。

InnoDB 存储引擎提供了个 innodb_autoinc_lock_mode 的系统变量,是用来控制选择用 AUTO-INC 锁,还是轻量级的锁。

  • 当 innodb_autoinc_lock_mode = 0,就采用 AUTO-INC 锁,语句执行结束后才释放锁;
  • 当 innodb_autoinc_lock_mode = 2,就采用轻量级锁,申请自增主键后就释放锁,并不需要等语句执行后才释放。
  • 当 innodb_autoinc_lock_mode = 1:
    • 普通 insert 语句,自增锁在申请之后就马上释放;
    • 类似 insert … select 这样的批量插入数据的语句,自增锁还是要等语句结束后才被释放;

三、行级锁

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

普通的 select 语句是不会对记录加锁的,因为它属于快照读。如果要在查询时对记录加行锁,可以使用下面这两个方式,这种查询会加锁的语句称为锁定读(当前读)

-- 对读取的记录加共享锁
select ... lock in share mode;

-- 对读取的记录加独占锁
select ... for update;

上面加锁的语句必须在事务中

细分的话,行级锁的类型主要有三类:

  • Record Lock,记录锁,也就是仅仅把一条记录锁上;
  • Gap Lock,间隙锁,锁定一个范围,但是不包含记录本身;
  • Next-Key Lock:Record Lock + Gap Lock 的组合,锁定一个范围,并且锁定记录本身。

3.1 Record Lock

给一条存在的记录加锁

-- table t 存在主键id 1,3,5
select id from t where id = 3 for update;

3.2 Gap Lock

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

假设,表中有一个范围 id 为(3,5)间隙锁,那么其他事务就无法插入 id = 4 这条记录了,这样就有效的防止幻读现象的发生。

-- table t 存在age(非唯一索引) 1,3,5
-- 事务a,锁住3(记录锁),同时会锁住(3,5]这个范围
select id from t where id = 3 for update;
-- 事务b,会阻塞,因为(3,5]被锁住了
INSERT into my_db.test values(4,4)

3.3 Next-Key Lock

Next-Key Lock 称为临键锁,是 Record Lock + Gap Lock 的组合,锁定一个范围,并且锁定记录本身。

在查询时MySQL会根据索引的类型,SQL查询条件组合使用 Record Lock 、 Gap Lock

具体情况比较复杂:可参考:https://learn.lianglianglee.com/专栏/MySQL实战45讲/21 为什么我只改一行的语句,锁这么多?.mdopen in new window

Loading...