MySQL丢失更新问题的出现和解决

devtools/2024/9/22 18:10:32/

MySQL丢失更新问题的出现和解决

丢失更新问题(Lost Update)指的是两个或多个事务在读同一数据并基于此数据进行更新操作时,某些更新操作被覆盖或丢失。例如,事务A和事务B都读取了某个数据,然后事务A更新了该数据,而事务B在没有意识到事务A的更新的情况下,也更新了该数据,导致事务A的更新被覆盖。

丢失更新问题的出现原因

丢失更新问题通常在以下情况下出现:

  1. 无锁定的并发访问:多个事务同时读取并修改同一数据,而没有任何形式的锁定机制。
  2. 低隔离级别:在READ UNCOMMITTED或READ COMMITTED隔离级别下,事务可以看到其他未提交事务的修改,增加了丢失更新问题的可能性。

解决丢失更新问题的方法

1. 使用乐观锁

乐观锁通过在每次更新数据时检查数据是否在读取后被修改来避免丢失更新问题。常用的方法是添加一个版本号或时间戳字段,每次更新时检查版本号是否一致,不一致则说明数据已被其他事务修改,更新失败。

示例:

-- 添加版本号字段
ALTER TABLE my_table ADD COLUMN version INT DEFAULT 0;-- 更新数据时检查版本号
UPDATE my_table
SET value = 'new_value', version = version + 1
WHERE id = 1 AND version = 2;

2. 使用悲观锁

悲观锁通过锁定数据来防止其他事务同时访问和修改同一数据。MySQL提供了SELECT … FOR UPDATE语法来实现悲观锁。

示例:

-- 锁定数据
START TRANSACTION;
SELECT value FROM my_table WHERE id = 1 FOR UPDATE;-- 更新数据
UPDATE my_table SET value = 'new_value' WHERE id = 1;-- 提交事务
COMMIT;

3. 提升隔离级别

提升隔离级别可以减少并发事务对同一数据的访问冲突。将隔离级别设置为REPEATABLE READ或SERIALIZABLE可以有效防止丢失更新问题。

示例:

-- 设置事务隔离级别为REPEATABLE READ
SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;-- 或者设置为SERIALIZABLE
SET SESSION TRANSACTION ISOLATION LEVEL SERIALIZABLE;

REPEATABLE READ 隔离级别下的丢失更新

尽管REPEATABLE READ隔离级别能够防止脏读和不可重复读,但在某些情况下仍可能出现丢失更新问题。在MySQL中,REPEATABLE READ隔离级别通过使用多版本并发控制(MVCC)来提供一致性视图,确保同一事务中多次读取的数据是一致的。然而,这并不能完全避免丢失更新问题,因为多个事务仍然可以基于相同的数据快照进行更新。

示例

假设有一个表 account,其中包含字段 idbalance

CREATE TABLE account (id INT PRIMARY KEY,balance DECIMAL(10, 2)
);

现在有两个事务,事务A和事务B,都试图更新同一个账户的余额。

-- 事务A
START TRANSACTION;
SELECT balance FROM account WHERE id = 1;  -- 假设读取到的balance为100-- 事务B
START TRANSACTION;
SELECT balance FROM account WHERE id = 1;  -- 同样读取到的balance为100
UPDATE account SET balance = 80 WHERE id = 1;  -- 将余额减少20
COMMIT;-- 事务A
UPDATE account SET balance = 120 WHERE id = 1;  -- 将余额增加20(基于原始值100)
COMMIT;

在这种情况下,事务B的更新将会被事务A的更新覆盖,导致事务B的更新丢失。

解决方法

为了避免这种情况,可以结合使用悲观锁和适当的事务处理。

-- 事务A
START TRANSACTION;
SELECT balance FROM account WHERE id = 1 FOR UPDATE;  -- 加锁读取-- 事务B
START TRANSACTION;
SELECT balance FROM account WHERE id = 1 FOR UPDATE;  -- 尝试加锁,将被阻塞直到事务A完成-- 事务A
UPDATE account SET balance = 120 WHERE id = 1;
COMMIT;-- 事务B(在事务A完成后继续)
UPDATE account SET balance = 80 WHERE id = 1;
COMMIT;

通过使用 FOR UPDATE 加锁读取,可以确保在事务A完成之前,事务B无法读取到被锁定的数据,从而避免丢失更新问题。

参考链接

  • MySQL文档:MySQL 事务隔离级别

在这里插入图片描述


http://www.ppmy.cn/devtools/56294.html

相关文章

CesiumJS【Basic】- #026 加载kml/kmz文件

文章目录 加载kml/kmz文件1 目标2 代码2.1 main.ts3 资源加载kml/kmz文件 1 目标 加载kml/kmz文件(kmz是kml的压缩格式) 2 代码 加载CesiumJS【Basic】- #025 生成kml/kmz文件导出生成的entities.kml/kmz文件,并修改点、线、面元素的颜色 2.1 main.ts import * as Ces…

windows 安装 Kubernetes(k8s)

windows 安装 docker 详情见: https://blog.csdn.net/sinat_32502451/article/details/133026301 minikube Minikube 是一种轻量级的Kubernetes 实现,可在本地计算机上创建VM 并部署仅包含一个节点的简单集群。 下载地址:https://github.…

qt实现打开pdf(阅读器)功能用什么库比较合适

关于这个问题,网上搜一下,可以看到非常多的相关博客和例子,可以先看看这个总结性的博客(https://zhuanlan.zhihu.com/p/480973072) 该博客讲得比较清楚了,这里我再补充一下吧(qt官方也给出了一些…

不能创建第三个变量,实现两个数的交换

目录 常规实现两个数的交换(如:交换变量a和变量b) 方法一:加减法 方法二:异或操作符 常规实现两个数的交换(如:交换变量a和变量b) 创建一个临时变量tmp,先将其中一个…

Lodash-js工具库

1. Lodash 简介 Lodash 是一个现代 实用工具库,提供了许多有用的函数,帮助开发者处理常见的编程任务,如数组操作、对象处理、字符串处理等。Lodash 使得代码更简洁、更高效,极大地提高了开发效率。Lodash 的设计灵感来自于 Under…

智能体时代:Agent开发的三重境界

❝ 在人工智能领域,Agent开发是一个不断演进的过程,它涉及到如何将AI技术与实际应用相结合,以提高效率、增强用户体验和推动业务发展。本文将探讨Agent开发的三个阶段,从基础的API使用到复杂的智能应用开发,逐步深入&a…

帝国cms未审核文章可视化预览效果

有时候为了让编辑更加清楚的看到别人审核之后的效果,同时文章有需要下一级审核才能在前端展示出来,今天就来展示一个未审核文章预览审核后的效果 这次给某出版社开发的时候,他们需要实现编辑能够预览自己发布之后的审核效果,所以就…

IIC学习笔记(立创STMF4开发板)

目录 #I2C涉及相关知识 #I2C相关介绍 欢迎指正,希望对你,有所帮助!!! 个人学习笔记,参考文献,链接最后!!! #I2C涉及相关知识 SDA串行数据线: Ser…