SQL乐观锁是应用层通过版本号或时间戳实现的并发控制策略,核心为“先查后验、提交时校验”,不阻塞读写;适合读多写少、冲突概率低且允许重试的场景,如用户资料编辑、普通库存扣减、文章保存等。

SQL 乐观锁不是数据库内置的锁机制,而是一种通过应用层逻辑+数据库字段配合实现的并发控制策略,核心思想是“先查后验、提交时校验”,不阻塞读写,靠版本号或时间戳判断数据是否被修改过。
基于版本号(version)的乐观锁实现
在表中增加一个 version 字段(通常为整型,默认值为 0),每次更新数据时,把 version 加 1,并在 WHERE 条件中带上旧的 version 值:
UPDATE order SET status = 'paid', version = version + 1 WHERE id = 123 AND version = 5;
如果 SQL 影响行数为 0,说明 version 已被其他事务更新,当前操作失败,需重试或提示用户冲突。
- 版本号必须由数据库自增(如
version = version + 1),不能由应用层计算后传入,避免竞态 - 初始插入时 version 设为 0;首次更新成功后变为 1,后续依次递增
- 适合字段变更频繁、但冲突概率较低的业务,如订单状态流转、文章编辑保存
基于时间戳(updated_at)的乐观锁实现
用 updated_at 字段替代 version,更新时检查该字段是否仍等于查询时的值:
UPDATE product SET stock = 99, updated_at = NOW() WHERE id = 456 AND updated_at = '2024-05-20 10:30:15';
注意:必须确保 updated_at 精确到毫秒级(如 MySQL 的 DATETIME(3)),否则高并发下容易误判冲突。
- 时间戳方式对数据库类型敏感,部分数据库(如 PostgreSQL)支持
CURRENT_TIMESTAMP(6)高精度 - 不适合依赖定时任务批量更新的场景,因为时间戳可能被非业务逻辑覆盖
- 调试和排查更直观——可直接看到“谁在什么时候改了数据”
乐观锁的典型适用场景
乐观锁不是万能的,它适用于“读多写少、冲突概率低、允许重试”的业务环节:
- 用户资料编辑:多个端(App/Web/小程序)可能同时打开同一份资料,但真正同时提交修改的概率很低
- 库存扣减(非秒杀级):普通电商下单场景,配合重试+队列可降低超卖风险
- 内容管理系统中的文章保存:编辑器自动保存、手动提交等操作并存,需防止后提交者覆盖前提交者的修改
- 配置项更新:后台管理平台修改系统参数,人工操作频次低,强一致性要求不高但需防误覆盖
不建议用乐观锁的场景
以下情况更适合用悲观锁(如 SELECT ... FOR UPDATE)或分布式锁:
- 高频写入且冲突密集,比如秒杀库存扣减(乐观锁重试成本高,易引发雪崩)
- 业务逻辑复杂,需在事务中多次读取中间状态再决策,无法压缩为单条带条件 UPDATE
- 数据库不支持原子 version 自增(如某些 NoSQL 或老版本 MySQL 表结构限制)
- 前端无重试机制或不允许用户感知冲突(如支付确认页提交失败直接报错体验差)
关键不在“锁”,而在“协作预期”——乐观锁默认大家互不干扰,只在交集处做一次轻量校验。设计时要结合业务节奏、错误容忍度和重试能力综合判断。










