MySQL InnoDB 未使用索引导致行锁转表锁

MySQL InnoDB是MySQL中使用最广泛的一种存储引擎,支持事务,支持表级锁,另外还实现了行级锁,支持更好的并发写操作。InnoDB行级锁是通过给索引上的索引项加锁来实现的,这就导致只有通过索引条件检索数据,InnoDB才会使用行级锁,否则InnoDB将使用表级锁。下面通过一个例子来验证在未使用索引的情况下,行锁转成了表锁,案例使用的版本为MySQL 5.7.19。

1、表结构:

表tb中只有两个字段,id和name,两个字段上都没有索引,表中也只有5条数据,如下:
# 表结构
CREATE TABLE `tb` (
  `id` int(11) DEFAULT NULL,
  `name` varchar(50) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

# 表中数据
mysql> select * from tb;
+------+------+
| id   | name |
+------+------+
|    1 | 1    |
|    2 | 2    |
|    3 | 3    |
|    4 | 4    |
|    5 | 5    |
+------+------+
5 rows in set (0.00 sec)

2、未使用索引导致行锁转成表锁

启动两个连接,分别为session1和session2,session1更新id为1的记录,session2更新id为2的记录,两个session更新的数据没有交集。执行情况如下:
session1
session2
备注
begin;
begin;

select * from tb where id=1 for update;



select * from tb where id=2 for update;
session2阻塞
commit;



commit;

发现session2被session1阻塞,只有当session1提交或者回滚之后,session2才能返回,session1与session2更新的数据并没有交集,显然行锁升级成了表锁。

3、加上索引,再次执行

给表tb加上索引,再执行一次,看看session2还会不会阻塞。

alter table tb add index idx_id(id);
session1
session2
备注
begin;
begin;

select * from tb where id=1 for update;



select * from tb where id=2 for update;
session2执行正常,不再阻塞。
commit;



commit;

session2执行不再被阻塞,显然在id字段加了索引之后,由于session1和session2更新的数据没有交集,行级锁能够并行更新数据,session2不再阻塞。

MySQL InnoDB 的这一特性在实际开发中应当特别注意,避免由于未使用索引,行级锁升级为表级锁,导致并发度降低,SQL执行阻塞,影响正常业务。

文章评论

0条评论