2012-02-19 127 views
7

我正在阅读有关innodb交易的手册,但仍然有很多不清楚的东西给我。举例来说,我不太明白以下行为:inno db隔离级别和锁定

-- client 1        -- client 2 
mysql> create table simple (col int) 
     engine=innodb; 

mysql> insert into simple values(1); 
Query OK, 1 row affected (0.00 sec) 

mysql> insert into simple values(2); 
Query OK, 1 row affected (0.00 sec) 

mysql> select @@tx_isolation;                
+-----------------+                   
| @@tx_isolation | 
+-----------------+ 
| REPEATABLE-READ |                   
+-----------------+ 

mysql> begin;          
Query OK, 0 rows affected (0.01 sec)    
             mysql> begin; 
             Query OK, 0 rows affected (0.00 sec) 

mysql> update simple set col=10 where col=1; 
Query OK, 1 row affected (0.00 sec) 
Rows matched: 1 Changed: 1 Warnings: 0 

             mysql> update simple set col=42 where col=2; 
             -- blocks 

现在,最后更新命令(在客户端2)等待。我期望命令执行,因为我会假设只有行1被锁定。即使客户端2中的第二个命令是insert,行为也是一样的。任何人都可以描述这个例子背后的锁定背景(在哪里和为什么锁)?

+1

阅读关于**'REPEATABLE READ' **:[Transaction levels](http://dev.mysql.com/doc/refman/5.5/en/set-transaction.html):'For locking reads( SELECT FOR FOR UPDATE或LOCK IN SHARE MODE),UPDATE和DELETE语句,锁定**取决于语句是使用具有唯一搜索条件**的唯一索引,还是范围类型搜索条件。 ...' – 2012-02-19 22:49:51

回答

9

InnoDB按如下方式设置特定类型的锁。

  • SELECT ... FROM是一个持续读,读取数据库的快照,除非事务隔离级别设置为序列的设置没有锁。对于SERIALIZABLE级别,搜索会在遇到的索引记录上设置共享的下一个键锁。

  • SELECT ... FROM ...锁定共享模式在搜索遇到的所有索引记录上设置共享的下一个键锁定。

  • 对于搜索遇到的索引记录,SELECT ... FROM ... FOR UPDATE阻止其他会话执行SELECT ... FROM ... LOCK IN SHARE MODE或读取某些事务隔离级别。一致的读取操作将忽略读取视图中存在的记录上设置的任何锁定。

  • UPDATE ... WHERE ...在搜索遇到的每个记录上设置独占的下一个键锁定。

  • DELETE FROM ... WHERE ...在搜索遇到的每条记录上设置独占的下一个键锁定。

  • INSERT在插入的行上设置排它锁。该锁是一个索引记录锁,而不是下一个键锁(即没有间隙锁),并且不会阻止其他会话插入到插入行之前的间隙中。

InnoDB的有几种类型的记录级锁:

  • 记录锁:这是一个索引记录锁。

  • 间隙锁定:这是锁定索引记录之间的间隙,或锁定第一个或最后一个索引记录之后的间隙。

  • 下一个键锁定:这是索引记录上的记录锁定和索引记录之前的间隙上的间隙锁定的组合。

查看更多:

Avoiding the Phantom Problem Using Next-Key Locking

Avoiding deadlock

+0

良好的概述,但我想知道如何索引适合你写的东西。为什么索引的存在会改变我的示例中的行为? – clime 2012-02-24 11:40:09

+2

下一个键锁定结合了索引行锁定和间隙锁定。 InnoDB以这样的方式执行行级锁定,即当它搜索或扫描表索引时,它会在遇到的索引记录上设置共享锁或排它锁。另外,索引记录上的下一个键锁也会影响该索引记录之前的“间隙”。也就是说,下一个键锁定是索引记录锁定,并在索引记录之前的间隔上加上间隙锁定。 – 2012-02-24 12:30:24

0

ypercube有它的权利。具体而言,如果没有在条件中使用的唯一索引,它将锁定多于受影响的单个行。

要查看您所期望的行为,你的表创建改成这样:

create table simple (col int unique) ENGINE=InnoDB; 

col领域唯一索引将允许它仅锁定受影响的行。

+0

正如我发现的那样,需要有一个索引,但它不需要是唯一的。 – clime 2012-02-24 11:29:02

+0

啊,当我读到它'使用唯一的索引和独特的搜索条件'时,我将它作为'唯一'索引,但它们必须具有唯一性,就像单一的特定索引一样。 – kbenson 2012-02-24 19:34:48

+0

ypercube写的不是(完全)相关的。如果有唯一的索引和唯一的搜索条件,则使用记录锁而不是下一个键锁。在我的示例中,使用记录锁定或下一个键锁定并不重要,因为只有第一次更新可能锁定的间隔是1之前,第二次更新引用了2。重要的是存在(y )索引,因为update不会仅锁定目标记录,而是锁定在搜索目标记录_期间遇到的所有记录。没有索引=>全面扫描=>所有记录被锁定。 – clime 2012-02-24 19:58:03

0

“对于索引记录搜索遇到,SELECT ... FROM ... FOR UPDATE阻止执行SELECT其他会话.. 。FROM ...锁定在共享模式下或从某些事务隔离级别读取。一致的读取将忽略在读取视图中存在的记录上设置的任何锁定“

这些特定的锁可以通过select来更新,以便其他会话无法读取锁定的记录?

+0

对不起,这不是一个答案。考虑发布一个单独的问题。 – Beryllium 2014-06-16 06:52:38

+0

stalkoverflow人们不允许我发布它作为一个新问题 – ravi 2014-06-16 12:52:57

+0

在mysql中没有这样的锁定,请参阅[本答案](http://stackoverflow.com/a/21792907/3576887)了解变通办法 – elipoultorak 2015-12-29 08:22:15