当前位置: 首页 > 面试题库 >

MySQL中的乐观锁定

夹谷茂
2023-03-14
问题内容

我找不到有关MySQL中乐观锁定的任何详细信息。我读到开始事务使两个实体上的更新保持同步,但是,它不会停止两个用户同时更新数据而引起冲突。

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

谢谢你们。


问题答案:

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

让我们有一个非常简单的示例,并说您想用多个用户/客户端可以同时运行的代码来做到这一点:

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

NO LOCKING的方式是:

注意:所有代码{在大括号之间}都应位于应用程序代码中,而不是(有必要)在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时之间已经有人修改了数据。请注意,所有操作都没有交易!之所以能够这样做(没有事务),是因为这是一个非常简单的示例,但这也表明,乐观锁定的关键不在事务本身中。

那TRANSACTIONS呢?

 - 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检查。

在这种情况下,对于事务,我们将执行UPDATE的时间与提交UPDATE的时间分开了。那么,当“其他进程”在此时间范围内执行更新时,会发生什么呢?要知道到底发生了什么,需要深入研究隔离级别的细节(以及如何在每个引擎上进行管理)。以带有READ_COMMITTED的Microsoft
SQL
Server为例,更新的行被锁定,直到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时都会对其进行修改)来查看是否有人比我们要快并且更改了我们之间的行选择和更新。这里没有事务是由于如第一个示例中的简单性,与版本列的使用无关。同样,此列的使用取决于应用程序代码中的实现,而不是数据库引擎功能。

不仅如此,我还认为其他一些问题会使答案变得太长(已经太长),因此我现在仅以一些参考文献提及它们:

  • 有关SELECT上的事务影响的事务隔离级别(此处为MySQL)。
  • 对于主键未自动生成(或唯一约束)的表上的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

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



 类似资料:
  • 在多用户环境中,在同一时间可能会有多个用户更新相同的记录,会产生冲突,解决方案有两种:乐观锁、悲观锁。 悲观锁在这里不讲,自行Google。 乐观锁假设不会发生并发冲突,只在提交操作时检查是否违反数据完整性,不完整则更新失败。 乐观锁实现方式 使用整数表示数据版本号.更新时检查版本号是否一致,如果相等,则更新成功,且版本号+1.如果不等,则数据已经被修改过,更新失败。 使用时间戳来实现。 本质上也

  • 悲观锁:假定会发生并发冲突,屏蔽一切可能违反数据完整性的操作 乐观锁:假设不会发生并发冲突,只在提交操作时检查是否违反数据完整性。 乐观锁与悲观锁的具体区别: http://www.cnblogs.com/Bob-FD/p/3352216.html

  • 乐观锁Version 要使用乐观锁,需要使用version标记 type User struct { Id int64 Name string Version int `xorm:"version"` } 在Insert时,version标记的字段将会被设置为1,在Update时,Update的内容必须包含version原来的值。 var user User engine

  • 本文向大家介绍说一下乐观锁和悲观锁?相关面试题,主要包含被问及说一下乐观锁和悲观锁?时的应答技巧和注意事项,需要的朋友参考一下 乐观锁:每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在提交更新的时候会判断一下在此期间别人有没有去更新这个数据。 悲观锁:每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会阻止,直到这个锁被释放。 数据库的乐观锁需要自

  • 我们有一个系统,我们偶尔会得到一个乐观的锁定异常。我们在代码中已经解决了这个问题,但现在我正在查看JPA 2,并看到它有一个用于处理这个问题的注释(@版本) 我们的问题是,一个表上有多个事务,如果表锁已满,则即使未对相同的记录进行更改,也会导致乐观锁定异常。 我们在JBoss 4.2服务器上使用hibernate,数据库可以是MySQL或SQL服务器。 如果改为使用@Version,这会在两个数据

  • 我读到了乐观锁定方案,客户端可以读取值,执行计算,当需要写入时,更新在写入数据库之前经过验证。 假设如果我们为乐观锁采用版本机制,那么(在两个客户端的情况下)两者都将具有更新语句,如下所示: 更新表名称集字段 = val,版本 = 旧版本 1,其中版本 = 旧版本,ID = x; 现在让我们考虑以下两个客户端的场景: > 两个客户端都读取字段和版本的值。 两个客户端都在那里计算一些东西。生成字段的