跳至主要內容

表级锁

Jin大约 6 分钟

表级锁

1、介绍

表级锁,每次操作锁住整张表。锁定粒度大,发生锁冲突的概率最高,并发度最低。应用在MyISAM、InnoDB、BDB等存储引擎中。

对于表级锁,主要分为以下三类:

  • 表锁

  • 元数据锁(meta data lock,MDL)

  • 意向锁

2、表锁

2.1、表锁分类

对于表锁,分为两类:

  • 表共享读锁(read lock)
  • 表独占写锁(write lock)

2.2、语法

  • 加锁:lock tables 表名... read/write。
  • 释放锁:unlock tables / 客户端断开连接 。

2.3、读锁

image-20220531002508823
image-20220531002508823

左侧为客户端一,对指定表加了读锁,客户端一可读,但不能写 ,右侧客户端二的可读,但是会阻塞右侧客户端的写。测试:

# 加锁
lock tables tb_user tu read;

select *
from tb_user tu;
#未释放锁报错 [HY000][1100] Table 'tb_user' was not locked with LOCK TABLES
INSERT INTO mysql_demo.tb_user (id, name, phone, email, profession, age, gender, status, createtime)
VALUES (18, 'Jin', '17799990023', '37483844@qq.com', '工程造价', 29, '1', '4', '2003-05-26 00:00:00');

unlock tables;
# 客户端二,# 注意事项,不能和表锁客户端1在同一个会话中(DataGrip不能在同一个console中)
select *
from tb_user tu;

INSERT INTO mysql_demo.tb_user (id, name, phone, email, profession, age, gender, status, createtime)
VALUES (18, 'Jin', '17799990023', '37483844@qq.com', '工程造价', 29, '1', '4', '2003-05-26 00:00:00');

[HY000][1100] Table 'tb_user' was not locked with LOCK TABLES
image-20220601010751406
image-20220601010751406

2.4、写锁

# 加锁
lock tables tb_user tu write;

select *
from tb_user tu;
# 可以写入
INSERT INTO mysql_demo.tb_user (id, name, phone, email, profession, age, gender, status, createtime)
VALUES (66, 'Jin', '17799990023', '37483844@qq.com', '工程造价', 29, '1', '4', '2003-05-26 00:00:00');
unlock tables;
# 客户端二,# 注意事项,不能和表锁客户端1在同一个会话中(DataGrip不能在同一个console中)
select *
from tb_user tu;
# 阻塞
INSERT INTO mysql_demo.tb_user (id, name, phone, email, profession, age, gender, status, createtime)
VALUES (66, 'Jin', '17799990023', '37483844@qq.com', '工程造价', 29, '1', '4', '2003-05-26 00:00:00');
unlock tables;
image-20220601011455592
image-20220601011455592

左侧为客户端一,对指定表加了写锁,客户端一可读可写,客户端二读和写被阻塞,需等客户端一释放写锁,才能读和写**。

2.5、元数据锁

meta data lock , 元数据锁,简写MDL。

MDL加锁过程是系统自动控制,无需显式使用,在访问一张表的时候会自动加上。MDL锁主要作用是维护表元数据的数据一致性,在表上有活动事务的时候,不可以对元数据进行写入操作。为了避免DML与 DDL冲突,保证读写的正确性。

这里的元数据,大家可以简单理解为就是一张表的表结构。 也就是说,某一张表涉及到未提交的事务时,是不能够修改这张表的表结构的。

在MySQL5.5中引入了MDL,当对一张表进行增删改查的时候,加MDL读锁(共享);当对表结构进行变更操作的时候,加MDL写锁(排他)。

常见的SQL操作时,所添加的元数据锁:

对应SQL锁类型说明
lock tables xxx read / writeSHARED_READ_ONLY / SHARED_NO_READ_WRITE
select 、select ... lock in share modeSHARED_READ与SHARED_READ、 SHARED_WRITE兼容,与 EXCLUSIVE互斥
insert 、update、 delete、select ... for updateSHARED_WRITE与SHARED_READ、 SHARED_WRITE兼容,与 EXCLUSIVE互斥
alter table ...EXCLUSIVE与其他的MDL都互斥

演示:当执行SELECT、INSERT、UPDATE、DELETE等语句时,添加的是元数据共享锁(SHARED_READ / SHARED_WRITE),之间是兼容的。

image-20220601013007201
image-20220601013007201

当执行SELECT语句时,添加的是元数据共享锁(SHARED_READ),会阻塞元数据排他锁(EXCLUSIVE),之间是互斥的。

image-20220601013041953
image-20220601013041953

我们可以通过下面的SQL,来查看数据库中的元数据锁的情况:

select object_type,object_schema,object_name,lock_type,lock_duration from performance_schema.metadata_locks ;

我们在操作过程中,可以通过上述的SQL语句,来查看元数据锁的加锁情况。

image-20220601013118969
image-20220601013118969

2.6、意向锁

  1. 介绍

为了避免DML在执行时,加的行锁与表锁的冲突,在InnoDB中引入了意向锁,使得表锁不用检查每行数据是否加锁,使用意向锁来减少表锁的检查。

假如没有意向锁,客户端一对表加了行锁后,客户端二如何给表加表锁呢,来通过示意图简单分析一下:

首先客户端一,开启一个事务,然后执行DML操作,在执行DML语句时,会对涉及到的行加行锁。

image-20220610000037990
image-20220610000037990

当客户端二,想对这张表加表锁时,会检查当前表是否有对应的行锁,如果没有,则添加表锁,此时就会从第一行数据,检查到最后一行数据,效率较低

image-20220610000049305
image-20220610000049305

有了意向锁之后 :

客户端一,在执行DML操作时,会对涉及的行加行锁,同时也会对该表加上意向锁。

image-20220610000113004
image-20220610000113004

而其他客户端,在对这张表加表锁的时候,会根据该表上所加的意向锁来判定是否可以成功加表锁,而不用逐行判断行锁情况了。

image-20220610000123757
image-20220610000123757
  1. 分类
意向共享锁(IS)由语句select ... lock in share mode添加 。 与 表锁共享锁(read)兼容,与表锁排他锁(write)互斥
意向排他锁(IX)由insert、update、delete、select...for update添加 。与表锁共享锁(read)及排他锁(write)都互斥,意向锁之间不会互斥。

一旦事务提交了,意向共享锁、意向排他锁,都会自动释放。

可以通过以下SQL,查看意向锁及行锁的加锁情况:

select object_schema, object_name, index_name, lock_type, lock_mode, lock_data
from performance_schema.data_locks;

演示:

A. 意向共享锁与表读锁是兼容的,与表写锁互斥

# 客户端1

# 1 开启事务
begin;

# 2
select *
from tb_user tu
where id = 1 lock in share mode;

# 7
commit;

# ======================================客户端2======================================
# 注意事项,不能和表锁客户端1在同一个会话中(DataGrip不能在同一个console中)
# 3
select object_schema, object_name, index_name, lock_type, lock_mode, lock_data
from performance_schema.data_locks;

# 4  表共享读锁成功
lock tables tb_user tu read;

# 5
unlock tables;

# 6
lock tables tb_user tu write;
image-20220609235712017
image-20220609235712017
image-20220609235902281
image-20220609235902281

B. 意向排他锁与表读锁、写锁都是互斥的

# ====================意向排他锁(IX) 演示====================
# 1
begin;

# 2
UPDATE mysql_demo.tb_user t
SET t.status = '5'
WHERE t.id = 1;
commit ;

# ====================意向排他锁(IX) 演示====================
# 3
select object_schema, object_name, index_name, lock_type, lock_mode, lock_data
from performance_schema.data_locks;
#     阻塞状态,因为意向排他锁和读锁互斥
lock tables tb_user tu read;
unlock tables ;

#     阻塞状态,因为意向排他锁和写锁互斥
lock tables tb_user tu read;
unlock tables ;
image-20220610001307684
image-20220610001307684
image-20220610001440749
image-20220610001440749

贡献者: Jin