2

我与 aSELECT和 possible进行了交易INSERT。出于并发原因,我添加FOR UPDATESELECT. 为了防止出现幻行,我使用了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 上添加索引不会改变行为。

4

3 回答 3

1

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

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

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

我昨天在 potsgreSQl 文档上发送了一个关于真正可序列化与引擎可序列化的有趣案例,查看这个例子很有趣http ://www.postgresql.org/docs/8.4/static/transaction-iso.html#MVCC-SERIALIZABILITY

更新:其他有趣的资源:MySQL/InnoDB 是否实现了真正的可序列化隔离?

于 2011-01-12T13:04:20.513 回答
0

Did you have a look at this document: http://dev.mysql.com/doc/refman/5.1/en/innodb-locking-reads.html

If you ask me, mysql wasn't built to be used in that way... My recomendation is: If you can affort it -> Lock the whole table.

于 2011-01-12T12:48:35.423 回答
0

这可能不是错误。

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

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

  • 即使它现在“有效”,如果有人更改数据库模式,它也可能不会
  • 如果代码依赖于锁定的微妙之处,那么维护您的代码的工程师不太可能理解它是如何使用数据库的
于 2011-01-12T12:59:54.843 回答