我的交易包含SELECT
和INSERT
。出于并发原因,我将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上添加索引不会改变行为。
答案 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,但即使在那里,我相信有些情况下它的工作方式不同。
如果您的应用程序依赖于特定事务隔离模式中非常微妙的锁定行为,则可能会破坏: