MySQL:意向锁、间隙锁、临键锁

1,310 阅读21分钟

什么是锁?

共享资源在高并发环境下的一种保护

InnoDB存储引擎的锁

共享锁

排他锁

image.png

说白了就是读写锁

对细颗粒度上下锁, 那么在粗颗粒粒度就需要上意向锁

意向锁就是表锁, 分为 IS 和 IX 意向共享锁和意向排他锁

比如对 r 共享资源上锁, 那么就需要对 表, 页上意向锁

也就是说, 在上锁前, 需要对粗颗粒粒度上IX意向锁, 然后在对 r 上 X 排它锁

如果在X锁之前, 粗颗粒上已经上锁其他锁, 比如 IS 意向共享锁, 意向锁之间是相互兼容的, 所以 IS 和 IX 兼容

但是 X 锁 和 IS 锁是不兼容的, 所以需要等待

image.png

show engine INNODB STATUS;查看当前锁请求信息

show FULL PROCESSLIST;: 查看mysql 当前执行的进程, 可以看到是否发生死锁

一致性非锁定读

一致性非锁定读的意思是在读取数据时,不需要加锁,同时还能保证读取的数据是一致的。这听起来有点矛盾,不加锁怎么保证一致性呢?这里的关键是多版本控制(MVCC)

多版本控制(MVCC)

想象一下,你有一个文件,每次修改文件时,你不是直接覆盖原来的内容,而是创建一个新的版本。这样,即使有人正在读取旧版本的文件,你依然可以修改文件并创建新版本。读取旧版本的人不会受到影响,因为他们看到的是旧版本的内容。

在数据库中,这种机制通过undo log来实现。undo log 就像是一个历史记录,保存了数据的不同版本。每次修改数据时,都会在 undo log 中记录一个新的版本。读取数据时,数据库会根据需要选择合适的版本来读取。

读已提交 vs 可重复读

  • 读已提交:每次读取数据时,都会读取最新的版本。就像你每次从网上下载软件时,都会下载最新版本。
  • 可重复读:在同一个事务中,每次读取数据时,都会读取同一个版本。就像你第一次下载软件后,把它保存到网盘,以后每次都从网盘下载同一个版本,即使网上有新版本,你也不会受到影响。

带入到代码中是这样的:

会话1会话2
begin
SELECT * FROM student WHERE age = 22;
image.png
begin
UPDATE student SET age=23 WHERE id = 1; // 成功
SELECT * FROM student WHERE age = 22;
image.png
SELECT * FROM student WHERE age = 22;
image.png
commit;
SELECT * FROM student WHERE age = 22;
image.png
commit;

会话1 不管 会话2 怎么变化, 是否修改并提交了事务, 但是 会话1 查询还是存在该row

可以发现在 会话2 事务提交之后, 已经将数据同步到磁盘中了, 但是 会话1 还是旧的值, 说明 会话1 也是在快照上读取的, 因为我们的事务等级是 可重复读

如果在读已提交下, 会话2 commit 事务之后, 会话1 再去查询, 就会发现age被修改了

一致性锁定读

一致性锁定读的意思是在读取数据时,直接加锁,确保在读取期间数据不会被修改。

  • 排他锁(X锁) :当你使用 SELECT ... FOR UPDATE 时,数据库会给读取的数据加上排他锁,其他事务不能修改这些数据,直到你的事务结束。
  • 共享锁(S锁) :当你使用 SELECT ... LOCK IN SHARE MODE 时,数据库会给读取的数据加上共享锁,其他事务可以读取这些数据,但不能修改,直到你的事务结束。

举个例子

假设有两个会话(Session 1 和 Session 2)同时操作数据库。

一致性非锁定读(可重复读)

  • Session 1 开始一个事务,读取 age = 22 的数据。
  • Session 2 开始一个事务,修改 age = 22 的数据为 age = 23,并提交事务。
  • Session 1 再次读取 age = 22 的数据,发现数据没有变化,因为它在Session 1这个事务中读取的数据。

一致性锁定读

  • Session 1 开始一个事务,使用 SELECT ... FOR UPDATE 读取 age = 22 的数据,并加上排他锁。
  • Session 2 尝试修改 age = 22 的数据,但会被阻塞,直到 Session 1 的事务结束。

锁算法

行锁的三种算法

  • record lock(记录锁): 单个行记录上的锁
  • gap lock(间隙锁): 锁定一个范围, 但是不包含记录本身
  • Next-Key Lock(临键锁): Gap Lock+Record Lock, 锁定一个范围包括记录本身

Record Lock(记录锁)

  • 是什么? 记录锁就是针对某一行数据的锁。比如你有一条数据,主键是1,当你对这条数据进行操作时,MySQL会给这条数据加锁,防止其他事务同时修改它。
  • 什么时候用? 当你对某一行数据进行更新或删除时,MySQL会自动给这行数据加锁,确保在你操作完成之前,其他事务不能修改这行数据。
  • READ COMMITTED隔离级别下的特殊行为: 在这个隔离级别下,MySQL会在判断完WHERE条件后,如果发现某行数据不满足条件,就会立即释放这行数据的锁。这样可以提高并发性能,让其他事务更快地访问这些数据。

Gap Lock(间隙锁)

  • 是什么? 间隙锁是锁住一个“间隙”,也就是锁住两个数据行之间的空白区域。比如你有数据行1和3,间隙锁会锁住1和3之间的空白区域,防止其他事务在这个空白区域插入新的数据。
  • 为什么需要间隙锁? 主要是为了防止“幻读”。幻读是指在一个事务中,你第一次查询时没有看到某条数据,但第二次查询时却看到了这条数据,这是因为其他事务在中间插入了新数据。间隙锁可以防止这种情况发生。
  • 间隙锁的特点: 多个事务可以在同一个间隙上同时持有间隙锁,它们之间不会冲突。比如事务A和事务B都可以锁住1和3之间的间隙,它们不会互相阻塞。

Next-Key Lock(临键锁)

  • 是什么? 临键锁是记录锁和间隙锁的结合体。它不仅仅锁住某一行数据,还会锁住这行数据之前的“间隙”(也就是这行数据和前一行数据之间的空白区域)。
  • 作用是什么? 临键锁的主要作用是防止“幻读”。幻读是指在一个事务中,你第一次查询时没有看到某条数据,但第二次查询时却看到了这条数据,这是因为其他事务在中间插入了新数据。临键锁可以防止这种情况发生。
  • 区间范围: 临键锁的区间是左开右闭的,比如 (a, b],表示锁住从 a 到 b 之间的所有数据,包括 b,但不包括 a。
  • 特殊情况: 如果你锁定的行是唯一索引(比如主键),那么临键锁会退化为记录锁,只锁住这一行,不再锁住间隙。

插入意向锁

  • 是什么? 插入意向锁是一种特殊的锁,用于管理多个事务在同一个“间隙”中插入数据的情况。
  • 作用是什么? 当多个事务想要在同一个间隙中插入数据时,插入意向锁可以让它们知道有其他事务也在尝试插入,从而避免冲突。不过,插入意向锁并不会阻止其他事务插入数据,它只是一个“提示”。
  • 举个例子: 假设事务A和事务B都想在数据行1和3之间的间隙中插入数据。事务A会先设置一个插入意向锁,表示它要在这个间隙中插入数据。事务B看到这个锁后,知道事务A也在插入,但它仍然可以继续自己的插入操作。只有当两个事务试图在完全相同的位置插入数据时,才会发生冲突,需要等待对方完成。

意向锁

  • 是什么? 意向锁是一种表级锁,用来表示某个事务即将对表中的某些行加锁。
  • 作用是什么? 在没有意向锁之前,如果我们想修改表结构(比如添加或删除列),需要一行一行地扫描表,看看是否有其他锁存在,这个过程非常慢。有了意向锁之后,我们可以直接判断表上是否有意向锁,而不需要扫描每一行,大大提高了效率。
  • 特点: 意向锁通常是表级锁,而不是行级锁,而且多个意向锁之间是兼容的,不会互相阻塞。

总结

  • 记录锁 是锁住某一行数据,防止其他事务修改它。
  • 间隙锁 是锁住数据行之间的空白区域,防止其他事务在这个区域插入新数据。
  • READ COMMITTED隔离级别 下,MySQL会在判断完WHERE条件后,释放不满足条件的行的记录锁,提高并发性能。
  • 临键锁 是记录锁和间隙锁的结合,锁住某一行及其之前的间隙,防止幻读。
  • 插入意向锁 用于管理多个事务在同一个间隙中插入数据的情况,避免冲突。
  • 意向锁 是表级锁,用于提高表结构修改时的效率。

分析加锁范围和加锁类型

我们需要使用performance_schema.data_locks这张表来分析

使用书本上的案例来分析:

CREATE TABLE `z` (
  `a` int NOT NULL,
  `b` int DEFAULT NULL,
  PRIMARY KEY (`a`),
  KEY `b` (`b`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

首先我们执行

START TRANSACTION;
SELECT * from z where b = 3 for update;
SELECT * from `performance_schema`.data_locks;

image.png

image.png

image.png

lock_type 表示我们上的锁是表锁还是行锁, 明显第一个是表锁, 后续都是行锁

image.png

这个是我们查询表的表名

image.png

索引的名称或者字段名字

这里显示的是分别是我们z表的b字段和主键

image.png

第一个是意向排他锁, 也是个表锁

第二个是next-key lock

第三个是 record lock

第四个是 gap lock

image.png

第一个是表锁, 所以没有锁数据

第二个是 3, 5, 表示 b = 3 , id = 5 这行

第三个 5 表示 主键 id = 5 这一行

第四个6, 7 , b = 6, id = 7 这一行

可以直接看这里

现在综合一下

image.png

表锁, 意向排他锁

我们执行的是SELECT * from z where b = 3 for update;, for update本身就是为了加上排他锁的

而在上排他锁之前, 需要上意向排他锁

前面的章节我们知道这里一个顺序问题, 先粗颗粒上意向锁, 再细颗粒上下排他或共享锁

image.png

image.png

根据第二行的数据, 我们可以分析出:

b = 3 id = 5 这一行, 我们上 next-key lock, 是左开右闭区间, 所以锁的范围应该是b(1.3, 3]

image.png

lock_mode 显示出他是 next-key lock 但是不需要 gap lock 的, 所以是 record lock

锁的范围是 id = 5 这一条

image.png

lock_mode 可以看出他是一个关于b字段gap lock, 而这个 6表示b=6这一行, 所以锁的范围是 (3, 6.7] 这个范围

所以综合一下, 锁的整理范围在 (1.3, 6.7]

Q: 这上面的 1.36.7 是什么意思?

A: 是一个带小数点的数字, 他的意思是 1 和 6 都表示 b 字段的值, 然后 小数点的 3 和 7 表示主键

image.png

我们还可以带个例子

现在我们插入 b = 6 a = 6 这个row, 按照我的理解, 这条sql会阻塞

然后我们在插入 b = 6, a = 8 这条记录, 将不会阻塞

INSERT into z SELECT 6, 6; -- 阻塞
INSERT into z SELECT 8, 6; -- 插入成功

总结

我们可以借助performance_schema.data_locks这张表,在sql语句执行之后查询该表,便可以看到sql语句的上锁情况,比如上锁的类型,范围等

但需要在该sql语句执行期间事务未提交前查询performance_schema.data_locks表。

当我们在发现存在死锁,拿到mysql日志的情况下不清楚这两段sql语句为何会产生死锁的情况下,可以如上面如此分别测试两条sql语句上锁范围是否重叠,如何重叠,这样我们才会知晓如何解决该死锁。

找一些实例实战一下

数据:

CREATE TABLE `t` (
  `id` int NOT NULL,
  `c` int DEFAULT NULL,
  `d` int DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `c` (`c`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

INSERT INTO `test`.`t` (`id`, `c`, `d`) VALUES (0, 1, 2);
INSERT INTO `test`.`t` (`id`, `c`, `d`) VALUES (1, 2, 3);
INSERT INTO `test`.`t` (`id`, `c`, `d`) VALUES (10, 11, 12);
INSERT INTO `test`.`t` (`id`, `c`, `d`) VALUES (13, 14, 15);
INSERT INTO `test`.`t` (`id`, `c`, `d`) VALUES (16, 17, 18);
INSERT INTO `test`.`t` (`id`, `c`, `d`) VALUES (19, 20, 21);

实例1

假设我们在sessionA中需要执行两个sql语句

我们先确认锁的范围是什么。

sessionA:

# sql A
START TRANSACTION;
update t set d = d+0 where id = 10;
SELECT * from `performance_schema`.data_locks;
COMMIT;

image.png

record lock, 锁的范围是 id=10 这一行

上面执行了COMMIT语句,完成了一次事务的提交,所以在执行下面的sql B语句就不会产生阻塞的情况了。

# sql B
START TRANSACTION;
update t set d = d+0 where id = 7;
SELECT * from `performance_schema`.data_locks;

image.png

主键产生的间隙锁, 锁的范围是: (1, 10]

这边我并没有将失误提交掉,所以此次sql执行并没有结束,接着,我们在另一个绘画中执行新的mysql insert行为。

接着我们在另一个sessionB中执行sql

insert into t value(8, 8, 8);

直接阻塞, sessionA已经上锁了, 上了(1, 10], 所以阻塞,而我们插入的数据正在这个范围之中。

update t set d=d+1 where id = 10;

允许执行, 因为这里两个 session 产生了两个 快照

在可重复读的前提下, 产生了两个快照

案例2

sessionA:

START TRANSACTION;
SELECT id from t where t.c = 11 lock in share mode;
SELECT * from `performance_schema`.data_locks;

image.png

c=11 共享锁c=(11, 14] 共享间隙锁

sessionB

update t set d=d+0 where c=11;

阻塞

image.png

更新 c=11 这一行, 需要插入 排他锁 X, 但是这一行已经被 sessionA 上了共享锁, 不兼容, 所以阻塞

sessionC:

INSERT into t SELECT 6, 6, 6;

阻塞, 在 sessionA上锁的范围中

image.png

如果你需要 insert row, 那么就需要插入意向锁, 不兼容

IX + S 的内容包含 X ==> 不兼容

判断不兼容的方法非常简单

如果是意向锁之间, 兼容

如果是意向锁与普通锁, 或者普通锁和普通锁之间: 可以计算他们的结果是否带有 X

比如:

IS + X = ISX 结果包含 X, 不兼容

IX + S = IXS 结果包含 X 不兼容

S + X = SX 结果包含 X 不兼容

IX + IX = IXIX 是意向锁之间, 兼容

案例3

sessionA

START TRANSACTION;
select * from t where c>=11 and c<14 for update;
SELECT * from `performance_schema`.data_locks;

image.png

LOCK_MODE类型中有三个next-key lock, 范围是

c = (2, 11], (11, 14], (14, 17] ==> (2, 17]

sessionB

INSERT into t SELECT 6, 11, 6;

阻塞, 在加锁范围在中

sessionC

DELETE FROM t where t.c = 2; -- 允许删除
DELETE FROM t where t.c = 11; -- 不允许

image.png

X+X = XX 包含X不兼容阻塞

c=2 是可以删除的, 因为 2不在加锁范围中(2, 17]

案例4

START TRANSACTION;
select * from t where id>11 and id<=13 order by id desc for update;
SELECT * from `performance_schema`.data_locks;

image.png

(1, 10], (10, 13], (13, 16]==> (1, 16]

案例5

START TRANSACTION;
select * from t where id>=10 and id<13 order by id desc for update;
SELECT * from `performance_schema`.data_locks;

image.png

范围是在 (0, 1], (1, 10], (10, 13] ==> (0, 13]

这里我们需要关注倒叙问题

START TRANSACTION;
select * from t where id>=10 and id<13 order by id for update;
SELECT * from `performance_schema`.data_locks;

image.png

范围是 [10], (10, 13] ==> [10, 13]

就因为一个 desc就导致范围是不同的

查看锁和事务的状态:

show PROCESSLIST;
SELECT * from information_schema.INNODB_TRX;
SELECT * from performance_schema.data_locks;
SELECT * from performance_schema.data_lock_waits;
SHOW ENGINE INNODB STATUS;

核心看这一行SELECT * from performance_schema.data_locks;

幻读

幻读的本质是在一次事务中, 多次执行相同的sql所获得的结果是不同的

记住一个关键词: 获得的结果, 也是说这个sql会产生一些返回值, 比如 select

举个例子

CREATE TABLE `z` (
  `a` int NOT NULL,
  `b` int DEFAULT NULL,
  PRIMARY KEY (`a`),
  KEY `b` (`b`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
INSERT INTO `test`.`z` (`a`, `b`) VALUES (1, 1);
INSERT INTO `test`.`z` (`a`, `b`) VALUES (3, 1);
INSERT INTO `test`.`z` (`a`, `b`) VALUES (5, 3);
INSERT INTO `test`.`z` (`a`, `b`) VALUES (7, 6);
INSERT INTO `test`.`z` (`a`, `b`) VALUES (10, 7);
START TRANSACTION;
select * from z where a < 3;

正常情况查询结果是(1, 1)这一行数据

此时在事务A未被提交的情况下, 在另一个事务B执行:

insert into select 2, 1;

然后在回到事务A再次执行相同sql:

select * from z where a < 3;

结果是(1, 1), (2, 1)

在同一个事务中, 执行相同sql语句, 得到的结果是不同的, 幻读产生了

使用next-key lock如何避免幻读?

START TRANSACTION;
select * from z where a < 3;

在可重复读情况下, 他会上临键锁, 范围是 (1, 3]

image.png

所以在这之间是无法添加新的row的, 因为无法添加X,GAP,INSERT_INTENTION

附加: mysql锁相关表

还可以在这里几张表看到这些信息:

SELECT * FROM `information_schema`.`INNODB_TRX`
SELECT * FROM `performance_schema`.`data_locks`
SELECT * FROM `performance_schema`.`data_lock_waits`

INNODB_TRX

这个表是系统表,用于显示当前正在运行的InnoDB事务的信息

这个表包含了事务的ID,状态,开始时间,锁定的资源,等待的资源等信息,可以帮助用户了解当前数据库的事务状态,以及排查可能出现的事务冲突等问题。

CREATE TEMPORARY TABLE `information_schema.INNODB_TRX` (
  `trx_id` bigint unsigned NOT NULL DEFAULT '0',
  `trx_state` varchar(13) NOT NULL DEFAULT '',
  `trx_started` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
  `trx_requested_lock_id` varchar(105) DEFAULT NULL,
  `trx_wait_started` datetime DEFAULT NULL,
  `trx_weight` bigint unsigned NOT NULL DEFAULT '0',
  `trx_mysql_thread_id` bigint unsigned NOT NULL DEFAULT '0',
  `trx_query` varchar(1024) DEFAULT NULL,
  `trx_operation_state` varchar(64) DEFAULT NULL,
  `trx_tables_in_use` bigint unsigned NOT NULL DEFAULT '0',
  `trx_tables_locked` bigint unsigned NOT NULL DEFAULT '0',
  `trx_lock_structs` bigint unsigned NOT NULL DEFAULT '0',
  `trx_lock_memory_bytes` bigint unsigned NOT NULL DEFAULT '0',
  `trx_rows_locked` bigint unsigned NOT NULL DEFAULT '0',
  `trx_rows_modified` bigint unsigned NOT NULL DEFAULT '0',
  `trx_concurrency_tickets` bigint unsigned NOT NULL DEFAULT '0',
  `trx_isolation_level` varchar(16) NOT NULL DEFAULT '',
  `trx_unique_checks` int NOT NULL DEFAULT '0',
  `trx_foreign_key_checks` int NOT NULL DEFAULT '0',
  `trx_last_foreign_key_error` varchar(256) DEFAULT NULL,
  `trx_adaptive_hash_latched` int NOT NULL DEFAULT '0',
  `trx_adaptive_hash_timeout` bigint unsigned NOT NULL DEFAULT '0',
  `trx_is_read_only` int NOT NULL DEFAULT '0',
  `trx_autocommit_non_locking` int NOT NULL DEFAULT '0',
  `trx_schedule_weight` bigint unsigned DEFAULT NULL
) ENGINE=MEMORY DEFAULT CHARSET=utf8mb3;

这些列的作用如下:

  • trx_id:事务ID,唯一标识一个事务。
  • trx_state:事务状态,表示事务是正在运行、等待锁、回滚或提交。
  • trx_started:事务开始时间。
  • trx_requested_lock_id:事务当前正在等待的锁的ID,可以和INNODB_LOCKS表关联以获取更多信息。
  • trx_wait_started:事务开始等待锁的时间。
  • trx_weight:事务的权重,表示事务持有或请求的锁的数量和类型。
  • trx_mysql_thread_id:与事务关联的MySQL线程ID,可以和PROCESSLIST表关联以获取更多信息。
  • trx_query:事务当前正在执行的SQL语句(如果有)。
  • trx_operation_state:事务当前正在执行的操作(如果有)。
  • trx_tables_in_use:事务当前正在使用的表的数量。
  • trx_tables_locked:事务当前锁定的表的数量。
  • trx_lock_structs:事务分配的锁结构的数量。
  • trx_lock_memory_bytes:事务分配的锁结构占用的内存字节数。
  • trx_rows_locked:事务锁定的行数。
  • trx_rows_modified:事务修改的行数。
  • trx_concurrency_tickets:事务在并发控制中使用的票据数。
  • trx_isolation_level:事务使用的隔离级别。
  • trx_unique_checks:事务是否启用唯一性检查。
  • trx_foreign_key_checks:事务是否启用外键检查。
  • trx_last_foreign_key_error:事务最后一次遇到的外键错误(如果有)。
  • trx_adaptive_hash_latched:事务是否持有自适应哈希索引锁。
  • trx_adaptive_hash_timeout:事务等待自适应哈希索引锁超时的时间(毫秒)。
  • trx_is_read_only:事务是否是只读的。
  • trx_autocommit_non_locking:事务是否是自动提交且非锁定的。
  • trx_schedule_weight:事务在调度器中使用的权重,表示其优先级和资源需求。

data_locks

这个表是性能表,用于记录当前数据库中的数据锁的信息

这个表包含了锁的类型,模式,状态,持有者,等待者等信息,可以帮助用户了解当前数据库中的数据锁的分布,以及排查可能出现的锁冲突等问题。

CREATE TABLE `performance_schema.data_locks` (
  `ENGINE` varchar(32) NOT NULL,
  `ENGINE_LOCK_ID` varchar(128) NOT NULL,
  `ENGINE_TRANSACTION_ID` bigint unsigned DEFAULT NULL,
  `THREAD_ID` bigint unsigned DEFAULT NULL,
  `EVENT_ID` bigint unsigned DEFAULT NULL,
  `OBJECT_SCHEMA` varchar(64) DEFAULT NULL,
  `OBJECT_NAME` varchar(64) DEFAULT NULL,
  `PARTITION_NAME` varchar(64) DEFAULT NULL,
  `SUBPARTITION_NAME` varchar(64) DEFAULT NULL,
  `INDEX_NAME` varchar(64) DEFAULT NULL,
  `OBJECT_INSTANCE_BEGIN` bigint unsigned NOT NULL,
  `LOCK_TYPE` varchar(32) NOT NULL,
  `LOCK_MODE` varchar(32) NOT NULL,
  `LOCK_STATUS` varchar(32) NOT NULL,
  `LOCK_DATA` varchar(8192) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL,
  PRIMARY KEY (`ENGINE_LOCK_ID`,`ENGINE`),
  KEY `ENGINE_TRANSACTION_ID` (`ENGINE_TRANSACTION_ID`,`ENGINE`),
  KEY `THREAD_ID` (`THREAD_ID`,`EVENT_ID`),
  KEY `OBJECT_SCHEMA` (`OBJECT_SCHEMA`,`OBJECT_NAME`,`PARTITION_NAME`,`SUBPARTITION_NAME`)
) ENGINE=PERFORMANCE_SCHEMA DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

这些字段的作用是:

  • ENGINE:持有或请求锁的存储引擎
  • ENGINE_LOCK_ID:存储引擎持有或请求的锁的ID
  • ENGINE_TRANSACTION_ID:请求锁的事务的ID
  • THREAD_ID:对应事务的线程ID
  • EVENT_ID:指明造成锁的事件ID
  • OBJECT_SCHEMA:对应锁表的**schema名称**
  • OBJECT_NAME:对应锁的表名
  • PARTITION_NAME:对应锁的分区名
  • SUBPARTITION_NAME:对应锁的子分区名
  • INDEX_NAME:对应锁的索引名
  • OBJECT_INSTANCE_BEGIN:锁对象在内存中的起始地址
  • LOCK_TYPE:锁的类型,如RECORDTABLE
  • LOCK_MODE:锁的模式,如SXISIX
  • LOCK_STATUS:锁的状态,如GRANTEDWAITING
  • LOCK_DATA:锁定的数据值

这张表比较重要, 后面会给出这张表怎么看的方法

data_lock_waits

这个表是性能表,用于记录当前数据库中的数据锁等待的信息。

这个表包含了等待锁的请求者,被等待的锁,以及等待的时间等信息,可以帮助用户了解当前数据库中的数据锁的竞争情况,以及优化事务的执行效率

CREATE TABLE `performance_schema.data_lock_waits` (
  `ENGINE` varchar(32) NOT NULL,
  `REQUESTING_ENGINE_LOCK_ID` varchar(128) NOT NULL,
  `REQUESTING_ENGINE_TRANSACTION_ID` bigint unsigned DEFAULT NULL,
  `REQUESTING_THREAD_ID` bigint unsigned DEFAULT NULL,
  `REQUESTING_EVENT_ID` bigint unsigned DEFAULT NULL,
  `REQUESTING_OBJECT_INSTANCE_BEGIN` bigint unsigned NOT NULL,
  `BLOCKING_ENGINE_LOCK_ID` varchar(128) NOT NULL,
  `BLOCKING_ENGINE_TRANSACTION_ID` bigint unsigned DEFAULT NULL,
  `BLOCKING_THREAD_ID` bigint unsigned DEFAULT NULL,
  `BLOCKING_EVENT_ID` bigint unsigned DEFAULT NULL,
  `BLOCKING_OBJECT_INSTANCE_BEGIN` bigint unsigned NOT NULL,
  KEY `REQUESTING_ENGINE_LOCK_ID` (`REQUESTING_ENGINE_LOCK_ID`,`ENGINE`),
  KEY `BLOCKING_ENGINE_LOCK_ID` (`BLOCKING_ENGINE_LOCK_ID`,`ENGINE`),
  KEY `REQUESTING_ENGINE_TRANSACTION_ID` (`REQUESTING_ENGINE_TRANSACTION_ID`,`ENGINE`),
  KEY `BLOCKING_ENGINE_TRANSACTION_ID` (`BLOCKING_ENGINE_TRANSACTION_ID`,`ENGINE`),
  KEY `REQUESTING_THREAD_ID` (`REQUESTING_THREAD_ID`,`REQUESTING_EVENT_ID`),
  KEY `BLOCKING_THREAD_ID` (`BLOCKING_THREAD_ID`,`BLOCKING_EVENT_ID`)
) ENGINE=PERFORMANCE_SCHEMA DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

这个表是用来显示当前等待数据锁的请求和阻塞它们的锁的。

每个字段的作用如下:

  • ENGINE:锁的引擎,如InnoDB
  • REQUESTING_ENGINE_LOCK_ID:请求锁的ID。
  • REQUESTING_ENGINE_TRANSACTION_ID:请求锁的事务ID。
  • REQUESTING_THREAD_ID:请求锁的线程ID。
  • REQUESTING_EVENT_ID:请求锁的事件ID。
  • REQUESTING_OBJECT_INSTANCE_BEGIN:请求锁的对象实例的起始地址。
  • BLOCKING_ENGINE_LOCK_ID:阻塞锁的ID。
  • BLOCKING_ENGINE_TRANSACTION_ID:阻塞锁的事务ID。
  • BLOCKING_THREAD_ID:阻塞锁的线程ID。
  • BLOCKING_EVENT_ID:阻塞锁的事件ID。
  • BLOCKING_OBJECT_INSTANCE_BEGIN:阻塞锁的对象实例的起始地址。