锁定读(locking read)、更新(UPDATE)或删除(DELETE)一般会在SQL语句处理过程当中扫描的每一个索引记录上设置记录锁。语句中是否存在排除行的WHERE条件并不重要。InnoDB不记得确切的WHERE条件,而只知道哪一个索引范围被扫描。这些锁一般是next-key锁,它还会阻止在记录以前插入“间隙”。然而,间隙锁(gap lock)能够被显式禁用,这会致使不使用next-key锁。数据库
若是在检索中使用了二级索引,而且要设置的索引记录锁是排它的,则InnoDB也会检索相应的汇集索引记录并在它们上设置锁。性能
执行SQL语句时,若是没有找到可用的索引,MySQL必须扫描整个表来处理该语句,这样的话表的每一行都会被锁定,从而阻塞其余用户对表的全部插入。所以,建立良好的索引很是重要,这样能够避免扫描许多没必要要的行。优化
InnoDB设置特定类型的锁,以下所示:spa
SELECT ... FROM 是一致读,读取数据库快照,除非将事务隔离级别设置为SERIALIZABLE,不然不设置锁。对于SERIALIZABLE级别,检索会在遇到的索引记录上设置共享的next-key锁。可是,对于使用惟一索引来搜索惟一行的语句,只须要一个索引记录锁。code
对于 SELECT ... FOR UPDATE 或者 SELECT ... LOCK IN SHARE MODE ,对扫描的行加锁,并对不符合结果集中包含条件的行(例如,若是它们不知足WHERE子句中给出的条件)释放锁。orm
SELECT ... LOCK IN SHARE MODE 在全部遇到的索引记录上设置共享的next-key锁。可是,对于使用惟一索引来搜索惟一行的语句,只须要一个索引记录锁。blog
SELECT ... FOR UPDATE 在搜索遇到的每一个记录上设置排它的next-key锁。可是,对于使用惟一索引来搜索惟一行的语句,仅须要索引记录锁定。索引
UPDATE ... WHERE ... 在搜索遇到的每一个记录上设置排它的next-key锁。可是,对于使用惟一索引来搜索惟一行的语句,仅须要索引记录锁定。事务
DELETE FROM ... WHERE ... 在搜索遇到的每一个记录上设置排它的next-key锁。可是,对于使用惟一索引来搜索惟一行的语句,仅须要索引记录锁定。rem
INSERT 在插入的行上设置排他锁。该锁是索引记录锁,不是next-key锁(即没有间隙锁),而且不会阻止其余会话插入到插入行以前的间隙中。
举个例子,假设有一张表t1,结构以下:
CREATE TABLE t1 (i INT, PRIMARY KEY (i)) ENGINE = InnoDB;
再假设,有三个会话操做顺序以下:
Session 1:
START TRANSACTION; INSERT INTO t1 VALUES(1);
Session 2:
START TRANSACTION; INSERT INTO t1 VALUES(1);
Session 3:
START TRANSACTION; INSERT INTO t1 VALUES(1);
Session 1:
ROLLBACK;
首先Session1得到i=1这一行的排它锁,接下来Session2和Session3因为主键重复只能请求获取该行的共享锁,因为行上已经有排它锁,所以Session2和Session3请求的共享锁不能被当即授予。再接着,Session1回滚,行上的排它锁被释放,因而Session2和Session3在该行上都持有共享锁,此时,死锁发生了,因为对方持有的共享锁,任何一方都不能得到该行的排它锁。
下面这组操做也是相似:
Session 1:
START TRANSACTION; DELETE FROM t1 WHERE i = 1;
Session 2:
START TRANSACTION; INSERT INTO t1 VALUES(1);
Session 3:
START TRANSACTION; INSERT INTO t1 VALUES(1);
Session 1:
COMMIT;
和前面的状况相似,Session1提交之后,Session2和Session3请求该行上的共享锁被当即授予,此时它们再请求获取排它锁时就出现死锁了,由于共享锁被另外一个事务持有。
补充: 汇集索引与辅助索引
clustered index (译:汇集索引、聚簇索引)
secondary index (译:二级索引、辅助索引)
每一个InnoDB表都有一个特殊的索引,称为汇集索引,用于存储行数据。一般,汇集索引与主键是同义词。为了从查询、插入和其余数据库操做中得到最佳性能,必须了解InnoDB如何使用汇集索引来优化每一个表的最多见的查找和DML操做。
Typically, the clustered index is synonymous with the primary key.
一般,“clustered index” 和 “primary key” 是一个意思。
当你在表上定义一个PRIMARY KEY时,InnoDB将它用做汇集索引。为建立的每一个表定义一个主键。若是没有逻辑惟一的非空列或列集,请添加一个新的自动递增(auto-increment)列,其值将自动填充。
若是你没有为你的表定义一个PRIMARY KEY,则MySQL会在全部键列都不为NULL的状况下找到第一个惟一索引,而且InnoDB使用它做为汇集索引。
若是表没有主键或合适的惟一索引,InnoDB会在包含行ID值的合成列上内部生成一个名为GEN_CLUST_INDEX的隐藏汇集索引
经过汇集索引访问行很是快,由于索引搜索直接指向包含全部行数据的页。若是表很大,汇集索引体系结构一般节省磁盘I/O操做。
除了汇集索引以外的全部索引都称为二级索引。在InnoDB中,二级索引中的每条记录都包含该行的主键列,以及为二级索引指定的列。InnoDB使用此主键值在汇集索引中搜索行。
若是主键很长,则辅助索引将使用更多空间,所以具备主键较短是比较有利的。
With the exception of spatial indexes, InnoDB indexes are B-tree data structures. Index records are stored in the leaf pages of their B-tree or R-tree data structure. The default size of an index page is 16KB. Supported sizes are 64KB, 32KB, 16KB (default), 8KB, and 4KB.