第一篇
我用最简单、最通俗、开发必懂的方式给你讲 SQL 版本锁(乐观锁 / 版本号锁),你一看就彻底明白。
一句话核心
版本锁 = 用一个“版本号”判断数据有没有被别人改过
- 没人改 → 允许更新
- 有人改 → 拒绝更新,防止数据错乱
它到底是什么?
版本锁就是乐观锁的最常用实现方式。
原理超级简单: 给表加一个字段(version),每次更新就 +1。 更新前先对比版本号,不一致就说明数据被改过了。
超通俗例子(你马上懂)
你要改一条数据:
- 你查询数据 → 拿到 version=5
- 你准备更新
- 如果别人先改了这条数据 → version 变成 6
- 你再提交更新时:
因为版本已经是 6 了 → 更新失败! 系统就知道:数据被改过了,不能覆盖!WHERE id=1 AND version=5
这就是 版本锁。
为什么要用它?
防止多人同时改同一条数据,导致数据被覆盖、错乱。
比如:
- A 读取余额 100
- B 读取余额 100
- A 扣 20 → 80
- B 扣 30 → 70 最终余额变成 70,A 的修改被吞了!
用版本锁就能避免这种灾难。
数据表怎么加版本锁?(直接照抄)
1. 加一个版本字段
ALTER TABLE 表名 ADD version INT NOT NULL DEFAULT 0;
2. 查询时带上版本号
SELECT id, money, version FROM 表 WHERE id=1;
3. 更新时必须匹配版本号 + 版本+1
UPDATE 表
SET money = 80, version = version + 1
WHERE id=1 **AND version=5**
- 版本匹配 → 更新成功
- 版本不匹配 → 影响行数 0 → 表示数据已被修改
版本锁的特点(最重要)
- 不上锁、不阻塞(所以叫乐观锁)
- 性能极好
- 靠版本号判断是否被修改
- 高并发场景必用
- ASP.NET、EF Core、MyBatis、Django 全都自带支持
最关键总结(你记这句就够)
版本锁 = 用 version 字段判断数据是否被修改 更新必须带上旧版本号 不一致就说明数据被改过 → 拒绝更新
第二篇
我直接给你三份可复制、可直接上线用的完整代码: SQL 表设计 + 原生 SQL 版本锁写法 + EF Core 完整版本锁配置。
一、SQL 表设计(加版本锁字段)
-- 给表加 version 字段(乐观锁·版本号)
ALTER TABLE 你的表名
ADD version INT NOT NULL DEFAULT 0;
示例表(方便你理解):
CREATE TABLE product (
id INT PRIMARY KEY,
name VARCHAR(50),
stock INT, -- 库存
price DECIMAL(10,2),
version INT NOT NULL DEFAULT 0 -- 版本锁字段
);
二、原生 SQL 版本锁写法(最标准)
1. 先查询(必须把 version 带出来)
SELECT id, stock, version
FROM product
WHERE id = 1;
假设查到:
- stock = 100
- version = 3
2. 更新时必须带版本号(核心)
UPDATE product
SET
stock = 90, -- 扣减库存
version = version + 1 -- 版本+1
WHERE
id = 1
AND version = 3; -- 只在版本没变时才更新
结果判断
- 影响行数 = 1 → 更新成功
- 影响行数 = 0 → 版本已变,更新失败(并发冲突)
这就是版本锁。
三、EF Core 完整版本锁(最常用)
1. 实体类加 Version 标记
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
public int Stock { get; set; }
public decimal Price { get; set; }
// 版本锁(EF Core 自动管理)
[ConcurrencyCheck]
[Column("version")]
public int Version { get; set; }
}
2. Fluent API 配置(更规范)
modelBuilder.Entity<Product>(entity =>
{
entity.ToTable("product");
entity.Property(p => p.Version)
.IsConcurrencyToken(); // 设为并发令牌(版本锁)
});
3. 业务代码(自动处理版本冲突)
using (var db = new YourDbContext())
{
var product = db.Product.FirstOrDefault(p => p.Id == 1);
// 业务修改
product.Stock -= 10;
try
{
await db.SaveChangesAsync();
// 成功:版本自动+1,完全不用你管
}
catch (DbUpdateConcurrencyException ex)
{
// 失败:别人已经改过了 → 版本冲突
// 这里写重试/提示用户
}
}
EF Core 自动帮你做的事
- 查询时自动读取
Version - 更新时自动生成
AND version = @old_version - 冲突时直接抛并发异常,安全、省心、标准
四、高并发安全更新模板(直接复制用)
public async Task<bool> ReduceStock(int productId, int count)
{
using var db = new YourDbContext();
for (int retry = 0; retry < 3; retry++) // 最多重试3次
{
try
{
var p = await db.Product.FindAsync(productId);
if (p.Stock < count) return false;
p.Stock -= count;
await db.SaveChangesAsync();
return true;
}
catch (DbUpdateConcurrencyException)
{
// 版本冲突 → 清空缓存,重试
db.ChangeTracker.Clear();
}
}
return false;
}
超简总结
- 版本锁 = 用 version 字段防并发覆盖
- SQL:更新必须带
AND version = 旧版本号 - EF Core:加
[ConcurrencyCheck]自动搞定 - 高并发:捕获异常 + 重试即可
第三篇
1. SQL Server 加版本锁(直接运行)
-- 给你的表加【版本锁字段】
ALTER TABLE 你的表名
ADD version INT NOT NULL DEFAULT 0;
2. 原生 SQL 版本锁更新模板(高并发安全)
-- 1. 查询数据(必须拿 version)
SELECT id, name, stock, version
FROM 你的表名
WHERE id = @id;
-- 2. 安全更新(版本不变才允许更新)
UPDATE 你的表名
SET
stock = stock - 1, -- 你的业务修改
version = version + 1
WHERE
id = @id
AND version = @旧版本号; -- 核心:版本锁
3. EF Core 完整版本锁(你现在项目直接用)
实体类
public class 你的表名
{
public int Id { get; set; }
public string Name { get; set; }
public int Stock { get; set; }
// 版本锁(自动管理)
[ConcurrencyCheck]
public int Version { get; set; }
}
高并发安全更新方法(直接复制到你的项目)
/// <summary>
/// 带版本锁 + 自动重试的安全更新
/// </summary>
public async Task<bool> UpdateWithVersionLock(int id)
{
int retryCount = 3; // 自动重试3次,高并发必加
using (var db = new YourDbContext())
{
for (int i = 0; i < retryCount; i++)
{
try
{
// 查询
var model = await db.你的表名.FindAsync(id);
if (model == null) return false;
// 你的业务修改
// model.Stock -= 1;
// 保存(自动带版本锁)
await db.SaveChangesAsync();
return true;
}
catch (DbUpdateConcurrencyException)
{
// 版本冲突 → 清空缓存,重试
db.ChangeTracker.Clear();
// 最后一次还失败 → 返回失败
if (i == retryCount - 1)
return false;
}
}
}
return false;
}
4. 一句话告诉你怎么用
- 数据库加个
version字段 - 实体加
[ConcurrencyCheck] - 更新时 EF Core 自动用版本锁
- 并发冲突自动捕获,数据永远不会乱、不会覆盖