惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

MongoDB | Blog
MongoDB | Blog
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
小众软件
小众软件
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
博客园 - Franky
博客园 - 聂微东
V
Visual Studio Blog
I
InfoQ
罗磊的独立博客
Security Latest
Security Latest
G
Google Developers Blog
博客园_首页
P
Proofpoint News Feed
T
Threat Research - Cisco Blogs
D
DataBreaches.Net
PCI Perspectives
PCI Perspectives
Forbes - Security
Forbes - Security
P
Proofpoint News Feed
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
Know Your Adversary
Know Your Adversary
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Recent Commits to openclaw:main
Recent Commits to openclaw:main
The Cloudflare Blog
AWS News Blog
AWS News Blog
Latest news
Latest news
T
Tailwind CSS Blog
P
Palo Alto Networks Blog
Hugging Face - Blog
Hugging Face - Blog
云风的 BLOG
云风的 BLOG
Cyberwarzone
Cyberwarzone
T
The Exploit Database - CXSecurity.com
WordPress大学
WordPress大学
Recorded Future
Recorded Future
A
Arctic Wolf
V
Vulnerabilities – Threatpost
Security Archives - TechRepublic
Security Archives - TechRepublic
宝玉的分享
宝玉的分享
人人都是产品经理
人人都是产品经理
月光博客
月光博客
有赞技术团队
有赞技术团队
P
Privacy & Cybersecurity Law Blog
Scott Helme
Scott Helme
美团技术团队
Hacker News - Newest:
Hacker News - Newest: "LLM"
Jina AI
Jina AI
N
News and Events Feed by Topic
Attack and Defense Labs
Attack and Defense Labs

博客园_首页

