前言
众所周知,mysql5后采用InnoDB引擎,而InnoDB引擎默认是行锁,这里不做过多赘述,今天主要讲下索引失效引起的行锁变表锁。
测试表结构
create table test_lock(a int(11),b varchar(16))engine=innodb;insert into test_lock values(1,'b2');insert into test_lock values(3,'3');insert into test_lock values(4,'4000');insert into test_lock values(5,'5000');insert into test_lock values(6,'6000');insert into test_lock values(7,'7000');insert into test_lock values(8,'8000');insert into test_lock values(9,'9000');create index idx_a on test_lock(a);create index idx_b on test_lock(b);
表数据

必备常识
当字段为varchar类型查询时不加引号('')会导致索引失效,如下:

如上图可以看出加了引号用到索引idx_b了,而不加引号就没有用到索引,在更新数据时,会导致InnoDB行锁变表锁。
行锁变表锁
为了演示容易观看,我开了两个客户端,并且设置不自动提交事务。

我们知道InnoDB更新时的行锁,在更新不同的行时是不干扰的。请看如下例子:

左边执行完,在执行右边,更新不同行数据,没有发生阻塞。确定是行锁。

提交后查询,两个客户端都能查询到更新的数据。
当我去掉引号做同样的更新更新时,如下:
此时右边发生阻塞,说明行锁变表锁。

当我左边提交事务后,右边阻塞消失,执行成功。

此时两边同时更新成功。
后面编写sql时一定要注意加引号,否则不仅会导致索引失效还会导致发生阻塞等一系列问题。
补充:间隙锁
如上面所说,不同行的更新是不会干扰的。这里也有个特例。

左边更新数据并没有a=2的行,但是右边发生了阻塞。

左边提交事务后,右边插入成功。

两边都提交事务后,查询更新成功。
什么是间隙锁
当我们用范围条件而不是相等条件检索数据,并请求共享或排他锁时,InnoDB会给符合条件的已有数据记录的索引项加索,对于键值在条件范围内并不存在的记录,叫做“间隙(GAP)”,InnoDB也会对这个“间隙”加索,这种锁机制就是所谓的间隙锁(Next-Key锁)。
危害
因为Query执行过程中通过范围查找的话,他会锁定整个范围内所有的索引键值,即使这个键值并不存在。
间隙锁有一个比较致命的弱点,就是当锁定一个范围键值之后,即使某些不存在的键值也会无辜的锁定,而造成在锁定的时候无法插入锁定键值范围内的任何数据,在某些场景下这可能会对性能造成很大的危害。




