InnoDB 锁的类型

一、全局锁

mysql> flush table with read lock;

FTWRL 会对整个实例加只读锁。会阻塞所有线程读以外的所有操作。查看线程状态 State: Waiting for global read lock

通常 对全库做逻辑备份(mysqldump)时,会加全局锁。

(1)如果在主库上执行逻辑备份,备份期间会阻塞实例所有写操作。

(2)如果在备库上执行逻辑备份,备份期间会阻塞所有 binlog 的回放,导致复制延迟。

对于事务表的逻辑备份,因为支持事务一致性视图,可以避免 全局读锁。对于非事务的 MyISAM 表备份时,则无法避免 global read lock。

二、表级锁

表级锁有两种:表锁 和 MDL 锁

1. 表锁

mysql> lock table t1 read; 所有线程只能读 t1,写阻塞,DDL 阻塞。包括当前线程。查看线程状态 State: Waiting for table metadata lock

当前线程写  t1 会报错: 
mysql> delete from t1 where id=6;
ERROR 1099 (HY000): Table 't1' was locked with a READ lock and can't be updated
其他线程写 t1 会阻塞:

mysql> lock table t1 write; 其他线程读写 t1 阻塞,DDL t1 阻塞。查看线程状态 State: Waiting for table metadata lock

当前线程读写 t1 不阻塞,DDL t1 不阻塞: 
mysql> delete from t1 where id=6;  
mysql> alter table t1 add column "hobby" varchar(5);

但当前线程也不允许读写 其他表 eg: t ,会报错:
mysql> select * from t limit 1; delete from t where id=1;
ERROR 1100 (HY000): Table 't' was not locked with LOCK TABLES

可见,表锁的影响还是挺大的,InnoDB 通常不会触发表锁,因为太影响并发性能了。

2. MDL 锁

MDL 锁不需要显示添加,对进行 DML 操作的时候会自动在该表加 MDL 读锁,DDL 加 MDL 写锁,读写互锁斥。另外需要说明的是,MDL 锁是事务中语句开始执行的时候添加的,直到该事务提交才会释放。一般情况下,我们不需要太关注 MDL 锁,但一些特殊情况需要有了解。
(1)在有活跃事务或者高并发的的表上执行 DDL 操作:

session A session B session C session D
T1 mysql> begin;
select * from t1 where id=1;
T2 mysql> select * from t1 where id=1;
T3 mysql> alter table t1 add index(age);
blocked
T4 mysql> select * from t1;
blocked

session A 的 DML 操作,首先会在表 t1 上加 MDL 读锁,但还未提交事务。因为 MDL 读锁之间兼容,所以 session B 的操作不会被阻塞。session C DDL添加所引,要先获取 MDL 写锁,因为读写锁互斥,所以 session C 会被 block 住,等待 表图 t1 上的MDL 读锁释放。 但此时,session C 也会阻塞他之后 表 t1 上的所有 DML,DDL 语句,看起来像是锁表了一样,直到 session A 提交释放读锁,C, D 才会执行成功。

在 有长事务,垃圾 SQL,或高并发的表上执行 DDL 操作,需要引起注意,可能会导致 MDL 锁等待的问题。

在 session A 中不显示开启事务,直接模拟垃圾 sql: mysql> select *,sleep(120) from t1 where id=1; 同样能验证 MDL 锁等待的问题。

posted @ 2021-06-16 11:25  梦里花。  阅读(118)  评论(0)    收藏  举报