带有innodb和可序列化事务的Mysql不会(总是)锁定行

时间:2011-01-12 12:30:00

标签: mysql transactions innodb isolation-level

我的交易包含SELECTINSERT。出于并发原因,我将FOR UPDATE添加到SELECT。为了防止幻像行,我正在使用SERIALIZABLE事务隔离级别。当表中有任何行时,这一切都可以正常工作,但如果表是空的则不行。当表为空时,SELECT FOR UPDATE不执行任何(独占)锁定,并发线程/进程可以发出相同的SELECT FOR UPDATE而不会被锁定。

CREATE TABLE t (
  id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
  display_order INT
) ENGINE = InnoDB;

SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
START TRANSACTION;
SELECT COALESCE(MAX(display_order), 0) + 1 from t FOR UPDATE;

..

这个概念与SQL Server一样正常,但不适用于MySQL。关于我做错了什么想法?

修改

在display_order上添加索引不会改变行为。

3 个答案:

答案 0 :(得分:1)

有一些有趣这个,两个事务都准备好获得真正的锁定。只要其中一个事务尝试执行插入,锁就会存在。如果两个事务都尝试了,那么将获得死锁并回滚。如果只有其中一个尝试它,它将获得锁定等待超时

如果检测到锁定等待超时,则可以回滚,这将允许下一个事务执行插入。

所以我认为你很可能会很快得到死锁异常或超时异常,这应该保存情况。但谈到完美的“可序列化”情况,这实际上是空表的不良副作用。在所有情况下引擎都不是完美的,至少不能进行双事务插入..

我昨天发送了一个关于真正可串行性与引擎可串行性的有趣案例,在potsgreSQl文档中,查看这个例子它是搞笑http://www.postgresql.org/docs/8.4/static/transaction-iso.html#MVCC-SERIALIZABILITY

更新: 其他有趣的资源:Does MySQL/InnoDB implement true serializable isolation?

答案 1 :(得分:0)

你看过这个文件了吗? http://dev.mysql.com/doc/refman/5.1/en/innodb-locking-reads.html

如果你问我,mysql不是以这种方式使用的...... 我的推荐是: 如果你可以使用它 - >锁定整个桌子。

答案 2 :(得分:0)

这可能不是一个错误。

不同数据库实现特定事务隔离级别的方式不是100%一致的,并且有许多边缘情况要考虑哪些行为不同。 InnoDB旨在模仿Oracle,但即使在那里,我相信有些情况下它的工作方式不同。

如果您的应用程序依赖于特定事务隔离模式中非常微妙的锁定行为,则可能会破坏:

  • 即使它现在“有效”,如果有人更改了数据库架构,也可能没有。
  • 维护代码的工程师不太可能理解如何使用数据库,如果它取决于锁定的细微之处