Linux实操--组管理、权限管理和定时任务 Java + EasyExcel 实现单个接口导出多个Excel Mem0 源码解析系列(二):提示词工程的深度剖析 Openclaw TaskFlow究竟是什么?和普通Skill技能有什么区别 博文阅读密码验证 - 博客园 嘉立创开源:应该是全网MicroPython教程最多的开发板 Hermes Agent 集成实践:从协议到生产 2026年AI编程工具横评:Cursor、Codex、Claude Code、Zed、Windsurf Java程序员必看的RAG入门教程 2026 AI效率神器:Superpowers + Claude Code 保姆级教程 本地大模型部署全攻略:从 0 到 1 玩转 Ollama 【从0到1构建一个ClaudeAgent】内存管理-上下文压缩 .NET 高级开发 | 设计、实现一个事件总线框架 电子小白入门之NE555 3. WorkBuddy:隐藏玩法,一键召唤专家,让 AI 以"专家身份"给你干活 和AI一起搞事情#3:Claude Teammate 游戏开发翻车实录 【OpenClaw】通过 Nanobot 源码学习架构---(7)Memory C# .NET 周刊|2026年3月3期 我在 Debian 11 上把 K8s 单机搭起来了,过程没你想的那么顺(/opt 目录版) 深度学习进阶(七)Data-efficient Image Transformer CLI+Skill搭建浏览器AI自动化框架,告别一切重复枯燥任务 告别Token账单无底洞:OpenClaw本地部署,重塑企业数据主权的唯一解 FastAPI+Vue:文件分片上传+秒传+断点续传,这坑我帮你踩平了! SBTI 爆火后,我做了个程序员版的 CBTI。。已开源 + 附开发过程 多模态检索开始进入工程期:用 Sentence Transformers 搭建可落地的 Multimodal RAG 100多行代码实现一个最简单的Agent(用ReAct) Claude Code 通关手册(八):推荐 5 个 Hooks,代码质量提升 3 倍 老板:“有人截图了!”。安全部门:“收到,马上查暗水印!” - why技术 技术之外,皆是人间 C#/.NET/.NET Core技术前沿周刊 | 第 69 期(2026年4.01-4.12) Snack JSONPath 项目架构分析 Claude Code Buddy 小析:一个非核心功能,如何体现产品的细节完成度 AI新时代下的图床管理方案-Cloudflare图床+MCP+Skills方案指南 化繁为简:顺丰速运App如何通过 HarmonyOS SDK实现专业级空间测量 从零实现富文本编辑器#13-React非编辑节点的内容渲染 AI开发-python-langchain框架(3-23-OpenAI Functions风格Tool Calling智能助手) .NET + AI 进阶实战:基于类的技能开发 - 打造可治理的 Agent 能力模块 【从0到1构建一个ClaudeAgent】规划与协调-技能 上周热点回顾(4.6-4.12) 电子小白的工具三件套:面包板、杜邦线、万能板 单表五亿数据的查询优化 | Mysql、StarRocks 2. WorkBuddy:从“我是谁”到“帮我干活” C# 如何减少代码运行时间:7 个实战技巧 基于HelixToolkit.SharpDX 渲染3D模型 - 笺上知微 从零开始的双臂具身VLA起源及现阶段发展综述 - SkyXZ 记对 xonsh shell 的使用, 脚本编写, 迁移及调优 - pluvium27 受够了Vibe Coding的失控?换个起点,让AI事半功倍 从开始配置漏洞环境到漏洞复现流程 - 難しい 关于10年工作经验的程序员对OpenClaw的实战经验分享以及看法 - 虚无境 Any metadata 的内存布局 C# .NET 周刊|2026年3月2期 - InCerry 我帮你测过了,测试圈排名第二的 Skill 依然很牛逼 Skill Discovery | 无监督技能发现的经典工作总结 - MoonOut PbootCMS 网站内容数量多导致访问慢?这些实用优化方案帮你提速! - 家兴网络技术工作室 上下文工程是什么?过时了么?一文讲明白! - 一枫说码 网站漏洞怎么发现并修复?一篇实用指南(附完整流程) - 家兴网络技术工作室 开了 TUN 模式还是直连?90% 的人都踩过这个坑 Github日报|2026年04月12日 - AI一族 AScript扩展多种脚本语言 - rockey627 AI 学习笔记:Agent 的记忆机制 你能被装进一个文件里吗?——7 万人把同事"蒸馏"成了 AI - 我没有三颗心脏 Claude Code 通关手册(七):给 AI 装上技能包——Skills 完全指南 - 暮色之狐 在浏览器中快速编辑代码:VSCode Web 集成实践 - Newbe36524 蒸馏自己 skill?基于 Deepseek 的蒸馏器,丐版蒸馏方式,简单便捷 - To_Carpe_Diem Spring AI Aliababa和AgentScope,哪个更好? - 苏三说技术 Etsy 把 1000 个 MySQL 分片迁进 Vitess:425TB 数据背后的真正问题不是性能,而是运维规模 MicroPython LVGL基础知识和概念:底层渲染与性能优化 - FreakStudio 数据库草图算法 Python 潮流周刊#146:CPython 引入 Rust 的进展 - 豌豆花下猫 最小生成树 - mofei1116 红日靶场七:从外网入口、容器逃逸到 AD 接管的完整利用链复盘 - YouDiscovered1t 分享四款开源且实用的 Kafka 管理工具 - 追逐时光者 vLLM 权重加载机制全解析:从挑战到理想架构 LCT 学习笔记 - ACehomoxue Avalonia UI 12.0.0 正式发布:架构演进和性能飞跃 - 张善友 当 AI Agent 把调用链拉长,延迟开始成为一门生意 conhost.exe 无法显示 U+2717 - 145a 太秀了,我把自己蒸馏成了 Skill!已开源 - 程序员鱼皮 ASP.NET Core 内存缓存实战:一篇搞懂该怎么配、怎么避坑 基于 Ghostty 带有分割标签页和为 Claude 编程设计的通知终端 - BugShare AI 焊死入口:教育的“操作系统级”重塑 - 郝hai 初级Java开发工程师使用sql脚本编写代码的过程是简单而且不糊涂 - CoderOilStation Claude Code通关手册(六):MCP协议完全指南 - 暮色之狐 边框灯光环绕动画特效实现指南 - Newbe36524 开源:子木蒸馏版的 SEO 审计工具 seo-audit-skill v1.0 我所理解的Python元模型 【从0到1构建一个ClaudeAgent】规划与协调-TodoWrite - 程序员Seven Claude 和 Codex 在审计 Skill 上性能差异探究 - ACai_sec AScript如何实现中文脚本引擎 - rockey627 【渗透测试】HTB Season10 Garfield 全过程wp - dynasty_chenzi Android 开发者为什么必须掌握 AI 能力?端侧视角下的技术变革 树状数组正确性证明 - AC-wyr 你的 AI 焦虑,可能比 AI 本身更危险——ATM 机没有消灭银行柜员,但恐慌消灭了你的判断力 - 我没有三颗心脏 一个拉胯的分库分表方案有多绝望?整个部门都在救火! - 冰河团队 动态规划入门必学之走方格问题 - Ofnoname PostgREST 与 PostgreSQL 角色权限配置全解析(生产级实践) - SheepDog1998 使用 UEFI 图形输出协议 GOP 在屏幕上显示图像的方法 - 阿源- Claude Code通关手册(五):组建你的AI专家团队,子代理系统 - 暮色之狐 一个程序员到架构师的催婚路之感悟(整整10年后的催婚相亲感悟) - MisterLip 用 Agent Skill 自动生成工作周报 - 赵康
【后端开发】(含图解与实例)乐观锁、悲观锁和分布式锁,做项目时到底该怎么选?
IronBro · 2026-04-25 · via 博客园_首页

