讲师中心 微信公众号

工具推荐

视频效率加速
剧云
剧云

中文短剧剧本制作平台

Laper
Laper

剧本策划到成片协作

AniShort
AniShort

AI短剧协同制作平台

Seko
Seko

AI短剧一键生成成片

mysql如何使用乐观锁处理并发

千静吖_3396

千静吖_3396

发布时间:2025-09-24 09:59:01

|

521人浏览过

|

来源于php中文网

原创

答案:乐观锁通过版本号或时间戳在更新时检查数据一致性,适用于读多写少场景。1. 在表中添加version字段,读取数据时获取版本号;2. 更新时用WHERE条件验证版本号未变,成功则自增版本号;3. 若UPDATE影响行数为0,说明版本已变,需重试或报错;4. 与悲观锁相比,乐观锁并发性能好但冲突高时重试成本大;5. 实现时需避免漏加版本条件、INT溢出、无退避重试等问题;6. 可选替代方案包括时间戳和业务字段CAS,但版本号最通用。

mysql如何使用乐观锁处理并发

在MySQL里,要处理并发问题,特别是避免“丢失更新”这样的情况,乐观锁是一个非常实用的策略。它不像悲观锁那样在操作前就锁定资源,而是假设冲突不常发生,只在提交更新时检查数据是否被其他事务修改过。简单来说,就是通过一个版本号或时间戳来判断,如果你要修改的数据在我读取后已经被别人动过了,那这次更新就作废,需要重新来过。

解决方案

实现MySQL乐观锁的核心思路,是在数据表中增加一个版本字段(比如 version 或 update_time)。

当你从数据库读取一条记录时,会同时获取它的当前版本号。

SELECT data_field, version FROM your_table WHERE id = 123;

假设你读到 version = 1。

然后,在你的业务逻辑处理完,准备更新这条记录时,你需要带上之前读取到的版本号作为条件。

UPDATE your_table
SET data_field = 'new_value', version = version + 1
WHERE id = 123 AND version = 1;

这条SQL语句的精妙之处在于:

  1. version = version + 1:成功更新后,版本号会自增,表明数据已经被修改。
  2. WHERE id = 123 AND version = 1:只有当这条记录的 id 匹配,并且它的 version 仍然是你当初读取到的那个值时,更新才会成功。

如果这条 UPDATE 语句执行后,影响的行数(ROWS AFFECTED)是0,那就说明在你处理数据期间,其他事务已经修改了这条记录,导致 version 字段不再是 1 了。这时,你的更新尝试就失败了,需要根据业务需求进行重试(重新读取数据、重新处理、再次尝试更新)或者报错。

乐观锁与悲观锁,我该如何选择?

这确实是数据库并发控制中一个绕不开的哲学问题。我的看法是,这没有绝对的优劣,只有适不适合你当前场景的考量。

乐观锁就像它的名字一样,对并发冲突持乐观态度。它觉得“冲突没那么频繁,我们没必要一开始就大动干戈地锁住资源,浪费性能”。所以,它把冲突检测放到了更新阶段。这种方式在读多写少的场景下表现出色。比如,一个电商网站的商品详情页,大部分用户都在浏览,只有少数用户会购买或修改商品信息。如果用悲观锁,每次读取商品信息都加锁,那网站性能肯定会雪崩。乐观锁在这里就游刃有余,用户读数据几乎没有开销,只有在更新时才可能遇到冲突,即便冲突了,重试几次通常也能成功。它的缺点是,如果并发写操作非常频繁,导致冲突不断,那么大量的重试会消耗更多资源,反而不如悲观锁高效。而且,冲突解决的逻辑需要应用层去实现,增加了开发复杂度。

悲观锁则恰恰相反,它觉得“冲突随时可能发生,为了数据安全,我必须提前把资源锁住”。它在读取数据时就对数据加上排他锁,直到事务结束才释放。这确保了在整个事务期间,其他任何事务都不能修改或读取被锁定的数据(取决于锁的类型)。这种模式适用于写多读少、冲突频繁的场景,或者对数据一致性要求极高,绝不允许任何形式的冲突(比如银行转账)。它的优点是简单粗暴,数据一致性保障强。但代价是,锁的开销大,会降低并发性能,容易产生死锁。

所以,我的选择标准是:

win可用的mysql指令skill
win可用的mysql指令skill

安装、修复、更新或打包一个基于 Python 和 PyMySQL 实现的 Windows `mysql` 命令。适用于用户希望在 PowerShell/CMD 中输入 `mysql` 时重定向到 Python 实现、需要交互式输入 MySQL 连接信息、需要同步到当前用户本地 bin 目录,或需要将该能力封装成可公开复用的 Codex skill。

下载
  • 低冲突率、读多写少、对响应时间敏感的场景,优先考虑乐观锁。
  • 高冲突率、写多读少、对数据一致性要求极高、宁愿牺牲部分并发性的场景,考虑悲观锁。
  • 有时候,甚至可以混合使用,对核心关键数据用悲观锁,其他用乐观锁。

实现乐观锁时,有哪些“坑”要避免?

尽管乐观锁看起来很优雅,但在实际落地中,还是有些“坑”是需要我们特别留心的,不然可能事与愿违。

一个最常见的,也是最致命的“坑”,就是忘记在 UPDATE 语句中加入版本号的 WHERE 条件。如果你只写了 SET version = version + 1 而没有 WHERE version = old_version,那所有并发的更新都会成功,只是版本号不断递增,但数据依然会被覆盖,这就完全失去了乐观锁的意义,成了彻头彻尾的“丢失更新”。这就像你精心设计了一道防火墙,结果把大门敞开了。

另一个需要注意的,是版本号的数据类型。如果你的系统并发量非常大,或者某个业务场景下对同一条记录的更新极其频繁,那么 INT 类型的 version 字段可能会面临溢出的风险。虽然这在大多数业务中不常见,但一旦发生,会导致版本号回绕,引发严重的数据一致性问题。这时候,使用 BIGINT 这样的更大整数类型会更安全。或者,如果使用时间戳作为版本号,要确保时间戳的精度足够,并且在比较时能够精确匹配,避免因为毫秒级的差异导致误判。

还有就是重试机制的设计。乐观锁的冲突解决策略,很大程度上依赖于应用层的重试。如果发生冲突,你的应用应该怎么做?是立即重试,还是等待一段时间再重试?重试多少次?有没有最大重试次数?如果没有一个合理的重试策略,可能会导致:

  1. 无限重试:在极端高并发下,如果每次重试都失败,系统会陷入死循环,耗尽资源。
  2. 重试风暴:大量失败的事务同时进行重试,反而加剧了数据库的压力,形成恶性循环。 所以,设计一个带有指数退避(Exponential Backoff)和最大重试次数的重试机制至关重要,这能有效缓解系统压力。

最后,别忘了事务的边界。乐观锁的版本检查和更新操作,通常需要在同一个数据库事务中完成。虽然乐观锁本身不依赖数据库的锁定机制,但为了保证 SELECT 到 UPDATE 之间的操作原子性,将它们包裹在一个事务中是良好的实践。

除了版本号,还有其他实现乐观锁的姿势吗?

当然有,版本号(version number)只是最常见、最直观的一种实现方式,但不是唯一。从本质上讲,乐观锁就是一种“先检查后更新”的模式,只要能确保更新时数据没有被别人动过,都可以算作乐观锁的范畴。

一种常见的替代方案是使用时间戳(timestamp)。你可以在表中增加一个 updated_at 或者 last_modified_time 字段,类型可以是 DATETIME 或 TIMESTAMP。每次更新记录时,都自动更新这个字段为当前时间。

-- 读取
SELECT data_field, updated_at FROM your_table WHERE id = 123;
-- 更新
UPDATE your_table
SET data_field = 'new_value', updated_at = NOW()
WHERE id = 123 AND updated_at = '2023-10-27 10:00:00'; -- 假设这是之前读取到的时间戳

这种方式和版本号类似,但它利用了时间的自然递增性。需要注意的是,时间戳的精度问题,如果并发非常高,在同一毫秒内发生多次更新,可能会导致时间戳相同,从而无法正确检测冲突。MySQL的 TIMESTAMP 字段通常能精确到微秒,这在大多数情况下是足够的,但仍需留意。

另一种思路是基于业务字段的比较(Compare-And-Swap,CAS)。这种方式不额外增加版本字段,而是直接利用业务字段的旧值来判断。比如,你要更新一个商品的库存 stock 字段:

-- 读取(这里可能不需要读取旧值,直接在更新时判断)
-- 更新
UPDATE products
SET stock = stock - 1
WHERE id = 456 AND stock > 0 AND stock = 10; -- 假设你期望的旧库存是10

这里,AND stock = 10 就是一个乐观锁的判断条件。它确保只有当 stock 字段的值仍然是你期望的 10 时,更新操作才会执行。如果 stock 已经被其他事务修改成了 9,那么你的更新就不会成功。这种方式特别适用于只更新特定字段,并且该字段的值具有明确的“旧值”预期的情况。它的优点是侵入性小,不需要额外字段,但缺点是如果业务逻辑复杂,涉及多个字段的更新,这种方式会变得非常笨重,难以维护。

总的来说,版本号是实现乐观锁最通用、最健壮的方式,而时间戳和基于业务字段的CAS则是在特定场景下的灵活变通。选择哪种,取决于你的具体需求、数据特性以及对复杂度的接受程度。

热门AI工具

更多
讯飞智作

讯飞智作是一款AI视频创作工具,AI文本配音工具,数字人课程、营销视频制作。

超级麦吉

一款通过数字员工协作完成调研、数据分析与文档制作的AI办公平台。

SoundView
SoundView Hot

SoundView是一款面向跨境电商的视频翻译、配音和本地化 AI 创作平台。

DeepSeek

DeepSeek是一款面向对话、写作、编程和推理场景的AI大模型工具。

墨刀AI
墨刀AI Hot

一款AI图像与设计工具,主要用于产品经理的专属智能体,适合需要提升相关任务效率的用户。

讯飞智文

一款面向学习和办公场景的AI文档创作工具,可辅助生成PPT与Word文档,提高资料整理和内容制作效率。

WorkBuddy

一款AI办公效率工具,主要用于腾讯云推出的AI原生桌面智能体工作台,适合需要提升相关任务效率的用户。

火山引擎

火山引擎是一款面向企业的云计算与AI服务平台。

豆包大模型

豆包大模型是一款由字节跳动推出的企业级大语言模型服务平台。

相关专题

更多
数据分析工具有哪些
数据分析工具有哪些

数据分析工具有Excel、SQL、Python、R、Tableau、Power BI、SAS、SPSS和MATLAB等。详细介绍:1、Excel,具有强大的计算和数据处理功能;2、SQL,可以进行数据查询、过滤、排序、聚合等操作;3、Python,拥有丰富的数据分析库;4、R,拥有丰富的统计分析库和图形库;5、Tableau,提供了直观易用的用户界面等等。

4983

2023.10.12

SQL中distinct的用法
SQL中distinct的用法

SQL中distinct的语法是“SELECT DISTINCT column1, column2,...,FROM table_name;”。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

1011

2023.10.27

SQL中months_between使用方法
SQL中months_between使用方法

在SQL中,MONTHS_BETWEEN 是一个常见的函数,用于计算两个日期之间的月份差。想了解更多SQL的相关内容,可以阅读本专题下面的文章。

1309

2024.02.23

SQL出现5120错误解决方法
SQL出现5120错误解决方法

SQL Server错误5120是由于没有足够的权限来访问或操作指定的数据库或文件引起的。想了解更多sql错误的相关内容,可以阅读本专题下面的文章。

7041

2024.03.06

sql procedure语法错误解决方法
sql procedure语法错误解决方法

sql procedure语法错误解决办法:1、仔细检查错误消息;2、检查语法规则;3、检查括号和引号;4、检查变量和参数;5、检查关键字和函数;6、逐步调试;7、参考文档和示例。想了解更多语法错误的相关内容,可以阅读本专题下面的文章。

3703

2024.03.06

oracle数据库运行sql方法
oracle数据库运行sql方法

运行sql步骤包括:打开sql plus工具并连接到数据库。在提示符下输入sql语句。按enter键运行该语句。查看结果,错误消息或退出sql plus。想了解更多oracle数据库的相关内容,可以阅读本专题下面的文章。

7020

2024.04.07

sql中where的含义
sql中where的含义

sql中where子句用于从表中过滤数据,它基于指定条件选择特定的行。想了解更多where的相关内容,可以阅读本专题下面的文章。

9821

2024.04.29

sql中删除表的语句是什么
sql中删除表的语句是什么

sql中用于删除表的语句是drop table。语法为drop table table_name;该语句将永久删除指定表的表和数据。想了解更多sql的相关内容,可以阅读本专题下面的文章。

1330

2024.04.29

Go-Zero API开发使用教程
Go-Zero API开发使用教程

本专题聚焦Go-Zero的API开发,拆解.api描述文件的语法结构、路由与分组定义、参数绑定与校验规则,说明统一响应体、JWT鉴权、跨域与接口文档生成的落地方式,并给出网关聚合与接口测试的实践建议。

20

2026.11.12

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn