讲师中心 微信公众号
AI工具推荐 视频效率加速

SQL 并发更新冲突处理策略

星强大大_4231

星强大大_4231

发布时间:2026-02-24 19:25:02

|

884人浏览过

|

来源于php中文网

原创

应使用带预期旧值的UPDATE语句(如WHERE id=123 AND balance=500)实现乐观锁,若ROW_COUNT()为0则说明数据被并发修改,需重试或报错;避免先查后更新、浮点数直接比对、滥用SELECT FOR UPDATE,高并发任务分发优选SKIP LOCKED,INSERT ON DUPLICATE KEY UPDATE中累加需用VALUES(score),version字段推荐BIGINT防溢出。

sql 并发更新冲突处理策略

UPDATE 时被其他事务抢改了数据,怎么避免覆盖

直接用 UPDATE 更新一行,不加条件检查,就容易让后提交的事务无声覆盖前一个事务的修改——比如两个请求同时给用户余额加 100,结果只加了一次。这不是数据库坏了,是没做并发控制。

最常用也最可控的方式,是在 WHERE 子句里带上“预期旧值”做校验:

UPDATE accounts SET balance = balance + 100 WHERE id = 123 AND balance = 500;

如果执行后 ROW_COUNT()(MySQL)或 pg_affected_rows()(PostgreSQL)返回 0,说明这行数据在你读完之后已被别人改过,当前更新失效,需要重试或报错。

  • 别只靠应用层“先查再更新”,查和更新之间存在时间窗口,中间可能被篡改
  • 如果字段类型是浮点数或有精度问题,避免直接比对原始值;可改用版本号(version)或时间戳(updated_at)字段
  • 使用 SELECT ... FOR UPDATE 能锁住行,但会阻塞其他事务,高并发下容易引发锁等待甚至死锁,不是万能解法

PostgreSQL 的 SELECT FOR UPDATE 和 SKIP LOCKED 怎么选

SELECT ... FOR UPDATE 是行级写锁,适合“读-改-写”强一致性场景,比如库存扣减;但它会让并发请求排队,吞吐上不去。

当你要从队列表(如任务队列)里安全取一条未处理的任务,并且多个 worker 同时拉取时,SKIP LOCKED 就很关键:

SELECT * FROM jobs WHERE status = 'pending' ORDER BY id LIMIT 1 FOR UPDATE SKIP LOCKED;

它会让被其他事务锁住的行直接跳过,而不是等锁释放,避免 worker 卡住。

  • 没有 SKIP LOCKED 时,多个 FOR UPDATE 可能争同一行,造成无意义等待
  • SKIP LOCKED 只在 PostgreSQL 9.5+ 和 MySQL 8.0.1+ 支持,老版本只能靠应用层加分布式锁或乐观锁兜底
  • 不能和 GROUP BY、聚合函数混用;也不能在可重复读(RR)隔离级别下保证“绝对不重复取”,需配合状态字段二次校验

MySQL 的 INSERT ON DUPLICATE KEY UPDATE 真的安全吗

它常被用来做“存在则更新,不存在则插入”,看起来像原子操作,但只对 UNIQUE 或 PRIMARY KEY 冲突生效,而且更新逻辑只触发一次——这点容易误判。

例如:INSERT INTO points (user_id, score) VALUES (123, 10) ON DUPLICATE KEY UPDATE score = score + 10; 表面上是“加 10”,但如果两条并发插入同时触发冲突,它们都读到旧值(比如 50),然后各自加 10,最终变成 60 而不是 70。

  • 这不是 bug,是语义限制:表达式里的 score 是语句开始时的快照值,不是实时值
  • 想真正累加,得用 VALUES(score) 引用 VALUES 子句中的值,或者改用 UPDATE ... WHERE + 乐观锁
  • 注意 ON DUPLICATE KEY UPDATE 会影响 AUTO_INCREMENT 计数器,即使没插入新行,自增 ID 也可能跳号

乐观锁 version 字段为什么经常被设成 BIGINT

因为 INT 最大值才 21 亿,高频更新表(比如订单状态变更每秒几百次)几个月就可能溢出;一旦 version 溢出变负数,后续所有更新都会因 WHERE version = ? 不匹配而失败。

  • 用 BIGINT 不是为了“以后扩展”,而是防止真实生产中踩到整数溢出这个静默陷阱
  • 别把 version 当时间戳用——有人会写 version = UNIX_TIMESTAMP(),但这会导致并发更新时时间相同、校验失效
  • 如果业务允许,version 可以简化为布尔型(如 is_dirty),但仅适用于“只允许改一次”的极简场景,通用性差

并发更新不是非得选一种策略包打天下。实际项目里,账户余额用带旧值校验的 UPDATE,任务分发用 SKIP LOCKED,幂等写入用 INSERT ... ON DUPLICATE KEY UPDATE 配合 VALUES(),version 字段默认上 BIGINT——这些组合背后都是具体冲突模式和性能权衡。漏掉任意一环,线上就容易出现“数据莫名不对”这种最难复现的问题。

本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

热门AI工具

更多
豆包大模型

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

SkildArt
SkildArt Hot

SkildArt是一款AI文本写作工具,一站式 AI 视觉创作平台。

UpDream
UpDream Hot

一款AI视频创作工具,主要用于哔哩哔哩推出的自研AI视频创作工具,适合需要提升相关任务效率的用户。

WorkBuddy

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

二狗PPT
二狗PPT Hot

一款AI演示文稿工具,主要用于专为中式职场打造的AI PPT生成工具,适合需要提升相关任务效率的用户。

Loomy
Loomy Hot

一款AI工具,主要用于科大讯飞发布的桌面级 AI 助理,比 OpenClaw 更易用、更安全!,适合需要提升相关任务效率的用户。

PixPix
PixPix Hot

PixPix是一款面向电商视觉生产的AI商品图生成工具。

切问学术

切问学术是一款AI论文写作工具,复旦大学NLP团队推出的AI学术智能体。

DeepSeek

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

相关专题

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

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

3883

2023.10.12

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

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

831

2023.10.27

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

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

1009

2024.02.23

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

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

5721

2024.03.06

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

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

2663

2024.03.06

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

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

5700

2024.04.07

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

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

7521

2024.04.29

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

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

1030

2024.04.29

LLVM自定义Pass怎么写
LLVM自定义Pass怎么写

本专题聚焦LLVM自定义Pass开发,整理Pass类结构、run()方法、PreservedAnalyses、CMake构建、插件注册、-load-pass-plugin加载和测试用例编写流程。

0

2026.09.30

热门下载

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

精品课程

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

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