什么是锁?
共享资源在高并发环境下的一种保护
InnoDB
存储引擎的锁
共享锁
排他锁
说白了就是读写锁
对细颗粒度上下锁, 那么在粗颗粒粒度就需要上意向锁
意向锁就是表锁, 分为 IS 和 IX 意向共享锁和意向排他锁
比如对 r 共享资源上锁, 那么就需要对 表, 页上意向锁
也就是说, 在上锁前, 需要对粗颗粒粒度上IX意向锁, 然后在对 r 上 X 排它锁
如果在X锁之前, 粗颗粒上已经上锁其他锁, 比如 IS 意向共享锁, 意向锁之间是相互兼容的, 所以 IS 和 IX 兼容
但是 X 锁 和 IS 锁是不兼容的, 所以需要等待
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; | |
begin | |
UPDATE student SET age=23 WHERE id = 1; // 成功 | |
SELECT * FROM student WHERE age = 22; | |
SELECT * FROM student WHERE age = 22; | |
commit; | |
SELECT * FROM student WHERE age = 22; | |
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;
lock_type
表示我们上的锁是表锁还是行锁, 明显第一个是表锁, 后续都是行锁
这个是我们查询表的表名
索引的名称或者字段名字
这里显示的是分别是我们z表的b字段和主键
第一个是意向排他锁
, 也是个表锁
第二个是next-key lock
第三个是 record lock
第四个是 gap lock
第一个是表锁, 所以没有锁数据
第二个是 3, 5
, 表示 b = 3 , id = 5
这行
第三个 5 表示 主键 id = 5 这一行
第四个6, 7 , b = 6, id = 7
这一行
可以直接看这里
现在综合一下
表锁, 意向排他锁
我们执行的是SELECT * from z where b = 3 for update;
, for update
本身就是为了加上排他锁的
而在上排他锁之前, 需要上意向排他锁
前面的章节我们知道这里一个顺序问题, 先粗颗粒上意向锁, 再细颗粒上下排他或共享锁
根据第二行的数据, 我们可以分析出:
对b = 3 id = 5
这一行, 我们上 next-key lock
, 是左开右闭区间, 所以锁的范围应该是b
的(1.3, 3]
lock_mode
显示出他是 next-key lock
但是不需要 gap lock
的, 所以是 record lock
锁的范围是 id = 5
这一条
从 lock_mode
可以看出他是一个关于b字段
的 gap lock
, 而这个 6表示b=6这一行, 所以锁的范围是 (3, 6.7]
这个范围
所以综合一下, 锁的整理范围在 (1.3, 6.7]
Q: 这上面的
1.3
和6.7
是什么意思?A: 是一个带小数点的数字, 他的意思是 1 和 6 都表示 b 字段的值, 然后 小数点的 3 和 7 表示主键
我们还可以带个例子
现在我们插入
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;
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;
主键产生的间隙锁, 锁的范围是: (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;
c=11
共享锁 和 c=(11, 14]
共享间隙锁
sessionB
update t set d=d+0 where c=11;
阻塞
更新 c=11
这一行, 需要插入 排他锁 X
, 但是这一行已经被 sessionA
上了共享锁
, 不兼容
, 所以阻塞了
sessionC
:
INSERT into t SELECT 6, 6, 6;
阻塞, 在 sessionA
上锁的范围中
如果你需要 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;
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; -- 不允许
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;
(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;
范围是在 (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;
范围是 [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]
所以在这之间是无法添加新的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
:存储引擎持有或请求的锁的IDENGINE_TRANSACTION_ID
:请求锁的事务的IDTHREAD_ID
:对应事务的线程IDEVENT_ID
:指明造成锁的事件IDOBJECT_SCHEMA
:对应锁表的**schema
名称**OBJECT_NAME
:对应锁的表名PARTITION_NAME
:对应锁的分区名SUBPARTITION_NAME
:对应锁的子分区名INDEX_NAME
:对应锁的索引名OBJECT_INSTANCE_BEGIN
:锁对象在内存中的起始地址LOCK_TYPE
:锁的类型,如RECORD
,TABLE
等LOCK_MODE
:锁的模式,如S
,X
,IS
,IX
等LOCK_STATUS
:锁的状态,如GRANTED
,WAITING
等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
:阻塞锁的对象实例的起始地址。