--查看执行计划,全表扫描(type=ALL) EXPLAIN SELECT*FROM child WHERE name ='D'FORUPDATE; +----+-------------+-------+------+---------------+------+---------+------+------+-------------+ | id | select_type |table| type | possible_keys | key | key_len |ref|rows| Extra | +----+-------------+-------+------+---------------+------+---------+------+------+-------------+ |1| SIMPLE | child |ALL|NULL|NULL|NULL|NULL|5|Usingwhere| +----+-------------+-------+------+---------------+------+---------+------+------+-------------+
--执行查询,加表锁 SELECT*FROM child WHERE name ='D'FORUPDATE; +----+-----------+------+ | id | parent_id | name | +----+-----------+------+ |4|3| D | +----+-----------+------+
--开启事务 T2 START TRANSACTION;
--查看执行计划,命中索引 idx_parent_id EXPLAIN SELECT*FROM child WHERE parent_id =4FORUPDATE; +----+-------------+-------+------+---------------+---------------+---------+-------+------+-------+ | id | select_type |table| type | possible_keys | key | key_len |ref|rows| Extra | +----+-------------+-------+------+---------------+---------------+---------+-------+------+-------+ |1| SIMPLE | child |ref| idx_parent_id | idx_parent_id |8| const |1|NULL| +----+-------------+-------+------+---------------+---------------+---------+-------+------+-------+
--查看执行计划,命中索引 idx_parent_id EXPLAIN SELECT*FROM child WHERE parent_id =2AND name ='A'FORUPDATE; +----+-------------+-------+------+---------------+---------------+---------+-------+------+-------------+ | id | select_type |table| type | possible_keys | key | key_len |ref|rows| Extra | +----+-------------+-------+------+---------------+---------------+---------+-------+------+-------------+ |1| SIMPLE | child |ref| idx_parent_id | idx_parent_id |8| const |2|Usingwhere| +----+-------------+-------+------+---------------+---------------+---------+-------+------+-------------+
--执行查询 SELECT*FROM child WHERE parent_id =2AND name ='A'FORUPDATE; +----+-----------+------+ | id | parent_id | name | +----+-----------+------+ |1|2| A | +----+-----------+------+
--开启事务 T2 START TRANSACTION;
--查看执行计划,命中索引 idx_parent_id EXPLAIN SELECT*FROM child WHERE parent_id =2AND name ='C'FORUPDATE; +----+-------------+-------+------+---------------+---------------+---------+-------+------+-------------+ | id | select_type |table| type | possible_keys | key | key_len |ref|rows| Extra | +----+-------------+-------+------+---------------+---------------+---------+-------+------+-------------+ |1| SIMPLE | child |ref| idx_parent_id | idx_parent_id |8| const |2|Usingwhere| +----+-------------+-------+------+---------------+---------------+---------+-------+------+-------------+
-- 执行查询,虽然 T1、T2 访问不同行的记录,但由于使用了相同的索引键 parent_id = 2,出现锁冲突,从而阻塞,一直等待 SELECT*FROM child WHERE parent_id =2AND name ='C'FORUPDATE;
4、即便在条件中使用了索引字段,但是否使用索引来检索数据是由 MySQL 通过判断不同执行计划的代价来决定的,如果 MySQL 认为全表扫描效率更高,比如对一些很小的表,它就不会使用索引,这种情况下 InnoDB 将使用表锁,而不是行锁。因此,在分析锁冲突时,别忘了检查 SQL 的执行计划,以确认是否真正使用了索引:
--查看执行计划,虽然使用了索引 idx_parent_id,但 MySQL 认为全表扫描效率更高,因此实际上没有使用索引 EXPLAIN SELECT*FROM child WHERE parent_id =2FORUPDATE; +----+-------------+-------+------+---------------+------+---------+------+------+-------------+ | id | select_type |table| type | possible_keys | key | key_len |ref|rows| Extra | +----+-------------+-------+------+---------------+------+---------+------+------+-------------+ |1| SIMPLE | child |ALL| idx_parent_id |NULL|NULL|NULL|5|Usingwhere| +----+-------------+-------+------+---------------+------+---------+------+------+-------------+
--虽然使用了索引 idx_parent_id,但由于进行了全表扫描,因此实际使用表锁 SELECT*FROM child WHERE parent_id =2FORUPDATE; +----+-----------+------+ | id | parent_id | name | +----+-----------+------+ |1|2| A | |2|2| C | |3|2| C | +----+-----------+------+