繁体   English   中英

MySQL中的乐观锁

[英]Optimistic locking in MySQL

我在 MySQL 中找不到有关乐观锁定的任何详细信息。 我读到启动事务会使两个实体的更新保持同步,但是 - 它不会阻止两个用户同时更新数据,从而导致冲突。

显然乐观锁会解决这个问题? 这是如何在 MySQL 中应用的。 是否有 SQL 语法/关键字? 还是 MySQL 有默认行为?

谢谢你们。

关键是乐观锁不是数据库功能,不适用于 MySQL 或其他:乐观锁是一种使用 DB 和标准指令应用的实践。

让我们举一个非常简单的例子,假设您想在多个用户/客户端可以同时运行的代码中执行此操作:

  1. 从具有一个 ID 字段 (iD) 和两个数据字段 (val1, val2) 的行中选择数据
  2. 可选择使用数据进行计算
  3. 更新该行的数据

无锁定方式是:

注意:所有代码 { 大括号之间} 都打算在应用程序代码中,而不是(必须)在 SQL 端

- SELECT iD, val1, val2
       FROM theTable
       WHERE iD = @theId;
 - {code that calculates new values}
 - UPDATE theTable
       SET val1 = @newVal1,
           val2 = @newVal2
       WHERE iD = @theId;
 - {go on with your other code}

OPTIMISTIC LOCKING 方式是:

- SELECT iD, val1, val2
       FROM theTable
       WHERE iD = @theId;
 - {code that calculates new values}
 - UPDATE theTable
       SET val1 = @newVal1,
           val2 = @newVal2
       WHERE iD = @theId
           AND val1 = @oldVal1
           AND val2 = @oldVal2;
 - {if AffectedRows == 1 }
 -     {go on with your other code}
 - {else}
 -     {decide what to do since it has gone bad... in your code}
 - {endif}

请注意,关键点在于 UPDATE 指令的结构和后续受影响的行数检查。 正是这两件事一起让您的代码意识到在您执行 SELECT 和 UPDATE 之间已经有人修改了数据。 请注意,一切都已在没有事务的情况下完成! 这是可能的(没有事务)只是因为这是一个非常简单的例子,但这也说明乐观锁定的关键点不在于事务本身。

那么交易呢?

 - SELECT iD, val1, val2
       FROM theTable
       WHERE iD = @theId;
 - {code that calculates new values}
 - BEGIN TRANSACTION;
 - UPDATE anotherTable
       SET col1 = @newCol1,
           col2 = @newCol2
       WHERE iD = @theId;
 - UPDATE theTable
       SET val1 = @newVal1,
           val2 = @newVal2
       WHERE iD = @theId
           AND val1 = @oldVal1
           AND val2 = @oldVal2;
 - {if AffectedRows == 1 }
 -     COMMIT TRANSACTION;
 -     {go on with your other code}
 - {else}
 -     ROLLBACK TRANSACTION;
 -     {decide what to do since it has gone bad... in your code}
 - {endif}

最后一个示例表明,如果您在某个时刻检查冲突并发现在您已经修改其他表/行时发生了冲突......那么对于事务,您可以回滚自此以来所做的所有更改开始。 显然,由您(知道您的应用程序在做什么)来决定每次可能发生的冲突要回滚的操作量有多大,并基于此决定将事务边界放在哪里以及在何处检查与特殊的冲突UPDATE + AffectedRows 检查。

在这种情况下,对于事务,我们将执行更新的时刻与提交的时刻分开。 那么当“其他进程”在这个时间范围内执行更新时会发生什么? 要知道究竟发生了什么,需要深入研究隔离级别的细节(以及如何在每个引擎上管理它们)。 例如,在 Microsoft SQL Server 的情况下,带有 READ_COMMITTED 更新的行被锁定,直到 COMMIT,因此“其他进程”不能在该行上执行任何操作(保持等待),SELECT 也不能(实际上它只能 READ_COMMITTED )。 因此,由于“其他进程”活动被推迟,因此 UPDATE 将失败。

VERSIONING OPTIMISTIC LOCKING 选项:

 - SELECT iD, val1, val2, version
       FROM theTable
       WHERE iD = @theId;
 - {code that calculates new values}
 - UPDATE theTable
       SET val1 = @newVal1,
           val2 = @newVal2,
           version = version + 1
       WHERE iD = @theId
           AND version = @oldversion;
 - {if AffectedRows == 1 }
 -     {go on with your other code}
 - {else}
 -     {decide what to do since it has gone bad... in your code}
 - {endif}

此处显示,与其检查所有字段的值是否仍然相同,我们可以使用专用字段(每次执行 UPDATE 时都会修改)来查看是否有人比我们更快并更改了我们之间的行选择和更新。 这里没有事务是由于第一个示例中的简单性,与版本列的使用无关。 同样,此列的使用取决于应用程序代码中的实现,而不是数据库引擎功能。

不仅如此,我认为还有其他几点会使这个答案太长(已经太长了)所以我现在只提到它们并提供一些参考:

  • 事务隔离级别( 此处为 MySQL )关于事务对 SELECT 的影响。
  • 对于具有未自动生成的主键(或唯一约束)的表上的 INSERT,如果两个进程尝试在必须唯一的地方插入相同的值,它会自动失败,无需特别检查。
  • 如果您没有 id 列(主键或唯一约束),那么单个 SELECT + UPDATE 也需要事务,因为您可能会惊奇地发现,在其他人进行修改后,与 UPDATE 的 WHERE 子句的条件匹配的行数比预期的多。

如何在实践中检查并获得信心

由于隔离级别值和实现可能不同,因此最好的建议(如本站点中的惯例)是在使用的平台/环境上执行测试。

这可能看起来很困难,但实际上可以很容易地从任何 DB 开发环境中完成,使用两个单独的窗口,并在每个窗口上启动一个事务,然后一个一个地执行命令。

在某些时候,您会看到命令执行无限期地继续。 然后,当在另一个窗口上调用 COMMIT 或 ROLLBACK 时,它完成执行。

这里有一些非常基本的命令,可以按照刚才的描述进行测试。

使用这些来创建表格和一个有用的行:

CREATE TABLE theTable(
    iD int NOT NULL,
    val1 int NOT NULL,
    val2 int NOT NULL
);
INSERT INTO theTable (iD, val1, val2) VALUES (1, 2 ,3);

然后在两个不同的窗口上逐步执行以下操作:

BEGIN TRAN

SELECT val1, val2 FROM theTable WHERE iD = 1;

UPDATE theTable
  SET val1=11
  WHERE iD = 1 AND val1 = 2 AND val2 = 3;

COMMIT TRAN

然后以您可能想到的任何顺序更改命令的顺序和执行顺序。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM