MySQL查看事务与锁的操作示例小结

 更新时间:2025年12月29日 11:50:58   作者:ZePingPingZe  
本文介绍了MySQL中事务和锁的基本概念以及不同隔离级别下的锁类型,并通过实际测试验证了这些锁的使用情况,本文给大家介绍的非常详细,感兴趣的朋友跟随小编一起看看吧

1. MySQL如何查看事务与锁🔒

-- 1. 查看当前运行的事务
SELECT * FROM INFORMATION_SCHEMA.INNODB_TRX;
-- 2. 查看锁信息
-- 2.1 MySQL 5.7
SELECT * FROM INFORMATION_SCHEMA.INNODB_LOCKS;
SELECT * FROM INFORMATION_SCHEMA.INNODB_LOCK_WAITS;
-- 2.2 MySQL 8.0+
SELECT * FROM performance_schema.data_locks;
SELECT * FROM performance_schema.data_lock_waits;

从【MySQL-InnoDB锁、事务与MVCC】文章可以看出来,锁有下面几种分类:

  • 按锁模式分为:共享锁(S锁)、排它锁(X锁)。
  • 按锁粒度分为:表锁、行锁。
  • 表锁分为:表级S锁、表级X锁、表级IS锁(意向共享锁)、表级IX锁(意向排它锁)。
    • IS锁和IX锁的目的:只是为了后续 在加表级别的S锁和X锁时 判断 表中是否有已经被加锁的 记录,以避免用遍历的方式来查看表中有没有上锁的记录。
  • 行锁分为记录锁(Record Lock)、间隙锁(Gap Lock)、临键锁(Next-key Lock,Gap锁 + 记录锁)、隐式锁

对于MySQL的InnoDB存储引擎来说,不同事务隔离级别下使用的锁不同

  • READ COMMITTED(RC,读已提交)只有 记录锁(Record Lock),没有 间隙锁(Gap Lock)或 临键锁(Next-Key Lock)
  • REPEATABLE READ(RR,可重复读)默认使用 Next-Key Lock(记录锁+间隙锁),注意这里说的是【默认】,并不意味着RR级别下使用行级锁时全都用的是 Next-Key。

2. 不同事务隔离级别下的锁类型验证

以下测试基于MySQL_8.0.30

用来验证的cus_info表结构:

