[学习记录]MySQL事务
学习目标
- 在REPEATABLE READ隔离级别下,
事务A
执行select,事务B
执行insert, 看看锁到底是什么情况. 同时重点看看,id是varchar但是写sql时候写的是number, 会有什么危害
SQL代码块记录
1. 事务A
执行select, 加S锁. 且 仅通过主键, 过滤出一条结果. 事务B
修改同一行数据. 但是id列本是varchar类型, 但是写sql时,写的是数字
代码块
-- 1. 先执行事务A的查询(注意没提交)
mysql> BEGIN;SELECT * FROM article WHERE id=3 LOCK IN SHARE MODE;
Query OK, 0 rows affected (0.00 sec)
+----+-------+-----------+--------------+
| id | name | words_num | content_desc |
+----+-------+-----------+--------------+
| 3 | test2 | 233 | 1 |
+----+-------+-----------+--------------+
1 row in set (0.01 sec)
-- 2. 查看当前锁的情况和事务的情况
mysql> SELECT engine_lock_id, lock_type, lock_mode, lock_status, lock_data FROM performance_schema.data_locks;
+-------------------------------------+-----------+-----------+-------------+------------------------+
| engine_lock_id | lock_type | lock_mode | lock_status | lock_data |
+-------------------------------------+-----------+-----------+-------------+------------------------+
| 2676907382656:1073:2676913015832 | TABLE | IS | GRANTED | NULL |
| 2676907382656:11:4:1:2676896890904 | RECORD | S | GRANTED | supremum pseudo-record |
| 2676907382656:11:4:2:2676896890904 | RECORD | S | GRANTED | '1' |
| 2676907382656:11:4:3:2676896890904 | RECORD | S | GRANTED | '110801' |
| 2676907382656:11:4:4:2676896890904 | RECORD | S | GRANTED | '2' |
| 2676907382656:11:4:13:2676896890904 | RECORD | S | GRANTED | '4' |
| 2676907382656:11:4:14:2676896890904 | RECORD | S | GRANTED | '5' |
| 2676907382656:11:4:15:2676896890904 | RECORD | S | GRANTED | '3' |
+-------------------------------------+-----------+-----------+-------------+------------------------+
8 rows in set (0.04 sec)
mysql>
select trx_id,trx_isolation_level,trx_state,trx_requested_lock_id from INNODB_TRX;
+-----------------+---------------------+-----------+-----------------------+
| trx_id | trx_isolation_level | trx_state | trx_requested_lock_id |
+-----------------+---------------------+-----------+-----------------------+
| 284151884093312 | REPEATABLE READ | RUNNING | NULL |
+-----------------+---------------------+-----------+-----------------------+
1 row in set (0.03 sec)
-- 3. 事务B中进行更新
mysql> BEGIN;UPDATE article SET words_num = 999 WHERE id = 3;
Query OK, 0 rows affected (0.00 sec)
-- (卡住)
-- 4. 查看当前锁的情况和事务的情况
mysql> SELECT engine_lock_id, lock_type, lock_mode, lock_status, lock_data FROM performance_schema.data_locks;
+-------------------------------------+-----------+-----------+-------------+------------------------+
| engine_lock_id | lock_type | lock_mode | lock_status | lock_data |
+-------------------------------------+-----------+-----------+-------------+------------------------+
| 2676907383432:1073:2676913016600 | TABLE | IX | GRANTED | NULL |
| 2676907383432:11:4:2:2676896893976 | RECORD | X | WAITING | '1' |
| 2676907382656:1073:2676913015832 | TABLE | IS | GRANTED | NULL |
| 2676907382656:11:4:1:2676896890904 | RECORD | S | GRANTED | supremum pseudo-record |
| 2676907382656:11:4:2:2676896890904 | RECORD | S | GRANTED | '1' |
| 2676907382656:11:4:3:2676896890904 | RECORD | S | GRANTED | '110801' |
| 2676907382656:11:4:4:2676896890904 | RECORD | S | GRANTED | '2' |
| 2676907382656:11:4:13:2676896890904 | RECORD | S | GRANTED | '4' |
| 2676907382656:11:4:14:2676896890904 | RECORD | S | GRANTED | '5' |
| 2676907382656:11:4:15:2676896890904 | RECORD | S | GRANTED | '3' |
+-------------------------------------+-----------+-----------+-------------+------------------------+
10 rows in set (0.04 sec)
mysql>
select trx_id,trx_isolation_level,trx_state,trx_requested_lock_id from INNODB_TRX;
+-----------------+---------------------+-----------+------------------------------------+
| trx_id | trx_isolation_level | trx_state | trx_requested_lock_id |
+-----------------+---------------------+-----------+------------------------------------+
| 15430 | REPEATABLE READ | LOCK WAIT | 2676907383432:11:4:2:2676896893976 |
| 284151884093312 | REPEATABLE READ | RUNNING | NULL |
+-----------------+---------------------+-----------+------------------------------------+
2 rows in set (0.04 sec)
--5. 提交事务A
mysql> COMMIT;
Query OK, 0 rows affected (0.00 sec)
-- 提交后,事务B中马上能看到
Query OK, 1 row affected (10.60 sec)
Rows matched: 1 Changed: 1 Warnings: 0
--6. 查看当前锁的情况和事务的情况
mysql> SELECT engine_lock_id, lock_type, lock_mode, lock_status, lock_data FROM performance_schema.data_locks;
+-------------------------------------+-----------+-----------+-------------+------------------------+
| engine_lock_id | lock_type | lock_mode | lock_status | lock_data |
+-------------------------------------+-----------+-----------+-------------+------------------------+
| 2676907383432:1073:2676913016600 | TABLE | IX | GRANTED | NULL |
| 2676907383432:11:4:1:2676896893976 | RECORD | X | GRANTED | supremum pseudo-record |
| 2676907383432:11:4:2:2676896893976 | RECORD | X | GRANTED | '1' |
| 2676907383432:11:4:3:2676896893976 | RECORD | X | GRANTED | '110801' |
| 2676907383432:11:4:4:2676896893976 | RECORD | X | GRANTED | '2' |
| 2676907383432:11:4:13:2676896893976 | RECORD | X | GRANTED | '4' |
| 2676907383432:11:4:14:2676896893976 | RECORD | X | GRANTED | '5' |
| 2676907383432:11:4:15:2676896893976 | RECORD | X | GRANTED | '3' |
+-------------------------------------+-----------+-----------+-------------+------------------------+
8 rows in set (0.04 sec)
mysql>
select trx_id,trx_isolation_level,trx_state,trx_requested_lock_id from INNODB_TRX;
+--------+---------------------+-----------+-----------------------+
| trx_id | trx_isolation_level | trx_state | trx_requested_lock_id |
+--------+---------------------+-----------+-----------------------+
| 15430 | REPEATABLE READ | RUNNING | NULL |
+--------+---------------------+-----------+-----------------------+
1 row in set (0.04 sec)
--8. 提交事务B, 然后看看这个表的全部id
mysql> COMMIT;
Query OK, 0 rows affected (0.00 sec)
mysql> select id from article;
+--------+
| id |
+--------+
| 1 |
| 110801 |
| 2 |
| 3 |
| 4 |
| 5 |
+--------+
6 rows in set (0.02 sec)
-- EXPLAIN一下语句看看
mysql> EXPLAIN SELECT * FROM article WHERE id="3" LOCK IN SHARE MODE;
+----+-------------+---------+------------+-------+---------------+---------+---------+-------+------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+---------+------------+-------+---------------+---------+---------+-------+------+----------+-------+
| 1 | SIMPLE | article | NULL | const | PRIMARY | PRIMARY | 258 | const | 1 | 100.00 | NULL |
+----+-------------+---------+------------+-------+---------------+---------+---------+-------+------+----------+-------+
1 row in set (0.02 sec)
mysql> EXPLAIN SELECT * FROM article WHERE id=3 LOCK IN SHARE MODE;
+----+-------------+---------+------------+------+---------------+------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+---------+------------+------+---------------+------+---------+------+------+----------+-------------+
| 1 | SIMPLE | article | NULL | ALL | PRIMARY | NULL | NULL | NULL | 6 | 16.67 | Using where |
+----+-------------+---------+------------+------+---------------+------+---------+------+------+----------+-------------+
1 row in set (0.03 sec)
mysql> EXPLAIN SELECT * FROM article WHERE id=3;
+----+-------------+---------+------------+------+---------------+------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+---------+------------+------+---------------+------+---------+------+------+----------+-------------+
| 1 | SIMPLE | article | NULL | ALL | PRIMARY | NULL | NULL | NULL | 6 | 16.67 | Using where |
+----+-------------+---------+------------+------+---------------+------+---------+------+------+----------+-------------+
1 row in set (0.03 sec)
现象
事务A
和事务B
与之前学习的时候一样的操作, 但是只是写的数字, 可以看到 锁覆盖了整个表的id, 而且还出现了一个没见过的supremum pseudo-record
- 在
事务A
查询没有提交的时候,事务B
在执行update的时候 , 只出现了等一个锁, 而且看起来是按顺序来等着要锁的
GPT总结
现象揭示了MySQL InnoDB引擎在处理SQL语句和锁机制时一些细致而特殊的行为。这些现象可以从数据类型的转换、记录锁的工作原理,以及InnoDB的实现细节三个层面进行解析。
1. VARCHAR主键与WHERE条件中的隐式类型转换
- 现象:
在表中,id
是VARCHAR
类型,SQL语句中使用WHERE id = number
会锁定整个表的多个记录,而WHERE id = "string"
只锁定匹配的行。 - 原因:
- InnoDB的锁是基于索引的,而索引操作依赖于SQL条件的精确匹配。
- 当
id
为VARCHAR
类型时:- 使用
WHERE id = "string"
会正确匹配索引项,并仅对匹配的行加锁。 - 使用
WHERE id = number
时,MySQL会隐式地将number
转换为VARCHAR
,但这种隐式转换可能导致索引扫描行为异常。
- 使用
- 隐式转换的影响:
- MySQL可能认为
number
与多个记录潜在匹配,导致锁范围扩展(即锁住更多行或整个索引范围)。 - 具体锁表现:
LOCK IN SHARE MODE
:会为可能匹配的行加共享锁,包括主键上的索引范围。- 这就是为什么整个表看起来被锁住了。
- MySQL可能认为
2. supremum pseudo-record
的含义
- 现象:
performance_schema.data_locks
中出现了supremum pseudo-record
。 - 原因:
supremum pseudo-record
是 InnoDB 索引页的逻辑结束标记:- 每个索引页(B+树的一个节点)都会有一个额外的伪记录,表示当前页中所有记录的上界。
- 它本身并不对应任何真实数据,但会参与锁竞争。
- 当你对表进行操作(例如
LOCK IN SHARE MODE
或FOR UPDATE
),MySQL可能会在锁定索引记录时附带锁定supremum pseudo-record
。
- 锁定伪记录的目的:
- 保护索引页的完整性,防止其他事务插入新的记录到同一个索引范围内。
- 这是InnoDB的实现细节,确保B+树的插入、删除、更新操作的安全性。
- 建议:
- 如果频繁出现伪记录锁,可以检查是否存在大量的范围扫描操作。
3. 事务B
等待id='1'
的X锁,而非目标行id='3'
- 现象:
在事务A
未提交时,事务B
的UPDATE
操作锁住了id='1'
的记录,而非目标行id='3'
。 - 原因:
- 行级锁与索引范围锁:
- InnoDB 的行锁(
record lock
)依赖于索引。 - 如果查询条件未能直接精确匹配主键,InnoDB可能会进行范围扫描,锁定整个索引范围。
- 在
WHERE id = number
的情况下,由于隐式类型转换,可能导致索引扫描从id='1'
开始,逐行尝试加锁。
- InnoDB 的行锁(
- 锁顺序:
- InnoDB按照索引顺序加锁。
- 在尝试获取目标行(
id='3'
)的锁之前,事务B会按照索引顺序等待之前记录的锁释放,例如id='1'
。 - 这也解释了事务B在等待
id='1'
的锁,而不是直接等待id='3'
。
- 行级锁与索引范围锁:
改进与建议
-
避免隐式类型转换:
- 在SQL语句中,始终确保主键值类型与列定义匹配。
- 例如,如果主键是
VARCHAR
,则使用WHERE id = "string"
,而不是WHERE id = number
。
-
优化锁定范围:
- 使用主键查询,确保InnoDB只对目标行加锁,而非范围锁。
- 避免在事务中长时间持有共享锁或排他锁,减少锁竞争。
-
监控和调试锁行为:
- 使用
performance_schema.data_locks
和INNODB_TRX
检查锁状态和事务等待。 - 通过分析锁的顺序和范围,优化SQL语句和索引设计。
- 使用
-
关于
supremum pseudo-record
:- 不需要额外处理,这种锁是InnoDB的正常行为。
- 如果频繁出现伪记录锁,可以检查是否存在大量的范围扫描操作。
总结
你的现象揭示了InnoDB在类型转换、索引操作和锁管理上的细节特性。这些细节不仅影响性能,还可能导致意外的锁范围扩展。通过明确SQL语句的类型匹配、优化查询条件和索引设计,可以有效避免这些问题,提高事务的并发性能和可靠性。
2. 执行和上步骤一样的内容, 但是事务B
替换成INSERT
看看效果
代码块
-- 1. 开启事务A, id用number
mysql> BEGIN;SELECT * FROM article WHERE id=3 LOCK IN SHARE MODE;
Query OK, 0 rows affected (0.00 sec)
+----+-------+-----------+--------------+
| id | name | words_num | content_desc |
+----+-------+-----------+--------------+
| 3 | test2 | 999 | 1 |
+----+-------+-----------+--------------+
1 row in set (0.03 sec)
-- 2. 开启事务B
mysql> BEGIN;INSERT into article (id, `name`, words_num) VALUES ("6", "test6", 1);
Query OK, 0 rows affected (0.00 sec)
-- (卡住了)
-- 3.查看锁和事务
mysql> SELECT engine_lock_id, lock_type, lock_mode, lock_status, lock_data FROM performance_schema.data_locks;
+-------------------------------------+-----------+--------------------+-------------+------------------------+
| engine_lock_id | lock_type | lock_mode | lock_status | lock_data |
+-------------------------------------+-----------+--------------------+-------------+------------------------+
| 2676907383432:1073:2676913016600 | TABLE | IX | GRANTED | NULL |
| 2676907383432:11:4:1:2676896893976 | RECORD | X,INSERT_INTENTION | WAITING | supremum pseudo-record |
| 2676907382656:1073:2676913015832 | TABLE | IS | GRANTED | NULL |
| 2676907382656:11:4:1:2676896890904 | RECORD | S | GRANTED | supremum pseudo-record |
| 2676907382656:11:4:2:2676896890904 | RECORD | S | GRANTED | '1' |
| 2676907382656:11:4:3:2676896890904 | RECORD | S | GRANTED | '110801' |
| 2676907382656:11:4:4:2676896890904 | RECORD | S | GRANTED | '2' |
| 2676907382656:11:4:13:2676896890904 | RECORD | S | GRANTED | '4' |
| 2676907382656:11:4:14:2676896890904 | RECORD | S | GRANTED | '5' |
| 2676907382656:11:4:15:2676896890904 | RECORD | S | GRANTED | '3' |
+-------------------------------------+-----------+--------------------+-------------+------------------------+
10 rows in set (0.04 sec)
mysql>
select trx_id,trx_isolation_level,trx_state,trx_requested_lock_id from INNODB_TRX;
+-----------------+---------------------+-----------+------------------------------------+
| trx_id | trx_isolation_level | trx_state | trx_requested_lock_id |
+-----------------+---------------------+-----------+------------------------------------+
| 15432 | REPEATABLE READ | LOCK WAIT | 2676907383432:11:4:1:2676896893976 |
| 284151884093312 | REPEATABLE READ | RUNNING | NULL |
+-----------------+---------------------+-----------+------------------------------------+
2 rows in set (0.04 sec)
-- 4. 提交事务A
mysql> COMMIT;
Query OK, 0 rows affected (0.00 sec)
-- 同时能看到事务B
Query OK, 1 row affected (27.19 sec)
-- 5. 查看锁和事务
mysql> SELECT engine_lock_id, lock_type, lock_mode, lock_status, lock_data FROM performance_schema.data_locks;
+------------------------------------+-----------+--------------------+-------------+------------------------+
| engine_lock_id | lock_type | lock_mode | lock_status | lock_data |
+------------------------------------+-----------+--------------------+-------------+------------------------+
| 2676907383432:1073:2676913016600 | TABLE | IX | GRANTED | NULL |
| 2676907383432:11:4:1:2676896893976 | RECORD | X,INSERT_INTENTION | GRANTED | supremum pseudo-record |
+------------------------------------+-----------+--------------------+-------------+------------------------+
2 rows in set (0.04 sec)
mysql>
select trx_id,trx_isolation_level,trx_state,trx_requested_lock_id from INNODB_TRX;
+--------+---------------------+-----------+-----------------------+
| trx_id | trx_isolation_level | trx_state | trx_requested_lock_id |
+--------+---------------------+-----------+-----------------------+
| 15432 | REPEATABLE READ | RUNNING | NULL |
+--------+---------------------+-----------+-----------------------+
1 row in set (0.04 sec)
-- 6. 在事务A窗口, 再开一个事务A1
BEGIN;SELECT * FROM article WHERE id=3 LOCK IN SHARE MODE;
-- (卡住了)
-- 6至7之间, 事后再次测试中, 增加一个状态的查询. 因为是**另外补充的**, 所以id不一样,不用管
mysql>
select trx_id,trx_isolation_level,trx_state,trx_requested_lock_id from INNODB_TRX;
+-----------------+---------------------+-----------+-------------------------------------+
| trx_id | trx_isolation_level | trx_state | trx_requested_lock_id |
+-----------------+---------------------+-----------+-------------------------------------+
| 15442 | REPEATABLE READ | RUNNING | NULL |
| 284151884093312 | REPEATABLE READ | LOCK WAIT | 2676907382656:11:4:16:2676896891248 |
+-----------------+---------------------+-----------+-------------------------------------+
2 rows in set (0.04 sec)
mysql> SELECT engine_lock_id, lock_type, lock_mode, lock_status, lock_data FROM performance_schema.data_locks;
+-------------------------------------+-----------+--------------------+-------------+------------------------+
| engine_lock_id | lock_type | lock_mode | lock_status | lock_data |
+-------------------------------------+-----------+--------------------+-------------+------------------------+
| 2676907383432:1073:2676913016600 | TABLE | IX | GRANTED | NULL |
| 2676907383432:11:4:1:2676896893976 | RECORD | X,INSERT_INTENTION | GRANTED | supremum pseudo-record |
| 2676907383432:11:4:16:2676896894320 | RECORD | X,REC_NOT_GAP | GRANTED | '6' |
| 2676907382656:1073:2676913015832 | TABLE | IS | GRANTED | NULL |
| 2676907382656:11:4:2:2676896890904 | RECORD | S | GRANTED | '1' |
| 2676907382656:11:4:3:2676896890904 | RECORD | S | GRANTED | '110801' |
| 2676907382656:11:4:4:2676896890904 | RECORD | S | GRANTED | '2' |
| 2676907382656:11:4:13:2676896890904 | RECORD | S | GRANTED | '4' |
| 2676907382656:11:4:14:2676896890904 | RECORD | S | GRANTED | '5' |
| 2676907382656:11:4:15:2676896890904 | RECORD | S | GRANTED | '3' |
| 2676907382656:11:4:16:2676896891248 | RECORD | S | WAITING | '6' |
+-------------------------------------+-----------+--------------------+-------------+------------------------+
11 rows in set (0.04 sec)
-- 7. 等待事务A1超时后, 看一下锁和事务的状态
mysql> SELECT engine_lock_id, lock_type, lock_mode, lock_status, lock_data FROM performance_schema.data_locks;
+-------------------------------------+-----------+--------------------+-------------+------------------------+
| engine_lock_id | lock_type | lock_mode | lock_status | lock_data |
+-------------------------------------+-----------+--------------------+-------------+------------------------+
| 2676907382656:1073:2676913015832 | TABLE | IS | GRANTED | NULL |
| 2676907382656:11:4:2:2676896890904 | RECORD | S | GRANTED | '1' |
| 2676907382656:11:4:3:2676896890904 | RECORD | S | GRANTED | '110801' |
| 2676907382656:11:4:4:2676896890904 | RECORD | S | GRANTED | '2' |
| 2676907382656:11:4:13:2676896890904 | RECORD | S | GRANTED | '4' |
| 2676907382656:11:4:14:2676896890904 | RECORD | S | GRANTED | '5' |
| 2676907382656:11:4:15:2676896890904 | RECORD | S | GRANTED | '3' |
| 2676907383432:1073:2676913016600 | TABLE | IX | GRANTED | NULL |
| 2676907383432:11:4:1:2676896893976 | RECORD | X,INSERT_INTENTION | GRANTED | supremum pseudo-record |
| 2676907383432:11:4:16:2676896894320 | RECORD | X,REC_NOT_GAP | GRANTED | '6' |
+-------------------------------------+-----------+--------------------+-------------+------------------------+
10 rows in set (0.04 sec)
mysql>
select trx_id,trx_isolation_level,trx_state,trx_requested_lock_id from INNODB_TRX;
+--------+---------------------+-----------+-----------------------+
| trx_id | trx_isolation_level | trx_state | trx_requested_lock_id |
+--------+---------------------+-----------+-----------------------+
| 15437 | REPEATABLE READ | RUNNING | NULL |
| 15432 | REPEATABLE READ | RUNNING | NULL |
+--------+---------------------+-----------+-----------------------+
2 rows in set (0.04 sec)
-- 8. 提交事务A1, 重新使用'3'再开启一个事务A2
mysql> BEGIN;SELECT * FROM article WHERE id="3" LOCK IN SHARE MODE;
Query OK, 0 rows affected (0.00 sec)
+----+-------+-----------+--------------+
| id | name | words_num | content_desc |
+----+-------+-----------+--------------+
| 3 | test2 | 999 | 1 |
+----+-------+-----------+--------------+
1 row in set (0.02 sec)
-- 9. 此时锁和事务的状态
mysql> SELECT engine_lock_id, lock_type, lock_mode, lock_status, lock_data FROM performance_schema.data_locks;
+-------------------------------------+-----------+--------------------+-------------+------------------------+
| engine_lock_id | lock_type | lock_mode | lock_status | lock_data |
+-------------------------------------+-----------+--------------------+-------------+------------------------+
| 2676907383432:1073:2676913016600 | TABLE | IX | GRANTED | NULL |
| 2676907383432:11:4:1:2676896893976 | RECORD | X,INSERT_INTENTION | GRANTED | supremum pseudo-record |
| 2676907383432:11:4:16:2676896894320 | RECORD | X,REC_NOT_GAP | GRANTED | '6' |
| 2676907382656:1073:2676913015832 | TABLE | IS | GRANTED | NULL |
| 2676907382656:11:4:15:2676896890904 | RECORD | S,REC_NOT_GAP | GRANTED | '3' |
+-------------------------------------+-----------+--------------------+-------------+------------------------+
5 rows in set (0.04 sec)
mysql>
select trx_id,trx_isolation_level,trx_state,trx_requested_lock_id from INNODB_TRX;
+-----------------+---------------------+-----------+-----------------------+
| trx_id | trx_isolation_level | trx_state | trx_requested_lock_id |
+-----------------+---------------------+-----------+-----------------------+
| 15432 | REPEATABLE READ | RUNNING | NULL |
| 284151884093312 | REPEATABLE READ | RUNNING | NULL |
+-----------------+---------------------+-----------+-----------------------+
2 rows in set (0.04 sec)
-- 10. 提交事务B, 然后再看看锁和事务状态
mysql> COMMIT;
Query OK, 0 rows affected (0.00 sec)
mysql> SELECT engine_lock_id, lock_type, lock_mode, lock_status, lock_data FROM performance_schema.data_locks;
+-------------------------------------+-----------+---------------+-------------+-----------+
| engine_lock_id | lock_type | lock_mode | lock_status | lock_data |
+-------------------------------------+-----------+---------------+-------------+-----------+
| 2676907382656:1073:2676913015832 | TABLE | IS | GRANTED | NULL |
| 2676907382656:11:4:15:2676896890904 | RECORD | S,REC_NOT_GAP | GRANTED | '3' |
+-------------------------------------+-----------+---------------+-------------+-----------+
2 rows in set (0.04 sec)
mysql>
select trx_id,trx_isolation_level,trx_state,trx_requested_lock_id from INNODB_TRX;
+-----------------+---------------------+-----------+-----------------------+
| trx_id | trx_isolation_level | trx_state | trx_requested_lock_id |
+-----------------+---------------------+-----------+-----------------------+
| 284151884093312 | REPEATABLE READ | RUNNING | NULL |
+-----------------+---------------------+-----------+-----------------------+
1 row in set (0.04 sec)
-- 再重新试一下, 事务B的insert. 在此之前, 所有事务都提交了,然后数据库中id是'6'的那一行数据已经删除了
-- 1. 开事务B
mysql> BEGIN;INSERT into article (id, `name`, words_num) VALUES ("6", "test6", 1);
Query OK, 0 rows affected (0.00 sec)
Query OK, 1 row affected (0.00 sec)
-- 2. 用事务C查询
mysql> BEGIN;SELECT * FROM article WHERE id="6" LOCK IN SHARE MODE;
-- (会卡住)
-- 3. 提交了事务C后, 再开启一个事务C1
mysql> COMMIT;
Query OK, 0 rows affected (0.00 sec)
mysql> BEGIN;SELECT * FROM article WHERE id="6";
Query OK, 0 rows affected (0.00 sec)
Empty set
现象
事务A
在id是varchar但是使用number写where时, 锁上了很多内容, 这会导致事务B
如果要进行insert
, 会卡住,事务B
在等supremum pseudo-record
的锁- 当
事务B
不提交时,事务A1
错误使用where id= number的写法时,会多生成了一个id’6’的X锁(这个X锁是事务B
的), 且事务A1
至少先在等待的是id’6’的S锁. 事务A1
虽然因为拿不到id’6’的S锁卡住, 并且最后超时了, 但是超时后, 其他S锁拿到了,而且不会丢掉.- 当
事务B
不提交时,事务C
中针对id’6’加S锁查询时, 会卡住. - 当
事务B
不提交时,事务C1
中针对id’6’不加锁查询, 不会卡住, 当然也查不到结果. - 当
事务B
不提交时,事务A2
中针对id’3’'加S锁查询时,可以正常执行.
GPT总结
事务A1
等待 id=6
的共享锁(S锁),超时后仍能保留其他 S 锁
这是因为 MySQL 的锁机制允许事务部分获取资源。如果某些锁无法获取(如 id=6
的 S 锁),但其他资源已经成功加锁,这些锁会被保留。
事务A1
使用了共享锁来查询id=3
,但由于锁冲突,未能成功获取id=6
的 S 锁。- 超时后,事务会放弃等待,但已经获取的锁并不会被回滚。
INSERT操作默认行为
- 在MySQL的InnoDB引擎中,当事务B执行
INSERT
语句时,会给新插入的记录(比如id=6
)创建隐式锁。这种锁是短期的,通常在事务提交后释放。 - 重点:事务B在插入记录时,并不会主动生成其他显式锁(如共享锁或排他锁),除非后续的操作要求。
锁机制与等待现象
- 隐式锁:事务B插入
id=6
时生成的隐式锁是轻量级的,主要用于事务期间的并发控制。 - 显式锁:其他事务如果尝试显式锁定
id=6
,MySQL需要确保事务B的修改(插入)已经完成并提交,才会让其他事务获得锁。
因此,当其他事务尝试显式锁定时,会出现“等待”事务B释放隐式锁的现象。
锁类型的转换
- 当事务B插入记录后,没有立即生成显式锁。
- 当其他事务需要对
id=6
进行排他锁(X锁)时,MySQL会:- 先等待事务B的隐式锁释放(事务B提交或回滚)。
- 等到事务B的锁后, 生成事务A的排他锁(X锁)。
这个过程会显得像是“其他事务触发了锁”,但实际上是其他事务的操作使得锁冲突显现。
总结
- 事务B的隐式锁不会主动变成显式锁,除非其他事务的操作引发锁需求。
- 当其他事务尝试对
id=6
显式加锁时,它需要等待事务B释放对这条记录的隐式锁。 - 这个等待与锁的生成是MySQL的正常行为,用于保证事务隔离级别(比如RR或SERIALIZABLE)。