在 InnoDB 中,为什么不在索引范围内的行会被锁定?
In InnoDB, Why rows which are not in index range are getting locked?
考虑以下查询..
select * from Employee where ID>0 and ID <5.
这里ID是主键,Employee是Innodbtable,事务隔离读提交,table范围内没有行ID>0 ID<5。
我认为上面的查询只会锁定 0 到 5 之间的 id 范围,但 id 6(因为 id 6 数据在 table 中)也被锁定,这阻止了其他事务更新。
在执行 explain 时,行数显示为 1。
explain select * from Employee where (ID>0 and ID<5);
+----+-------------+----------+-------+---------------+---------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+----------+-------+---------------+---------+---------+------+------+-------------+
| 1 | SIMPLE | Employee | range | PRIMARY | PRIMARY | 8 | NULL | 1 | Using where |
+----+-------------+----------+-------+---------------+---------+---------+------+------+-------------+
但是系统中没有实际的行。
select * from Employee where (ID>0 and ID<5);
Empty set (0.00 sec)
显示引擎 innodb 状态输出:
---TRANSACTION B3D2D, ACTIVE 2109 sec
2 lock struct(s), heap size 376, 1 row lock(s)
MySQL thread id 894932, OS thread handle 0x7f53263e0700, query id 4140397 localhost root
INNODB_LOCKS table 输出:
+----------------+-------------+-----------+-----------+----------------------+------------+------------+-----------+----------+-----------+
| lock_id | lock_trx_id | lock_mode | lock_type | lock_table | lock_index | lock_space | lock_page | lock_rec | lock_data |
+----------------+-------------+-----------+-----------+----------------------+------------+------------+-----------+----------+-----------+
| B42F4:1822:3:7 | B42F4 | X | RECORD | `jbossdb`.`Employee` | `PRIMARY` | 1822 | 3 | 7 | 6 |
| B42ED:1822:3:7 | B42ED | X | RECORD | `jbossdb`.`Employee` | `PRIMARY` | 1822 | 3 | 7 | 6 |
+----------------+-------------+-----------+-----------+----------------------+------------+------------+-----------+----------+-----------+
INNODB_LOCK_WAITS 输出:
+-------------------+-------------------+-----------------+------------------+
| requesting_trx_id | requested_lock_id | blocking_trx_id | blocking_lock_id |
+-------------------+-------------------+-----------------+------------------+
| B42F4 | B42F4:1822:3:7 | B42ED | B42ED:1822:3:7 |
+-------------------+-------------------+-----------------+------------------+
PS : 我正在使用 mysql 版本 5.5
有猜想吗?
从 MySQL 8.0.1 开始,您实际上可以看到 innodb 中获取的所有数据锁,它们由性能模式检测:
SELECT * from performance_schema.data_locks;
见https://dev.mysql.com/doc/refman/8.0/en/data-locks-table.html
可能相关,来自 8.0.1 发行说明:
InnoDB:在使用 READ COMMITTED 隔离级别时,在外键验证期间不必要地采用了间隙锁。 (漏洞 #25082593)
因此,即使 MySQL 不应该使用 GAP 锁,仍然值得使用性能模式检查实际使用了哪些锁。
考虑以下查询..
select * from Employee where ID>0 and ID <5.
这里ID是主键,Employee是Innodbtable,事务隔离读提交,table范围内没有行ID>0 ID<5。
我认为上面的查询只会锁定 0 到 5 之间的 id 范围,但 id 6(因为 id 6 数据在 table 中)也被锁定,这阻止了其他事务更新。
在执行 explain 时,行数显示为 1。
explain select * from Employee where (ID>0 and ID<5);
+----+-------------+----------+-------+---------------+---------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+----------+-------+---------------+---------+---------+------+------+-------------+
| 1 | SIMPLE | Employee | range | PRIMARY | PRIMARY | 8 | NULL | 1 | Using where |
+----+-------------+----------+-------+---------------+---------+---------+------+------+-------------+
但是系统中没有实际的行。
select * from Employee where (ID>0 and ID<5);
Empty set (0.00 sec)
显示引擎 innodb 状态输出:
---TRANSACTION B3D2D, ACTIVE 2109 sec
2 lock struct(s), heap size 376, 1 row lock(s)
MySQL thread id 894932, OS thread handle 0x7f53263e0700, query id 4140397 localhost root
INNODB_LOCKS table 输出:
+----------------+-------------+-----------+-----------+----------------------+------------+------------+-----------+----------+-----------+
| lock_id | lock_trx_id | lock_mode | lock_type | lock_table | lock_index | lock_space | lock_page | lock_rec | lock_data |
+----------------+-------------+-----------+-----------+----------------------+------------+------------+-----------+----------+-----------+
| B42F4:1822:3:7 | B42F4 | X | RECORD | `jbossdb`.`Employee` | `PRIMARY` | 1822 | 3 | 7 | 6 |
| B42ED:1822:3:7 | B42ED | X | RECORD | `jbossdb`.`Employee` | `PRIMARY` | 1822 | 3 | 7 | 6 |
+----------------+-------------+-----------+-----------+----------------------+------------+------------+-----------+----------+-----------+
INNODB_LOCK_WAITS 输出:
+-------------------+-------------------+-----------------+------------------+
| requesting_trx_id | requested_lock_id | blocking_trx_id | blocking_lock_id |
+-------------------+-------------------+-----------------+------------------+
| B42F4 | B42F4:1822:3:7 | B42ED | B42ED:1822:3:7 |
+-------------------+-------------------+-----------------+------------------+
PS : 我正在使用 mysql 版本 5.5
有猜想吗?
从 MySQL 8.0.1 开始,您实际上可以看到 innodb 中获取的所有数据锁,它们由性能模式检测:
SELECT * from performance_schema.data_locks;
见https://dev.mysql.com/doc/refman/8.0/en/data-locks-table.html
可能相关,来自 8.0.1 发行说明:
InnoDB:在使用 READ COMMITTED 隔离级别时,在外键验证期间不必要地采用了间隙锁。 (漏洞 #25082593)
因此,即使 MySQL 不应该使用 GAP 锁,仍然值得使用性能模式检查实际使用了哪些锁。