-- test.cus_info definition
CREATE TABLE `cus_info` (
  `id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键',
  `cus_id` varchar(21) NOT NULL COMMENT '客户编号',
  `phone_num` varchar(11) NOT NULL COMMENT '手机号',
  `nick_name` varchar(20) NOT NULL COMMENT '昵称',
  `sex` varchar(1) NOT NULL COMMENT '性别,0-女,1-男',
  PRIMARY KEY (`id`),
  UNIQUE KEY `uk_ci` (`cus_id`) USING BTREE, -- 二级索引:唯一索引
  UNIQUE KEY `uk_pn` (`phone_num`) USING BTREE -- 唯一索引
) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='客户信息表';
-- 插入语句
INSERT INTO cus_info (cus_id, phone_num, nick_name, sex) VALUES('200606011708203560002', '10000000002', '盖世无双', '0');
-- 更新语句
UPDATE cus_info SET cus_id='200606011708203560002', phone_num='10000000002', nick_name='盖世无双', sex='0' WHERE id=2;
-- 查询语句
SELECT id, cus_id, phone_num, nick_name, sex FROM cus_info WHERE id=2;

表数据如下:id(1, 2, 3, 5, 7, 8)

2.1 RC级别下的验证

先查看一下事务隔离级别:

-- 切换到test库
mysql> use test;
-- 查询事务隔离级别
mysql> show variables like '%isolation%';
+-----------------------+----------------+
| Variable_name         | Value          |
+-----------------------+----------------+
| transaction_isolation | READ-COMMITTED |
+-----------------------+----------------+
1 row in set (0.00 sec)

2.1.1 共享锁(S锁)

普通的select语句,InnoDB存储引擎不会加任何锁

mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)
-- 普通的select语句,InnoDB存储引擎不会加任何锁。可以通过 performance_schema.data_locks表查看。
mysql> select * from cus_info where id > 2 and id < 7;
-- 查询结果展示省略
-- 加 共享锁(S锁)
mysql> select * from cus_info where id > 2 and id < 7 for share;
+----+-----------------------+-------------+-----------+-----+
| id | cus_id                | phone_num   | nick_name | sex |
+----+-----------------------+-------------+-----------+-----+
|  3 | 200606011708203560003 | 10000000003 | 默默      | 0   |
|  5 | 200606011708203560005 | 10000000005 | 王        | 0   |
+----+-----------------------+-------------+-----------+-----+
2 rows in set (0.00 sec)

查询锁信息:performance_schema.data_locks

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

可以看到:

        ① 给表cus_info加了 表级别的IS锁(意向共享锁)。

        ② 给id(主键)为3和5的两条记录加了 Record Lock(记录锁)、S锁(共享锁),并且不是Gap锁。

-- 测试完后提交或回滚事务
mysql> rollback; -- 或者 commit
Query OK, 0 rows affected (0.00 sec)

2.1.2 排它锁(X锁🔒)

mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)

开启事务后,我们先来看一下 information_schema库的INNODB_TRX视图信息,可以看到没有任何数据,由此也可以看出来 start transaction; 开启一个事务并没有让 MySQL真正开始分配事务编号(trx_id)

mysql> select * from information_schema.INNODB_TRX;
Empty set (0.00 sec)

再执行一个 普通的select语句,再看看 INNODB_TRX 视图信息:

        trx_id(事务编号)、trx_state(事务状态,running运行中)、trx_started(事务开始时间)、trx_isolation_level(事务隔离级别)。注意trx_id(事务编号)的值,等下看看会发生什么变化。此时还没有加任何锁,所以 performance_schema.data_locks表中没有任何信息。

mysql> select * from cus_info where id > 2 and id < 7;
+----+-----------------------+-------------+-----------+-----+
| id | cus_id                | phone_num   | nick_name | sex |
+----+-----------------------+-------------+-----------+-----+
|  3 | 200606011708203560003 | 10000000003 | 默默      | 0   |
|  5 | 200606011708203560005 | 10000000005 | 王        | 0   |
+----+-----------------------+-------------+-----------+-----+
2 rows in set (0.00 sec)
-- 这里只查看事务的部分信息(INNODB_TRX视图列太多了,只展示现在需要的)
mysql> select trx_id, trx_state, trx_started, trx_isolation_level from information_schema.INNODB_TRX;
+-----------------+---------------------+-----------+---------------------+---------------------+
| trx_id          | trx_mysql_thread_id | trx_state | trx_started         | trx_isolation_level |
+-----------------+---------------------+-----------+---------------------+---------------------+
| 284035063683520 |                   8 | RUNNING   | 2025-12-28 17:28:27 | READ COMMITTED      |
+-----------------+---------------------+-----------+---------------------+---------------------+
1 row in set (0.00 sec)
mysql> select * from performance_schema.data_locks;
Empty set (0.00 sec)
1. Update操作
mysql> update cus_info set sex=1 where id=5;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0
-- 查看 information_schema.INNODB_TRX
mysql> select trx_id, trx_mysql_thread_id, trx_state,
    -> trx_started, trx_isolation_level from information_schema.INNODB_TRX;
+--------+---------------------+-----------+---------------------+---------------------+
| trx_id | trx_mysql_thread_id | trx_state | trx_started         | trx_isolation_level |
+--------+---------------------+-----------+---------------------+---------------------+
|  20301 |                   8 | RUNNING   | 2025-12-28 17:28:27 | READ COMMITTED      |
+--------+---------------------+-----------+---------------------+---------------------+
1 row in set (0.00 sec)

查看 information_schema.INNODB_TRX,发现 trx_id 发生了变化。

查看锁信息:performance_schema.data_locks,可以看到:

        ① 给表cus_info加了 表级别的IX锁(意向排他锁)。

        ② 给id(主键)为5的记录加了 Record Lock(记录锁)、X锁(排他锁),并且不是Gap锁。

2. Delete操作

接着上面的继续执行

mysql> delete from cus_info where id=3;
Query OK, 1 row affected (0.00 sec)

查看锁信息:performance_schema.data_locks

为了不影响下面的 select ... for update; 这里我们先回滚事务。

-- 为了不影响下面的 select ... for update; 这里我们先回滚事务
mysql> rollback; -- 或者 commit
Query OK, 0 rows affected (0.00 sec)
3.隐式锁和RC级别没有Gap锁的验证(Select...for update与Insert)

1)、开启事务1,并执行 select...for update 操作:

-- 开启事务1(spring框架中的事务管理器 DataSourceTransactionManager 就是这么开启一个事务的)
mysql> set autocommit = OFF;
Query OK, 0 rows affected (0.00 sec)
mysql> select * from cus_info where id > 3 and id < 8 for update;
+----+-----------------------+-------------+-----------+-----+
| id | cus_id                | phone_num   | nick_name | sex |
+----+-----------------------+-------------+-----------+-----+
|  5 | 200606011708203560005 | 10000000005 | 王        | 0   |
|  7 | 200606011708203560007 | 10000000007 | 赵        | 1   |
+----+-----------------------+-------------+-----------+-----+
2 rows in set (0.00 sec)

查看锁信息如下:只对(3, 8)这个区间里面 已存在的数据(id = 5 和 id = 7)进行加锁操作,不会对(3, 8)这个区间范围本身加锁。注意看这里加的 不是Gap锁,而是 行级排它锁。

这次,我们多查看一个列(engine_transaction_id,事务编号),当前事务1的事务编号为20307

2)、开启事务2,执行 insert 操作,可以看到 insert 成功了,说明事务1确实没有对(3, 8)这个区间范围本身加锁。由此可以说明,RC事务隔离级别下对于表中的记录 没有 Gap锁(间隙锁),只有 记录锁(Record Lock)

-- 1. 开启事务2
mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)
-- 2. 在insert前先select验证一下
mysql> select * from cus_info where id > 3 and id < 8;
+----+-----------------------+-------------+-----------+-----+
| id | cus_id                | phone_num   | nick_name | sex |
+----+-----------------------+-------------+-----------+-----+
|  5 | 200606011708203560005 | 10000000005 | 王        | 0   |
|  7 | 200606011708203560007 | 10000000007 | 赵        | 1   |
+----+-----------------------+-------------+-----------+-----+
2 rows in set (0.00 sec)
-- 3. insert id = 6 的记录成功了,说明事务1确实没有对(3, 8)这个区间范围本身加锁。
mysql> INSERT INTO cus_info (id, cus_id, phone_num, nick_name, sex) VALUES(6, '200606011708203560006', '10000000006', '阿文', '0');
Query OK, 1 row affected (0.00 sec)
-- 4. 再次select验证insert是否成功
mysql> select * from cus_info where id > 3 and id < 8;
+----+-----------------------+-------------+-----------+-----+
| id | cus_id                | phone_num   | nick_name | sex |
+----+-----------------------+-------------+-----------+-----+
|  5 | 200606011708203560005 | 10000000005 | 王        | 0   |
|  6 | 200606011708203560006 | 10000000006 | 阿文      | 0   |
|  7 | 200606011708203560007 | 10000000007 | 赵        | 1   |
+----+-----------------------+-------------+-----------+-----+
3 rows in set (0.00 sec)

我们再次来看 performance_schema.data_locks 锁信息,可以看到事务2(事务编号为20313)只有【1条】表级别的 IX锁(意向排它锁),并没有对 id = 6 的记录加任何锁(实际上,在MySQL的内存中,也就是在 Buffer Pool(缓冲池)的 数据页(页是MySQL中内存和磁盘进行数据交互的基本单位,页分为数据页、undo log页、redo log页[block]等等)上有一条 id=6的记录,并且该记录有一个【隐藏列 trx_id(事务编号)】,取值为 20313

3)、再回到事务1,执行 普通select 与 update操作(这里我们就更新 事务2插入的 id=6 的新纪录)。

① 先执行普通的 select语句,可以得出结论:事务1确实看不到事务2刚刚插入的id=6的新纪录(因为事务还没有commit,RC级别下,一个事务不会读取到另一个事物未提交的数据);

② 看是看不到(因为事务隔离级别是 RC,读已提交),但是更新操作却会阻塞,直到等待超时。

-- 1. 先执行普通的 select语句,可以得出结论:事务1确实看不到事务2刚刚插入的id=6的新纪录(因为事务还没有commit)
mysql> select * from cus_info where id > 3 and id < 8;
+----+-----------------------+-------------+-----------+-----+
| id | cus_id                | phone_num   | nick_name | sex |
+----+-----------------------+-------------+-----------+-----+
|  5 | 200606011708203560005 | 10000000005 | 王        | 0   |
|  7 | 200606011708203560007 | 10000000007 | 赵        | 1   |
+----+-----------------------+-------------+-----------+-----+
2 rows in set (0.00 sec)
-- 2. 看是看不到(因为事务隔离级别是 RC,读已提交),但是更新操作却会阻塞,直到等待超时。
mysql> update cus_info set sex=1 where id=6;
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction

我们 在update语句报错(报出 Lock wait timeout)之前 再来查看锁信息,可以看到:

① 事务2给 id = 6 的记录 加了 行级排它锁(X锁,不是Gap锁);

② 事务1的update语句因为要获取 事务2持有的锁,所以进入 waiting(等待状态)。

之所以会这样,是因为 insert语句 一开始给记录加的是【隐式X锁】,当update执行的时候,它会去表的 主键索引 对应的 B+树 去搜索,发现 id=6 的记录的 隐藏列trx_id=20313(事务2的事务编号),并且事务编号为20313的事务处于活跃状态(即,事务没有提交,information_schema.INNODB_TRX表中 trx_id=20313 的 trx_state(事务状态)为 running),这个时候就会给 performance_schema.data_locks 添加两条记录(即①、②)。

处于等待状态的锁要么在等待超时后死亡;要么在超时前事务2提交成功后获取到锁

        一个事务对新插入的记录可以不显式的加锁,但是由于【事务id】的存在,相当于加了一个【隐式锁】。其他事务对这条记录加S锁或者X锁时,由于隐式锁的存在,会先帮助当前事务生成一把锁,然后自己再生成一把锁并进入等待状态

2.2 RR级别下的验证

修改事务隔离级别:

-- 1. 设置事务隔离级别为:可重复读
mysql> set transaction_isolation = 'REPEATABLE-READ';
Query OK, 0 rows affected (0.00 sec)
-- 2. 查看设置是否成功
mysql> show variables like '%isolation%';
+-----------------------+-----------------+
| Variable_name         | Value           |
+-----------------------+-----------------+
| transaction_isolation | REPEATABLE-READ |
+-----------------------+-----------------+
1 row in set (0.00 sec)

表数据如下:id(1, 2, 3, 5, 7, 8)

2.2.1 共享锁(S锁)

-- 1. 开启事务
mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)
-- 2. 共享锁,select...for share
mysql> select * from cus_info where id > 2 and id < 7 for share;
+----+-----------------------+-------------+-----------+-----+
| id | cus_id                | phone_num   | nick_name | sex |
+----+-----------------------+-------------+-----------+-----+
|  3 | 200606011708203560003 | 10000000003 | 默默      | 0   |
|  5 | 200606011708203560005 | 10000000005 | 王        | 0   |
+----+-----------------------+-------------+-----------+-----+
2 rows in set (0.00 sec)

查看锁信息:performance_schema.data_locks

可以看到:

        ① 给表cus_info加了 表级别的IS锁(意向共享锁)。

        ② 加 Next-key Lock(临键锁,记录锁 + 间隙锁),给区间(2, 7)和 记录7 加锁。

-- 测试完后提交或回滚事务
mysql> rollback; -- 或者 commit
Query OK, 0 rows affected (0.00 sec)

2.2.2 排它锁(X锁🔒)

1. Update操作
-- 1. 开启事务
mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)
-- 2. 更新
mysql> update cus_info set sex=1 where id=5;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0

查看锁信息:performance_schema.data_locks。

注意看,这里加的行级锁为:X锁(排它锁)、not gap(不是gap锁)。唯一索引的等值查询(包括主键索引、唯一二级索引),Next-Key锁会退化为记录锁

-- 3. 根据 phone_num(唯一索引)更新
mysql> update cus_info set sex=1 where phone_num='10000000005';
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0

再次查看锁信息:performance_schema.data_locks。注意看 index_name:uk_pn、primary

-- 4. 更新区间 (3, 8)
mysql> update cus_info set sex=0 where id>3 and id<8;
Query OK, 1 row affected (0.00 sec)
Rows matched: 2  Changed: 1  Warnings: 0

再次查看锁信息:performance_schema.data_locks。这里又是对 (3, 8] 这个区间加 Next-Key Lock(临键锁,记录锁 + 间隙锁)。

2. Delete操作
mysql> delete from cus_info where id=3;
Query OK, 1 row affected (0.00 sec)

查看锁信息:performance_schema.data_locks

注意看,这里加的行级锁为:X锁(排它锁)、not gap(不是gap锁)。

-- 为了不影响下面的 select ... for update; 这里我们先回滚事务
mysql> rollback; -- 或者 commit
Query OK, 0 rows affected (0.00 sec)
3. RR级别Next-Key锁验证

1)、开启事务1,并执行 select...for update 操作:

mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)
mysql> select * from cus_info where id > 3 and id < 8 for update;
+----+-----------------------+-------------+-----------+-----+
| id | cus_id                | phone_num   | nick_name | sex |
+----+-----------------------+-------------+-----------+-----+
|  5 | 200606011708203560005 | 10000000005 | 王        | 0   |
|  7 | 200606011708203560007 | 10000000007 | 赵        | 1   |
+----+-----------------------+-------------+-----------+-----+
2 rows in set (0.00 sec)

查看锁信息:performance_schema.data_locks。给区间(3, 8)和 记录8 加锁,即给区间 (3, 8] 加了锁。事务1的编号为20320

2)、开启事务2,执行 insert 操作,可以看到 insert 失败了,说明事务1确实对(3, 8] 这个区间范围本身加了 gap锁+记录锁。

-- 1. 开启事务2
mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)
-- 2. insert前先执行 普通select查询看看
mysql> select * from cus_info where id > 3 and id < 8;
+----+-----------------------+-------------+-----------+-----+
| id | cus_id                | phone_num   | nick_name | sex |
+----+-----------------------+-------------+-----------+-----+
|  5 | 200606011708203560005 | 10000000005 | 王        | 0   |
|  7 | 200606011708203560007 | 10000000007 | 赵        | 1   |
+----+-----------------------+-------------+-----------+-----+
2 rows in set (0.00 sec)
-- 3. insert id=6的记录 失败,因为事务1给 (3, 8] 区间加了 gap锁+记录锁。
mysql> INSERT INTO cus_info (id, cus_id, phone_num, nick_name, sex) VALUES(6, '200606011708203560006', '10000000006', '阿文', '0');
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction

我们 在insert语句报错(报出 Lock wait timeout)之前 再来查看锁信息,可以看到:事务2(事务编号为20321)生成的行锁为:X锁(排它锁)、Gap(间隙锁)、insert intention(插入意向锁),并处于 waiting 状态。

到此这篇关于MySQL查看事务与锁的文章就介绍到这了,更多相关mysql查看事务与锁内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • mysql中replace into与insert into区别

    mysql中replace into与insert into区别

    本文主要介绍了mysql中replace into与insert into区别,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-01-01
  • MySQL多表查询的案例详解

    MySQL多表查询的案例详解

    这篇文章主要介绍了MySQL多表查询的案例说明,包括多表查询的分类及umion的使用,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2022-03-03
  • MySQL中增量备份的几种实现方法

    MySQL中增量备份的几种实现方法

    MySQL数据库的增量备份是确保数据安全和可恢复性的关键策略,本文就来介绍一下如何实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2025-01-01
  • MySQL忘记密码恢复密码的实现方法

    MySQL忘记密码恢复密码的实现方法

    流传较广的方法,mysql中文参考手册上的,各位vps主机租用客户和服务器托管用户忘记mysql5.1管理员密码时,可以使用这种方法破解下
    2008-07-07
  • MySQL 深分页查询优化实践与经验分享

    MySQL 深分页查询优化实践与经验分享

    文章总结了在企业级项目中优化深分页查询的经验,包括执行计划分析、索引优化和游标分页改写,通过复合索引和游标分页,可以显著提高查询性能,感兴趣的朋友跟随小编一起看看吧
    2025-12-12
  • MySQL数据表添加字段的三种方式总结

    MySQL数据表添加字段的三种方式总结

    这篇文章主要给大家介绍了关于MySQL数据表添加字段的三种方式,分别是末尾追加、首列插入、指定位置插入,均使用ALTER TABLE语句,文中提供了详细的代码示例,需要的朋友可以参考下
    2025-07-07
  • mysql优化系列 DELETE子查询改写优化

    mysql优化系列 DELETE子查询改写优化

    有个采用子查询的DELETE执行得非常慢,改写成SELECT后执行却很快,最后把这个子查询DELETE改写成JOIN优化过程
    2016-08-08
  • MySQL中with窗口函数说明及使用案例总结

    MySQL中with窗口函数说明及使用案例总结

    这篇文章主要介绍了MySQL中with窗口函数说明及使用案例的相关资料,窗口函数允许在查询结果的特定窗口上执行计算,而不会改变结果集的行数,文中通过代码介绍的非常详细,需要的朋友可以参考下
    2025-11-11
  • mysql 8.0 错误The server requested authentication method unknown to the client解决方法

    mysql 8.0 错误The server requested authentication method unkno

    在本篇文章里小编给大家整理的是关于mysql 8.0 错误The server requested authentication method unknown to the client解决方法,有此需要的朋友们可以学习下。
    2019-08-08
  • MySQL和PolarDB的相同点及不同点解读

    MySQL和PolarDB的相同点及不同点解读

    这篇文章主要介绍了MySQL和PolarDB的相同点及不同点,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2025-03-03

最新评论