前言

乐观锁、悲观锁、分布式锁,几乎是后端面试和项目实践里绕不开的三个概念。如果只看定义,它们似乎并不难理解:
乐观锁:假设并发冲突不严重,更新数据时再校验是否被别人改过;
悲观锁:假设并发冲突一定会发生,操作前先加锁,再执行后续逻辑;
分布式锁:在多服务节点部署的场景下,保证同一时刻只有一个节点能执行某段逻辑。
很多人背到这里,就觉得自己已经掌握了,但真正到了面试追问或者项目落地时,问题往往就开始了:

  • 乐观锁和悲观锁到底是在锁什么?
  • 分布式锁和前两者是同一类问题吗?
  • 库存扣减该用哪个?
  • 定时任务重复执行又该用哪个?
  • 分布式锁能不能替代乐观锁、悲观锁?

1 乐观锁:适合冲突少的更新场景

很多人第一次听到“乐观锁”时,会下意识觉得它是一种“真的加了锁”的机制。其实不是,乐观锁不强调“先锁住”,而强调“更新时确认数据还是不是我看到的那份数据”。

1.1 乐观锁到底在解决什么问题?

在并发场景下,多个请求可能同时修改同一份数据,如果没有任何控制,就容易出现覆盖更新的问题。
比如,商品库存原本是10:

  • 请求A读到库存是10
  • 请求B也读到库存是10
  • A扣减后写回9
  • B也扣减后写回9
    看起来两个请求都成功了,但实际上库存只少了1次,这就是典型的并发更新问题,导致数据的不一致性
    image

1.2 乐观锁通常怎么实现?

最常见的做法,是给数据加一个version版本号字段。
比如一条商品记录长这样:

id = 1   stock = 10   version = 3

当某个请求读到这条数据时,它不仅会读到库存,也会读到当前版本号。后续更新时,不是直接根据id去更新,而是带着这个version一起更新:

update product
set stock = stock - 1,
    version = version + 1
where id = 1
  and version = 3;

这条 SQL 的关键点不在stock - 1,而在:

and version = 3

只有当这条数据的版本号还是我最初读到的 3 时,我这次更新才允许成功。
如果更新成功,说明这期间没人动过这条数据。
如果更新失败,说明别的请求已经先一步改过了,你当前这次操作所基于的数据已经过期。

1.3 乐观锁实际例子

假设现在有一个“普通商品扣库存”的场景,库存初始值为

