【问题标题】:Mysql with innodb and serializable transaction does not (always) lock rows具有 innodb 和可序列化事务的 Mysql 不会(总是)锁定行
【发布时间】:2011-01-12 12:30:00
【问题描述】:

我与SELECT 和可能的INSERT 进行了交易。出于并发原因,我在SELECT 中添加了FOR UPDATE。为了防止出现幻行,我使用了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 上添加索引不会改变行为。

【问题讨论】:

    标签: mysql transactions innodb isolation-level


    【解决方案1】:

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

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

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

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

    更新: 其他有趣的资源:@​​987654322@

    【讨论】:

    • 我也注意到了可能的死锁/超时。处理死锁/超时并重试事务是可能的,但我更喜欢没有死锁的干净解决方案。
    • 如果您处理可序列化事务,您必须已经在事务处理中处理异常(带有重试循环)。 deadlock abort 只是另一个要处理的异常,它与应用程序中的算法相同。可序列化事务很可能会出现“不可序列化异常”,我会以同样的方式考虑死锁检测到的异常。
    • 我们当然会处理这些异常(并且当我们看到死锁/超时异常时也会重试事务),但我想知道为什么不使用(独占)锁(这应该防止任何可能的死锁)。
    • 我怀疑 EXCLUSIVE SHARE 不是存储在表上,而是存储在表的每一行上......但是当新行获得这个份额时,肯定有一些东西存储在表级别......你应该也许填写一份关于 MySQL 的错误报告,你会从真正的大师那里得到信息。
    • 在 SERIALIZABLE 模式下,您可能不需要使用 select 进行更新。
    【解决方案2】:

    这可能不是错误。

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

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

    • 即使它现在“有效”,但如果有人更改数据库架构,它也可能不会
    • 如果代码依赖于锁定的微妙之处,维护您的代码的工程师不太可能了解它是如何使用数据库的

    【讨论】:

    • 我同意这不一定是错误,我只是想知道为什么它不能按照我希望的方式工作。我在 Mysql 文档中找不到有关此问题的任何信息。
    【解决方案3】:

    您是否看过这份文件: http://dev.mysql.com/doc/refman/5.1/en/innodb-locking-reads.html

    如果你问我,mysql 不是为那样使用而构建的...... 我的建议是: 如果你负担得起 -> 锁定整张桌子。

    【讨论】:

    • 最好看5.1文档,隔离级别管理现在大不一样了
    • 据我所知,Mysql 文档明确建议在这些情况下使用 FOR UPDATE。只是空表的语义似乎有所不同。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多