You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

什么是Cursor Lost Update?如何在MySQL中复现该问题?

什么是Cursor Lost Update?

Cursor Lost Update是一种基于数据库游标操作的特殊丢失更新问题:当事务通过游标读取某行数据的快照后,另一个事务修改并提交了该行数据,而第一个事务后续基于游标读取的旧值(快照)对该行进行更新时,会直接覆盖掉第二个事务的修改,且这种情况无法被ANSI SQL定义的REPEATABLE READ隔离级别阻止。

它和普通丢失更新的核心区别在于:触发场景是游标读取与后续更新的组合——游标提供的快照读保证了事务内查询的一致性,但后续基于游标定位的更新操作,并不会检测其他事务对该行的已提交修改,最终导致更新丢失。

如何在MySQL中复现Cursor Lost Update?

准备测试环境

首先创建测试表并插入初始数据:

CREATE TABLE account (
    id INT PRIMARY KEY,
    balance INT NOT NULL
);
INSERT INTO account VALUES (1, 100);

分两个事务执行操作

打开两个MySQL客户端窗口,分别模拟事务A和事务B:


事务A(游标操作方)

  1. 启动事务并设置默认隔离级别(REPEATABLE READ):
SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;
START TRANSACTION;
  1. 声明可更新游标并读取目标行数据:
-- MySQL中满足单表、无聚合/分组的SELECT可创建可更新游标
DECLARE cur CURSOR FOR SELECT balance FROM account WHERE id = 1;
OPEN cur;
FETCH cur INTO @current_balance; -- 此时@current_balance的值为100

事务B(并发修改方)

  1. 启动事务、修改数据并立即提交:
START TRANSACTION;
UPDATE account SET balance = 200 WHERE id = 1; -- 将余额改为200
COMMIT;

回到事务A

  1. 基于游标读取的旧值更新目标行:
-- 通过游标定位行并更新,使用读取到的旧值计算新余额
UPDATE account SET balance = @current_balance + 50 WHERE CURRENT OF cur;
COMMIT;
  1. 验证最终结果:
SELECT balance FROM account WHERE id = 1; -- 结果为150,事务B的200被覆盖,更新丢失

关键说明

MySQL的REPEATABLE READ隔离级别通过快照读保证了事务内查询的一致性,但游标读取的快照与后续WHERE CURRENT OF的更新操作之间没有冲突检测机制——即使其他事务已修改并提交该行,事务A仍会基于游标快照的旧值完成更新,从而触发Cursor Lost Update。

内容的提问来源于stack exchange,提问作者Super Kai - Kazuya Ito

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.17 16:25:38