如何在 MySQL 中正确实现乐观锁?
我们的团队推断我们必须执行下面的 #4,否则存在另一个线程可以更新记录的相同版本的风险,但我们想验证这是最好的方法。
SELECT FOR UPDATE
对我们要更新的记录执行 a ,以便我们序列化谁可以对我们尝试更新的记录进行更改。为了澄清起见,我们试图阻止在同一时间窗口中选择相同记录的两个线程,如果它们同时尝试更新记录,则它们会在其中获取相同版本的记录时相互覆盖。我们相信,除非我们做 #4,否则有可能,如果两个线程同时进入各自的事务(但尚未发布更新),当它们进行更新时,第二个线程将使用 UPDATE ...其中 version = X 将对旧数据进行操作。
即使我们使用版本字段/乐观锁定,我们在更新时也必须执行这种悲观锁定的想法是否正确?
Cra*_*ger 20
你的开发人员错了。你需要要么 SELECT ... FOR UPDATE
或行版本,而不是两个。
试试看。打开三个 MySQL 会话(A)
,(B)
并(C)
指向同一个数据库。
在(C)
问题:
CREATE TABLE test(
id integer PRIMARY KEY,
data varchar(255) not null,
version integer not null
);
INSERT INTO test(id,data,version) VALUES (1,'fred',0);
BEGIN;
LOCK TABLES test WRITE;
Run Code Online (Sandbox Code Playgroud)
在两个(A)
和(B)
issueUPDATE
中都测试并设置行版本,更改winner
每个中的文本,以便您可以查看哪个会话是哪个:
-- In (A):
BEGIN;
UPDATE test SET data = 'winnerA',
version = version + 1
WHERE id = 1 AND version = 0;
-- in (B):
BEGIN;
UPDATE test SET data = 'winnerB',
version = version + 1
WHERE id = 1 AND version = 0;
Run Code Online (Sandbox Code Playgroud)
现在(C)
,UNLOCK TABLES;
要释放锁。
(A)
并且(B)
将比赛的行锁。其中一个将获胜并获得锁定。另一个将阻塞在锁上。获得锁定的获胜者将继续更改行。假设(A)
是赢家,您现在可以使用SELECT * FROM test WHERE id = 1
.
现在COMMIT
在获胜者会话中,说(A)
。
(B)
将获得锁定并继续更新。但是,版本不再匹配,因此它不会更改任何行,如行计数结果所报告的那样。只有一个UPDATE
有任何影响,客户端应用程序可以清楚地看到哪些UPDATE
成功,哪些失败。无需进一步锁定。
请在此处查看 pastebin 中的会话日志。我使用mysql --prompt="A> "
etc 来轻松区分会话之间的区别。我复制和粘贴按时间顺序交错的输出,所以它不是完全原始的输出,我可能在复制和粘贴时出错。自己测试看看。
如果您没有添加行版本字段,那么您需要SELECT ... FOR UPDATE
能够可靠地确保排序。
如果您考虑一下,如果您立即执行 a而不重新使用来自 的数据,或者您正在使用行版本控制,则 aSELECT ... FOR UPDATE
是完全多余的。在将采取锁定反正。如果其他人在您的读取和后续写入之间更新了该行,您的版本将不再匹配,因此您的更新将失败。这就是乐观锁的工作原理。UPDATE
SELECT
UPDATE
目的SELECT ... FOR UPDATE
是:
SERIALIZABLE
隔离或行版本控制的情况下写入基于原始行的新行。您不需要同时使用乐观锁定(行版本控制)和SELECT ... FOR UPDATE
. 使用其中之一。
归档时间: |
|
查看次数: |
20829 次 |
最近记录: |