Gre*_*orn 31 mysql sql database locking optimistic
我在MySQL中找不到乐观锁定的任何细节.我读到,启动事务会保持同步的两个实体的更新,但是它不会阻止两个用户同时更新数据而导致冲突.
显然乐观锁定会解决这个问题吗?这是如何在MySQL中应用的.这有SQL语法/关键字吗?或者MySQL有默认行为吗?
多谢你们.
Die*_*aro 108
关键是乐观锁定不是数据库功能,不适用于MySQL,也不适用于其他人:乐观锁定是使用带有标准指令的DB应用的实践.
让我们有一个非常简单的例子,并说你想在多个用户/客户端可以并发运行的代码中执行此操作:
注意:所有代码{在curl brakets之间}都应该在应用程序代码中,而不是(必要地)在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}
Run Code Online (Sandbox Code Playgroud)
- 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}
Run Code Online (Sandbox Code Playgroud)
请注意,关键点在于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}
Run Code Online (Sandbox Code Playgroud)
最后一个示例显示,如果您在某个时刻检查了冲突并发现已经修改了其他表/行时发生了冲突.. ..然后使用事务,您可以回滚自那以后所做的所有更改一开始.显然,取决于您(知道您的应用程序正在做什么)来决定每次可能的冲突回滚的操作量有多大,基于此决定放置事务边界的位置以及检查与特殊冲突的位置的位置UPDATE + AffectedRows检查.
在这种情况下,事务我们将执行UPDATE的时刻与提交时的时刻分开.那么当"其他进程"在这段时间内执行更新时会发生什么?要知道究竟发生了什么,需要深入了解隔离级别的细节(以及如何在每个引擎上进行管理).作为使用READ_COMMITTED的Micosoft SQL Server的一个例子,更新的行被锁定,直到COMMIT,因此"其他进程"不能对那些行做任何事情(等待),既不是SELECT(实际上它只能READ_COMMITTED) .因此,由于"其他进程"活动被推迟,因此UPDATE将失败.
- 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}
Run Code Online (Sandbox Code Playgroud)
这里显示的是,不是检查所有字段的值是否仍然相同,我们可以使用专用字段(每次我们进行更新时修改)以查看是否有人比我们更快并更改了我们之间的行选择和更新.这里没有事务是由于第一个例子中的简单性而与版本列的使用无关.此列的使用再次取决于应用程序代码中的实现,而不是数据库引擎功能.
除此之外还有其他一些观点,我认为这个答案太长了(已经太长了)所以我现在只提到它们一些参考文献:
由于隔离级别值和实现可能不同,因此最佳建议(通常在此站点中)是对使用的平台/环境执行测试.
这似乎很难,但实际上它可以很容易地从任何DB developmemt环境使用两个独立的窗口,并在每一个启动事务,然后通过一个执行命令一个完成.
在某些时候,您将看到命令执行无限期地继续.然后在另一个窗口上,它被称为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);
Run Code Online (Sandbox Code Playgroud)
然后在两个不同的窗口上逐步进行以下操作:
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
Run Code Online (Sandbox Code Playgroud)
然后以您可能想到的任何顺序更改命令的顺序和执行顺序.