MySQL 中的乐观锁

2024-04-08

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

显然乐观锁定可以解决这个问题?这在 MySQL 中是如何应用的。有相关的 SQL 语法/关键字吗?或者MySQL有默认行为吗?

多谢你们。


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

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

  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}

乐观锁定方式是:

- 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 的情况为例,更新的行 被锁定直到提交,因此“其他进程”不能对该行执行任何操作(一直等待),也不能执行 SELECT(实际上它只能 READ_COMMITTED)。 因此,由于“其他进程”活动被推迟,因此更新将失败。

VERSIONING 乐观锁定选项:

 - 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 http://dev.mysql.com/doc/refman/5.6/en/dynindex-isolevel.html)关于事务对 SELECT 的影响。
  • 对于主键未自动生成(或唯一约束)的表上的 INSERT,如果两个进程尝试在必须唯一的地方插入相同的值,它将自动失败,无需进行特殊检查。
  • 如果您没有 id 列(主键或唯一约束),那么单个 SELECT + UPDATE 也需要事务,因为您可能会惊讶地发现,在其他人进行修改后,与 UPDATE 的 WHERE 子句的条件匹配的行数比预期多。

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

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

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

在某些时候,您会看到命令无限期地继续执行。 然后,当在另一个窗口上调用 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

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

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

MySQL 中的乐观锁 的相关文章

  • Riak 在 MapReduce 查询中失败。使用哪种配置?

    我正在与 riak riak js 结合开发一个 nodejs 应用程序 并遇到以下问题 运行此请求 db mapreduce add logs run 正确返回存储在存储桶日志中的所有 155 000 个项目及其 ID logs 1GXt
  • 创建日期范围表

    我正在编写一份需要显示每天值的报告 我有查询的开始日期和结束日期 但我希望避免丢失日期 以防表不包含特定日期的值 我正在考虑创建一个基本日期范围表 其中包含开始和结束之间的所有日期 然后将其与数据表左连接以显示每一天的值 我找到了一些适用于
  • 在 SQL 中按键组对行进行顺序编号?

    SQL中有没有办法按顺序添加行号按关键组 假设一个表包含任意 CODE NAME 元组 示例表 CODE NAME A Apple A Angel A Arizona B Bravo C Charlie C Cat D Dog D Dopp
  • MySQL LIKE %string% 不够宽容。我还有什么可以用的吗?

    我有一位客户询问他们的搜索是否可以搜索公司名称 这些名称可以根据用户输入以多种格式进行搜索 例如数据库中存储的公司是 A J R Kelly Ltd 如果用户搜索 一个 J R Kelly 被发现 使用
  • ORA-00933 与内部联接和“as”混淆

    我有一个使用以下命令从两个表中获取数据的查询inner join 但我收到错误SQL command not properly ended as 下面有一个星号 select P carrier id O order id O aircra
  • 通过 SQLAlchemy 获取随机行

    如何使用 SQLAlchemy 从表中选择一个或多个随机行 这在很大程度上是一个特定于数据库的问题 我知道 PostgreSQL SQLite MySQL 和 Oracle 具有通过随机函数排序的能力 因此您可以在 SQLAlchemy 中
  • SQLAlchemy - 批量插入忽略:“重复条目”

    我有一个名为user data 列id and user id作为唯一的密钥 我想将一些历史数据导入到该表中 我用批量插入映射 http docs sqlalchemy org en rel 1 0 orm session api html
  • 如何在 Spring Data 中选择不同的结果

    我在使用简单的 Spring Data 查询或 Query 或 QueryDSL 在 Spring Data 中构建查询时遇到问题 如何选择三列 研究 国家 登录 不同的行 并且查询结果将是用户对象类型的列表 Table User Id S
  • Extbase - 从查询中获取创建的sql

    我想从我的typo3 扩展中获取一些数据库表 该扩展基于 extbase 查询总是不返回任何内容 但数据存在 我试过这个 query this gt createQuery query gt statement SELECT FROM my
  • django AuditTrail 与还原

    我正在开发一个新的网络应用程序 我需要将数据库中的任何更改存储到审核表中 此类审计表的目的是 稍后在真正的物理审计中 我们可以确定在某种情况下发生了什么 谁编辑了什么以及数据库当时的状态是什么 复杂的计算 所以大多数审计表将被写入而不是读取
  • 如何将 javax.persistence.Column 定义为 Unsigned TINYINT?

    我正在基于 MySQL 数据库中的现有表创建 Java 持久性实体 Bean 使用 NetBeans IDE 8 0 1 我在这个表中遇到了一个字段 其类型为 无符号 TINYINT 3 我发现可以执行以下操作将列的类型定义为 unsign
  • 是否可以将新表和旧表从触发器传递到 MySQL 中的过程中?

    是否可以将新表和旧表从触发器传递到 MySQL 中的过程中 我怀疑不会 因为没有过程接受的表这样的数据类型 有什么可能的解决方法吗 理想情况下它看起来像这样 CREATE TRIGGER Product log AFTER UPDATE O
  • Android中不同线程的数据库访问

    我有一个在 AsyncTasks 中从互联网下载数据的服务 它解析数据并将其存储在数据库中 该服务持续运行 当服务写入数据库时 活动会尝试从数据库中读取更改 我有一个数据库助手 有多种写入和读取方法 这会导致问题吗 可能尝试从两个不同的线程
  • Spring Data JPA 选择不同

    我有一个情况 我需要建立一个select distinct a address from Person a 其中地址是 Person 内的地址实体 类型的查询 我正在使用规范动态构建我的 where 子句并使用findAll Specifi
  • 在 PostgreSql 中计算百分比

    例如我有一个这样的表 string adm A 2 A 1 B 2 A 1 C 1 A 2 通过 SQL 查询 我想要这样的结果 string perc adm A 50 B 100 C 0 我想要每个字符串中数字 2 出现的百分比 我可以
  • MySQL:如何获取每个分组的x个结果数[重复]

    这个问题在这里已经有答案了 可能的重复 mysql 在 GROUP BY 中使用 LIMIT 来获取每组 N 个结果 https stackoverflow com questions 2129693 mysql using limit w
  • 如何连续添加起始行和下一行的值

    我只想创建一个 sql 查询 结果就像图片上的那样 类似于 SQL 中的斐波那契数列 Ex Column 1 10 则 Result 列的值为 Result 10 因为这是第一行 然后假设column1第二行的值为50 那么Result第二
  • 计算 mysql 数据库行数的最佳方法

    在遇到 mysql 查询加载时间慢的问题后 我现在正在寻找计算行数的最佳方法 我曾经愚蠢地使用过mysql num rows 函数来做到这一点 现在意识到这是最糟糕的方法 我实际上正在制作一个分页来用 PHP 制作页面 我找到了几种计算行数
  • 尝试在 React 应用程序中连接到 MySQL 数据库时,无法读取未定义的属性(读取“查询”)错误

    我正在尝试连接到 MySQL 数据库并在单击按钮后在 React 应用程序中运行查询 一些它如何给出错误 我当前的代码如下所示 import mysql from mysql function App async function sync
  • Hibernate + MySQL + rewriteBatchedStatements=true

    我有以下 Hibernate 配置

随机推荐