MySQL数据库性能优化(2) (6)

其中user_number带有一个索引(后续我们将讨论这个索引类型对间隙锁策略的影响),这样的检索条件很显然会涉及到一个范围的数据都将被更新(例如user_number==10、13、15、17、19、21……),于此同时有另一个事务B正在执行以下语句:

...... # 事务B正在执行一个插入操作 insert into myuser(.........,\'user_number\') values (.........,11); # 插入一个卡号为11的新会员,然后提交事务B ......

如果InnoDB只锁住user_number值为10的非聚簇索引和相应的聚簇索引,显然就会造成一个问题:在A事务处理过程中,突然多出了一条满足更新条件的记录。事务A会很纠结的,很尴尬的。如果读者是InnoDB引擎的开发者,您会怎么做呢?正确的做法是为满足事务A所执行检索条件的整个范围加锁,这个锁不是加在某个或某几个具体的记录上,因为那样做还是无法限制类似插入“一个卡号为11的新纪录”这样的情况,而是加在到具体索引和下一个索引之间,告诉这个索引B+树的其它使用者,包括这个索引在内的之后区域都不允许使用。这样的锁机制称为间隙锁(GAP锁)。

间隙锁和行级锁组合起来称为Next-Key Lock,实际上这两种锁一般情况下都是组合工作的。

表级锁:没有可以检索的索引,就无法使用InnoDB特定的锁。另外,索引失效InnoDB也会为整个数据表加锁。如果表级锁的性质是排它锁(实际上大多数情况是这样的锁),那么所有试图为这张数据表中任何资源加共享锁或者排它锁的事务都必须等待在数据表上的排它锁被解除后,才能继续工作。表级锁可以看作基于InnoDB引擎工作的数据表的最悲观锁,它是InnoDB引擎为了保持事务特性的一场豪赌。例如我们有如下的数据表结构:

uid(PK) varchar 
user_name varchar 
user_sex int

这张数据表中只有一个由uid字段构成的主索引。接着两个事务同时执行以下语句:

begin; select * from t_user where uid = 2 lock in share mode; #都先不执行commit,以便观察现象 #commit;

这里的select查询虽然使用的检索依据是uid,但是设置检索条件时uid的varchar类型却被错误的使用成了int类型。那么数据表将不再使用索引进行检索,转而进行全表扫秒。这是一种典型的索引失效情况,最终读者观察到的现象是,在执行以上同一查询语句的两个事务中,有一个返回了查询结果,但是另外一个一直为等待状态。以上的小例子也可以让读者看到,科学管理索引在InnoDB引擎中是何等重要。本文后续部分将向读者介绍表级锁的实质结构。

意向共享锁(IS锁)和意向排它锁(IX锁)

为了在某一个具体索引上加共享锁,事务需要首先为涉及到的数据表加意向共享锁(IS锁);为了在某一个具体所以上加排它锁,事务需要首先为涉及到的数据表加意向排它锁(IX锁)。这样InnoDB可以整体把握在并发的若干个事务中,让哪些事务优先执行更能产生好的执行效果。意向共享锁是InnoDB引擎自动控制的,开发人员无法人工干预,也不需要干预。

4-3-2、加锁过程实例

InnoDB引擎中的锁机制基于索引才能工作。对数据进行锁定时并不是真的锁定数据本身,而是对数据涉及的聚集索引和非聚集索引进行锁定。在之前的文章中我们已经介绍到,InnoDB引擎中的索引按照B+树的结构进行组织,那么加锁的过程很明显就是在对应的B+树上进行加锁位置检索和进行标记的过程。并且InnoDB引擎中的非聚簇索引最终都要依靠聚簇索引才能找到具体的数据记录位置,所以加锁的过程都涉及到对聚簇索引进行操作。

SELECT关键字的查询操作一般情况下都不会涉及到锁的问题(这种类型的读操作称为快照读),但并不是所有的查询操作都不涉及到锁机制。只要SELECT属于某种写操作的前置子查询/检索或者开发人员显式为SELECT加锁,这些SELECT语句就涉及到锁机制——这种读操作称为当前读。而执行Update、Delete、Insert操作时,InnoDB会根据会根据操作中where检索条件所涉及的一条或者多条数据加排它锁。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zgffsf.html