在多线程或多用户并发访问数据库时,为保证数据的一致性与完整性 ,并发控制机制 尤为重要。其中,乐观锁(Optimistic Lock)与悲观锁(Pessimistic Lock)是两种经典的并发控制策略。本文将系统介绍两者的概念、适用场景、实现方式与优缺点。
1 悲观锁(Pessimistic Lock)
1.1 概念
悲观锁是一种对并发操作 持"悲观态度 "的机制,它认为数据在高并发环境下很可能会被其他线程修改 ,因此在数据访问期间直接++加锁++ ,++防止其他线程读写++,确保操作的安全性。
1.2 实现方式
-
数据库层面加锁 :如使用 SQL 的
SELECT ... FOR UPDATE,会锁定查询的记录 ,直到事务提交或回滚。BEGIN;
SELECT * FROM product WHERE id=1 FOR UPDATE;
-- 执行更新操作
UPDATE product SET stock = stock - 1 WHERE id = 1;
COMMIT; -
Java 中的 synchronized、ReentrantLock 等属于悲观锁机制的典型应用。
1.3 适用场景
-
写操作频繁,冲突概率高;
-
对数据一致性要求极高,不能容忍冲突后回滚;
-
适用于传统数据库事务强一致的场景。
2 乐观锁(Optimistic Lock)
2.1 概念
乐观锁是一种对并发操作持"乐观态度 "的机制,它认为++数据冲突的概率较低++ ,因此不加锁 ,只在更新数据时检查是否有冲突。
2.2 实现方式
最常见的是使用 **版本号(version)或时间戳(timestamp)**进行并发控制。
-
读取数据时同时读取版本号;
-
更新时检查当前版本号是否与数据库中一致;
-
一致则更新,并将版本号加1;否则表示有并发冲突,放弃更新或重试。
2.3 适用场景
-
读多写少,冲突概率低;
-
业务可容忍失败重试机制;
-
适用于分布式系统或无锁场景,如前端乐观更新、NoSQL 等。
3 乐观锁与悲观锁的对比
| 项目 | 乐观锁 | 悲观锁 |
|---|---|---|
| 加锁方式 | 不加锁,通过版本控制 | 加锁,阻止其他线程操作 |
| 并发性能 | 高,适合读多写少 | 低,适合写多读少 |
| 冲突处理 | 检测冲突后重试或报错 | 阻止冲突发生 |
| 数据一致性 | 可接受一定冲突 | 强一致性 |
| 适用场景 | 高并发、分布式、响应优先 | 单体服务、事务控制严格 |
在实际项目中,悲观锁适用于高冲突、高一致性需求的场景 ,而乐观锁适用于高并发、低冲突、性能优先的业务逻辑。两者没有绝对的优劣,关键是根据业务特点和架构设计做出合理权衡。