我在MySQL中找不到有关乐观锁定的任何细节。 我读到,启动事务会保持同步的两个实体的更新,但是它不会阻止两个用户同时更新数据而导致冲突。
显然乐观锁定会解决这个问题吗?这是如何在MySQL中应用的。这有SQL语法/关键字吗?或者MySQL有默认行为吗?
谢谢你们。
答案 0 :(得分:95)
重点是乐观锁定不是数据库功能,不适用于MySQL,也不适用于其他人:乐观锁定是使用带有标准指令的数据库应用的一种做法。
让我们有一个非常简单的例子,并说你想在多个用户/客户端可以同时运行的代码中执行此操作:
注意:所有代码{在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}
- 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检查。
在这种情况下,对于事务,我们将执行UPDATE的时刻与提交时的时刻分开。那么当“其他进程”在这段时间内执行更新时会发生什么? 要知道究竟发生了什么,需要深入了解隔离级别的细节(以及如何在每个引擎上进行管理)。 以Micosoft SQL Server为例,使用READ_COMMITTED更新了行 直到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}
这里显示的是,不是检查所有字段的值是否仍然相同,我们可以使用专用字段(每次我们进行更新时修改)以查看是否有人比我们更快并更改了我们的SELECT和UPDATE之间的行。 这里没有事务是由于第一个例子中的简单性而与版本列的使用无关。 此列的使用再次取决于应用程序代码中的实现,而不是数据库引擎功能。
除此之外还有其他一些观点,我认为这个答案太长了(已经太长了),所以我现在只提到一些参考文献:
由于隔离级别值和实现可能不同,因此最佳建议(通常在此站点中)是对使用的平台/环境执行测试。
这可能看起来很难但实际上它可以很容易地从任何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
然后以您可能想到的任何顺序更改命令的顺序和执行顺序。