在高并发系统中,如何保证数据一致性,又尽量提升系统性能,是一个绕不开的话题。乐观锁正是在这种背景下被广泛使用的一种并发控制机制。本文将从概念、原理、实现方式、应用场景和实战案例等方面,带你系统理解什么是乐观锁。
文章目录
-
- 一、什么是乐观锁?
-
- [1. 乐观锁的定义](#1. 乐观锁的定义)
- [2. 通俗理解](#2. 通俗理解)
- 二、乐观锁的工作原理
- 三、乐观锁的主要实现方式
-
- [1. 基于版本号(Version)实现](#1. 基于版本号(Version)实现)
- [2. 基于时间戳实现](#2. 基于时间戳实现)
- [3. CAS(Compare And Swap)机制](#3. CAS(Compare And Swap)机制)
- [四、乐观锁 vs 悲观锁](#四、乐观锁 vs 悲观锁)
- 五、乐观锁的典型使用场景
-
- [1. 读多写少系统](#1. 读多写少系统)
- [2. 高并发但低冲突场景](#2. 高并发但低冲突场景)
- [3. 分布式系统](#3. 分布式系统)
- 六、实战案例:库存扣减
-
- [1. 不安全写法](#1. 不安全写法)
- [2. 基于版本号的乐观锁实现](#2. 基于版本号的乐观锁实现)
- 七、乐观锁的优缺点分析
- 八、使用乐观锁的注意事项
-
- [1. 控制重试次数](#1. 控制重试次数)
- [2. 防止 ABA 问题](#2. 防止 ABA 问题)
- [3. 结合业务降级处理](#3. 结合业务降级处理)
- 九、面试常见问题
-
- [1. 什么是乐观锁?](#1. 什么是乐观锁?)
- [2. 乐观锁适合什么场景?](#2. 乐观锁适合什么场景?)
- [3. CAS 和乐观锁关系?](#3. CAS 和乐观锁关系?)
- 参考

一、什么是乐观锁?
1. 乐观锁的定义
乐观锁(Optimistic Lock),是一种对并发冲突持"乐观态度"的锁机制。
它的核心思想是:
认为并发冲突发生的概率较低,操作时不加锁,提交时再校验。
如果发现数据被修改过,就重试或失败处理。
2. 通俗理解
乐观锁可以理解为:
- "先干活,后检查"
- "出问题再重来"
就像写文档时:
先编辑,保存时提示冲突再处理。
不会一开始就锁住文件。
二、乐观锁的工作原理
乐观锁的一般执行流程:
- 读取数据
- 记录版本号/状态值
- 修改数据
- 提交时校验版本
- 校验成功则更新,否则失败重试
示意流程:

本质上:
通过校验机制,而不是阻塞机制保证一致性。
三、乐观锁的主要实现方式
1. 基于版本号(Version)实现
这是最常见的乐观锁方案。
数据库表结构示例:
sql
ALTER TABLE product ADD version INT;
更新时校验版本:
sql
UPDATE product
SET stock = stock - 1, version = version + 1
WHERE id = 1 AND version = 3;
特点:
- 只有版本匹配才会更新成功
- 否则返回 0 行受影响
- 调用方决定是否重试
2. 基于时间戳实现
使用时间字段判断数据是否被修改。
示例:
sql
WHERE id = 1 AND update_time = '2026-02-14 10:00:00'
本质与版本号类似,但精度依赖时间戳。
3. CAS(Compare And Swap)机制
在 Java 并发包中,大量使用 CAS 实现乐观锁。
示例:
java
AtomicInteger count = new AtomicInteger(0);
count.compareAndSet(0, 1);
CAS 逻辑:
如果当前值等于期望值,则更新,否则失败。
四、乐观锁 vs 悲观锁
理解乐观锁,必须和悲观锁对比来看。
| 对比维度 | 乐观锁 | 悲观锁 |
|---|---|---|
| 并发态度 | 乐观 | 悲观 |
| 是否加锁 | 否 | 是 |
| 冲突处理 | 重试 | 阻塞 |
| 性能 | 高 | 稳定 |
| 适合场景 | 读多写少 | 写多冲突多 |
一句话总结:
冲突少用乐观锁,冲突多用悲观锁。
五、乐观锁的典型使用场景
1. 读多写少系统
例如:
- 商品信息查询
- 配置中心
- 用户资料
读远大于写,冲突概率低。
2. 高并发但低冲突场景
例如:
- 抢优惠券
- 抽奖系统
- 秒杀预减库存
适合乐观锁提升吞吐量。
3. 分布式系统
在微服务环境中,加锁成本高,乐观锁更常用。
六、实战案例:库存扣减
1. 不安全写法
java
stock = stock - 1;
可能导致超卖。
2. 基于版本号的乐观锁实现
sql
UPDATE goods
SET stock = stock - 1,
version = version + 1
WHERE id = 1 AND version = #{version};
Java 伪代码:
java
while (true) {
Goods goods = query();
boolean success = update(goods);
if (success) break;
}
失败则重试。
七、乐观锁的优缺点分析
优点
- 并发性能高
- 无阻塞
- 吞吐量大
- 适合分布式系统
缺点
- 实现复杂
- 重试成本高
- 冲突多时性能下降
- 可能出现 ABA 问题
八、使用乐观锁的注意事项
1. 控制重试次数
避免无限重试导致 CPU 飙高。
建议设置最大重试次数。
2. 防止 ABA 问题
ABA:值变了又变回原值。
解决方案:
- 加版本号
- 使用带时间戳的标记
3. 结合业务降级处理
多次失败时:
- 返回失败
- 排队处理
- 转人工
避免系统雪崩。
九、面试常见问题
1. 什么是乐观锁?
答:
不加锁,通过版本校验保证一致性。
2. 乐观锁适合什么场景?
答:
读多写少、冲突少场景。
3. CAS 和乐观锁关系?
答:
CAS 是乐观锁的底层实现方式之一。