MySQL / InnoDB是否实现了真正的可序列化隔离?

时间:2011-06-07 17:47:57

标签: transactions rdbms acid transaction-isolation

MySQL documentation来看,InnoDB引擎是否实现真正的可序列化隔离 1 snapshot isolation并不完全清楚,这通常会令人困惑地称为“可序列化“也是。它是哪一个?

如果MySQL InnoDB没有,那么有没有完全免费的,生产质量的RDBMS呢?

1 其中“真正的可序列化隔离”意味着不仅缺少根据SQL标准的读取异常,而且还存在写入偏斜异常,更详细地解释here

5 个答案:

答案 0 :(得分:11)

<强>更新

看到评论,这似乎是在MySQL 5.5中修复,这些例子我们仍然有一个表锁,索引的下一键锁不能被愚弄,AFAIK。

<强>原始

昨天发现了你的问题,我也想知道InnoDb的MVCC系列化模型。

所以我做了一些测试。 MySQL 5.1.37。对可序列化问题的一个很好的测试是在postgrESQL 9.0 MVCC documentation中提供的,在本章 Serializable Isolation与True Serializability 中我们可以看到MVCC模型在可序列化时的限制如果没有执行谓词锁定

让我们在MySQL上测试它:

CREATE TABLE t1 (
 class integer,
 value integer
) ENGINE=InnoDB;

INSERT INTO t1 (`class`,`value`) VALUES
  (1,10),
  (1,20),
  (2,100),
  (2,200);

现在我们将打开两个不同的连接以进行两个并行事务(T1和T2):

T1:

SET TRANSACTIOn ISOLATION LEVEL SERIALIZABLE;
BEGIN;
SELECT SUM(value) FROM t1 WHERE class = 1;

结果是30。

T2:

 SET TRANSACTIOn ISOLATION LEVEL SERIALIZABLE;
 BEGIN;
 SELECT SUM(value) FROM t1 WHERE class = 2;

结果是300。

现在出现了可串行化问题。如果T1插入一行,则表示T2中的选择无效(此处T2的作用相同)。

T1:

INSERT INTO t1 (`class`,`value`) VALUES (2,30);

==&GT;等待(锁定到位)

T2:

INSERT INTO t1 (`class`,`value`) VALUES (1,300);

==&GT; ERROR 1213(40001):尝试锁定时发现死锁;尝试重新启动交易

T1现在插入成功,t2有一个ROLLBACK,良好的可串行性

这会在PostgreSQL 9.0上失败(事情正在改变9.1,但这是另一个问题)。 实际上只有一个的交易可以在表格上执行插入。即使我们尝试使用。{/ p>插入class=3

INSERT INTO t1 (`class`,`value`) VALUES (3,30);

我们会看到等待锁定,以及遇到问题时的死锁。看起来我们在MySQL中有一个谓词锁定... 但实际上它是InnoDB中的next-key locking实现。

Innodb执行行锁定,并在索引上锁定一些间隙。这里我们没有表上的索引,看起来MySQL决定锁定表。

因此,让我们尝试测试下一键锁定,看看这是否强制执行可序列化。首先回滚正在运行的事务(T1)。然后创建一个索引。

CREATE index t1class ON t1 (class);

现在重做测试。 成功,仍然可以执行可序列化。好消息。

但是在索引到位的情况下,我认为下一键锁定和行锁是在索引上进行的。这意味着我们应该能够执行插入,如果它不影响并行交易...... 这就是一个大问题

T1:

SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
BEGIN;
SELECT SUM(value) FROM t1 WHERE class = 1;

结果是30。

T2:

 SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
 BEGIN;
 SELECT SUM(value) FROM t1 WHERE class = 2;

结果是300。

在这里,我们将在T1上创建一个不相关的插入,现在我们有一个索引,这将成功:

T1:

INSERT INTO t1 (`class`,`value`) VALUES (3,30);

两者都可以执行插入(这里我只做了一次),这是正常的。未应用预测锁定,未对class=3进行SELECT查询。如果我们给它提供良好的索引(插入时没有表锁定),看起来下一键锁定会更好。

现在我们尝试在下一个键锁上插入一个匹配选择T2的行(class = 2):

T1:

INSERT INTO t1 (`class`,`value`) VALUES (2,30);

哎哟。 成功

T2:

INSERT INTO t1 (`class`,`value`) VALUES (1,300);

==&GT;等候。那里还有锁。希望。

T1:

COMMIT;

T2:(锁定已经消失,插入已经完成)

SELECT SUM(value) FROM t1 WHERE class = 2;
COMMIT;

这里还有300。似乎可串行性已经消失。

select * from t1;
+-------+-------+
| class | value |
+-------+-------+
|     1 |    10 | 
|     1 |    20 | 
|     2 |   100 | 
|     2 |   200 | 
|     3 |    30 | <-- test
|     2 |    30 | <-- from trans1
|     1 |   300 | <-- from trans2 ERROR!
+-------+-------+

结果:通过在插入影响并行事务查询的行之前插入新的不相关行,我们欺骗了下一键锁定机制。或者至少这是我从测试中理解的。所以我会说,不相信引擎的真正可串行化。如果您在交易中拥有聚合功能,那么最好的方法是手动锁定表格,将您的可串行化问题转换为真正的一个人的情况,毫无意外!示例中的其他可串行化问题是约束验证(检查操作后数量仍为正数),您是否也对这些情况拥有锁定。

答案 1 :(得分:9)

  

有没有完全免费的,生产质量的RDBMS吗?

Postgres支持真正的可序列化隔离starting with version 9.1。它当然有资格作为“完全免费”和“生产质量”。

答案 2 :(得分:1)

您确定要使用“可序列化”交易吗?当然,你必须使用“SET session TRANSACTION ISOLATION LEVEL SERIALIZABLE;”这样整个会话就变得可序列化,而不仅仅是下一次交易。

我在OSX上用5.5.29进行测试

当我尝试在T1中插入(3,30)时,在类创建索引之后,事务在锁定等待超时后等待并中止。 (T2仍在进行中)

答案 3 :(得分:-2)

在链接you provided上阅读更多内容,它表示使用“可重复读取”模式(innodb的默认设置)可以消除读取异常,正如您提到的那样,您需要这样做。此外,阅读第二个链接,处理写入异常似乎转移到最终用户。在文章中,他们提到了Oracle的Select for Update,MySQL also supports。我不确定这是否符合您的要求,但它应该对您有所帮助。

答案 4 :(得分:-5)

我不相信MySQL实现了可序列化隔离,据我所知,它需要回滚功能,它肯定不支持。有关详细信息,请阅读here