【问题标题】:Is 'update tokens set tokens = tokens + 1' atomic in InnoDB?InnoDB 中的“更新令牌集令牌 = 令牌 + 1”是原子的吗?
【发布时间】:2011-12-06 21:13:12
【问题描述】:

不使用显式事务,是:

update tokens set tokens = tokens + 1

保证在 InnoDB 中是原子的?

【问题讨论】:

    标签: mysql thread-safety innodb atomic


    【解决方案1】:

    我的理解是,在自动提交模式下(即“没有显式事务”),单个语句就是单个事务。由于事务按照定义是原子的,因此您的单个语句也是原子的。

    但是,在评估约束时,这不是在语句(即事务)级别上进行的,而是在处理语句时逐行进行。

    如果您在 tokens 列上有一个唯一约束(索引),那么该更新很可能会因此而失败。对于引用同一个表的外键也是如此。

    【讨论】:

    • dev.mysql.com/doc/refman/5.0/en/innodb-locking-reads.html 有一个示例,它在发出更新语句之前执行“选择更新”。这是否意味着这种形式的更新不是原子的?我正在使用默认隔离级别。谢谢
    • @Limbeh:在示例中,UPDATE 是原子的(child_codes 中有一行),SELECT ... FOR UPDATE(采用排他锁/写锁)是为了序列化相对于其他会话的 SELECT/UPDATE 组合(即:使整个 SELECT/UPDATE 原子和一致)。
    • 谢谢。该示例还说选择更新需要关闭自动提交或启动事务。问题:如果我已经使用启动事务,我什至需要为更新选择而烦恼吗?我认为事务已经是原子的了。
    • 对不起,还有一个问题,如果我将其限制为单行,这是否也是原子的:“更新表 a=a+b+1,b=0”
    • 是的,这也是原子的
    【解决方案2】:

    我认为不是。没有我的 MySQL 隔离级别/备忘单,但我认为它在每行级别上是原子的(UPDATE 将使用范围锁 IIRC),但不是在每个表级别上。

    现在,比 UPDATE 是否是原子的更有趣的问题是,UPDATE 的非原子性何时可观察。答案是READ UNCOMMITTED隔离级别的单个语句可以观察到UPDATE的非原子性,而READ COMMITTED隔离级别的单个事务中的一系列相关语句可以观察到不同的更新。 REPEATABLE READ 和 SERIALIZABLE 看到 UPDATE 就好像它们是原子的和一致的。

    想象两个会话。会话 A 具有 SERIALIZABLE 隔离级别,并且在具有 1000 行的表中执行:UPDATE tokens SET tokens = tokens + 1

    • 假设 Session B 具有隔离级别 READ UNCOMMITTED 并且执行 SELECT sum(tokens) FROM tokens → 此选择可以看到部分更新(即:某些行已更新而其他行未更新)。
    • 假设会话 B 具有隔离级别 READ COMMITTED 并执行 SELECT sum(tokens) FROM tokens → 此选择无法看到部分更新,因此它看到 UPDATE 就好像它是原子的。
    • 假设会话 B 具有隔离级别 READ COMMITTED 并执行SELECT sum(tokens) FROM tokens WHERE id BETWEEN 1 AND 100; SELECT sum(tokens) FROM tokens WHERE id BETWEEN 501 AND 600,然后一些程序逻辑将这两个值相加 → 这些 SELECT 可以看到不同的 MVCC 快照 → 不同的更新,
    • 假设会话 B 具有隔离级别 REPEATABLE READ 并执行 SELECT sum(tokens) FROM tokens WHERE id BETWEEN 1 AND 100; SELECT sum(tokens) FROM tokens WHERE id BETWEEN 501 AND 600,然后某些程序逻辑将这两个值相加 → 这些 SELECT 无法看到不同的 MVCC 快照 → 他们看到相同的更新,

    【讨论】:

    • 无论如何,由 UPDATE 完成的锁定记录在 MySQL 文档中。
    • 如果它不是原子的,那么 InnoDB 的“事务”根本就不是事务。单个语句应该(实际上:必须)以原子方式工作 - 并且它必须在运行时看到表的一致状态。
    • @a_horse_with_no_name:这是否意味着 InnoDB 必须在整个表上设置 IX(意图排他)锁,然后为每个受影响的表设置表锁或索引/间隙/下一个键锁行,最后甚至在 dev.mysql.com/doc/refman/5.0/en/set-transaction.html中 READ COMMITED 的解释
    • 说,您有两个 READ UNCOMMITED 事务,一个执行 UPDATE,另一个执行 SELECT 以计算令牌总数。我认为您可以获得中间值。
    • READ UN COMMITTED 只是关闭所有事务处理
    猜你喜欢
    • 2020-06-10
    • 1970-01-01
    • 1970-01-01
    • 2020-09-21
    • 2012-02-16
    • 1970-01-01
    • 2021-08-25
    • 1970-01-01
    • 2017-11-20
    相关资源
    最近更新 更多