stock = 10    version = 5

这时请求A和请求B同时进来。
第一步,请求A和请求B都查询到

stock = 10    version = 5

然后两个请求都准备执行更新。
第二步,请求 A 先执行:

update product
set stock = 9,
    version = 6
where id = 1
  and version = 5;

执行成功,数据库中数据变成:

stock = 9    version = 6

第三步,接着请求 B 再执行:

update product
set stock = 9,
    version = 6
where id = 1
  and version = 5;

这时候就会失败,因为数据库中的version已经不是5,而是6了。
这就是乐观锁的本质:不是抢着先锁住资源,而是通过版本一致性校验,拒绝基于旧数据的更新。
image

2 悲观锁:适合高冲突、强一致场景

如果说乐观锁的思路是“先假设冲突不常发生”,
那悲观锁正好相反:它先假设冲突一定会发生,所以干脆在操作前先把资源锁住。
悲观锁的重点不是“更新失败后怎么补救”,而是在真正修改数据之前,就先阻止别人同时改。

2.1 悲观锁到底在解决什么问题?

悲观锁解决的依然是并发场景下对同一份数据的竞争更新问题。
但它和乐观锁的处理方式完全不同。
乐观锁是

  • 允许大家先一起读
  • 一起尝试更新
  • 最后根据版本号判断谁成功、谁失败

悲观锁是

  • 谁先拿到锁,谁先操作
  • 没拿到锁的人先等着
  • 等前一个操作完成后,后面的人再继续

所以从结果上看,悲观锁更像是在并发场景下,把对关键数据的修改强行串行化。

可以用一个账户余额扣减的例子来解释悲观锁。
假设一个账户当前余额是100元,现在同时来了两个扣款请求:

  • 请求A:扣80元
  • 请求B:扣30元

如果没有做好并发控制,就可能出现问题:

  • A读到余额100
  • B也读到余额100
  • A判断余额足够,扣减成功
  • B也判断余额足够,扣减成功

结果两个请求都通过了校验,总共扣了110元,账户上没有那么多钱,这样账户就出问题了。
image

2.2 悲观锁通常怎么实现?

在后端开发里,最常见的悲观锁实现,就是数据库的行锁

select * from account
where id = 1
for update;

这条SQL的核心是最后的:

for update

它表示:在当前事务提交之前,把这条记录锁住。后续如果其他事务也想对这条记录加锁或修改这条记录,就必须等待当前事务释放锁。

2.3 悲观锁实际例子

还是刚刚提到的余额扣减例子,如果应用悲观锁的话,会变成下面这样:
第一步,请求A先开启事务并加锁:

select balance from account
where id = 1
for update;

数据库把这条账户记录锁住。
第二步,请求B也来了,它如果也想对同一条账户记录执行for update,就会被阻塞,必须等待请求A提交事务。
第三步,请求A在事务里完成校验和更新,

  • 查到balance = 100
  • 判断100 >= 80,允许扣款
  • 执行更新,余额变成20
  • 提交事务
  • 释放锁

第四步,请求A释放锁后,请求B才能获得锁,才能进行查询操作,这时它再查余额,读到的已经是最新值20,然后会发现20 < 30,余额不足,从而扣款失败。
这样一来,系统就不会出现“两个请求都基于旧余额通过校验”的问题。
image

3 分布式锁:适合多节点之间抢执行权

讲到这里,很多人会自然地把分布式锁和乐观锁、悲观锁并列理解,觉得它们只是“不同类型的锁”。

但严格来说,它们并不完全在同一个维度上。
因为前两者主要解决的是:并发更新同一份数据时,如何保证结果正确。而分布式锁更关注的是:在多个服务实例同时运行的情况下,某个动作到底该由谁来执行

分布式锁很多时候锁的不是“某条数据库记录”,而是:

  • 某个任务的执行资格
  • 某段业务逻辑的处理权
  • 某个全局动作的唯一执行者

3.1 为什么单机锁到了分布式场景就不够用了?

在单机应用里,如果你想让一段代码同一时刻只能被一个线程执行,往往可以直接用synchronizedReentrantLock
这类本地锁的前提是,所有竞争者都在同一个进程、同一台机器里
但后端服务一旦真正上线,往往不会只部署一个实例。这时候问题就来了,比如你写了一个定时任务,代码里加了synchronized,你以为这样就能保证“同一时刻只执行一次”。但实际上:

  • 机器A上的线程能进来
  • 机器B上的线程也能进来
  • 它们彼此根本看不到对方的锁
    image

3.2 分布式锁通常怎么实现?

在工程里,分布式锁的实现方式不止一种,但最常见的是Redis分布式锁。
最基础的做法通常类似这样:

SET lock_key unique_value NX PX 30000
  • NX:只有当key不存在时才设置成功,这保证了只有一个实例能抢到锁
  • PX 30000:设置过期时间30秒,避免服务宕机后锁永远不释放
  • unique_value:锁的唯一标识,释放锁时要校验,避免把别人的锁删掉

分布式锁最关键的不是“加锁成功”本身,而是“锁的持有、过期、释放”都要设计清楚,因为它不像本地锁那样天然可靠,分布式锁涉及网络、超时、节点故障,所以边界问题会多很多。

3.3 分布式锁实际例子

用“定时任务只允许一个实例执行”这个经典例子来讲解:
假设系统部署了服务A和服务B两个实例,现在有一个定时任务,每天凌晨扫描超时订单并自动取消
如果没有分布式锁,那么在同一时刻,服务A会触发一次任务,服务B也会触发一次任务。
这样就可能出现:

  • 同一批订单被重复扫描
  • 同一笔退款逻辑被重复执行
  • 同一条通知被重复发送

很多人会说:“那我不是已经写了幂等了吗?”幂等当然很重要,但它解决的是“重复执行后结果尽量不出错”;
而分布式锁解决的是:从源头上减少重复执行,让同一时刻只有一个实例先进去做。他们解决问题的侧重点不一样。
一个典型流程通常是这样:
第一步,服务A和服务B同时触发任务,它们都准备执行“取消超时订单”,然后都去竞争同一把锁,锁名为:

cancel_timeout_order_task_lock

第二步,只有一个实例抢锁成功,假设服务A成功抢到了锁,服务A开始执行业务。
第三步,服务B因为没抢到锁,所以直接退出,或者稍后重试,直到服务A执行完成后释放锁,下一轮任务再重新竞争。
image

4 不同业务中到底怎么选锁?

写到这里,其实最重要的已经不是再去背“乐观锁是什么、悲观锁是什么、分布式锁是什么”了。真正决定你能不能把这类问题讲明白的,是你有没有意识到:这三种锁根本不是同一个维度上的技术点

很多人会把它们并排记忆,然后试图总结成“乐观锁轻一点,悲观锁重一点,分布式锁更高级一点”。
这种记法最容易在面试和项目里出问题,因为它会让你忽略一个关键事实:乐观锁、悲观锁主要解决的是数据竞争,分布式锁主要解决的是执行权竞争

类型 核心解决的问题 锁/控制的对象 常见实现 适合场景 主要优势 主要代价
乐观锁 并发更新同一份数据时,避免基于旧数据覆盖更新 数据版本一致性 version 版本号、CAS、时间戳 读多写少、冲突少的更新场景 并发性能好,不容易阻塞 冲突多时失败率高,需要重试
悲观锁 并发更新同一份关键数据时,保证强互斥和强一致 数据访问权 数据库行锁、select ... for update 写冲突高、强一致要求高的场景 逻辑直观,一致性更稳 阻塞明显,吞吐下降,可能死锁
分布式锁 多个服务节点同时竞争某个业务动作的执行权 某段业务逻辑的执行资格 Redis、ZooKeeper、数据库锁表/唯一约束辅助 定时任务单点执行、全局互斥、避免重复执行 能跨节点控制互斥 实现边界复杂,要处理超时、误删、故障等问题