SQL 版本锁(乐观锁 / 版本号锁)以及C#中版本号用法

3 阅读5分钟

第一篇

我用最简单、最通俗、开发必懂的方式给你讲 SQL 版本锁(乐观锁 / 版本号锁),你一看就彻底明白。

一句话核心

版本锁 = 用一个“版本号”判断数据有没有被别人改过

  • 没人改 → 允许更新
  • 有人改 → 拒绝更新,防止数据错乱

它到底是什么?

版本锁就是乐观锁的最常用实现方式。

原理超级简单: 给表加一个字段(version),每次更新就 +1。 更新前先对比版本号,不一致就说明数据被改过了。


超通俗例子(你马上懂)

你要改一条数据:

  1. 你查询数据 → 拿到 version=5
  2. 你准备更新
  3. 如果别人先改了这条数据 → version 变成 6
  4. 你再提交更新时:
    WHERE id=1 AND version=5
    
    因为版本已经是 6 了 → 更新失败! 系统就知道:数据被改过了,不能覆盖!

这就是 版本锁


为什么要用它?

防止多人同时改同一条数据,导致数据被覆盖、错乱。

比如:

  • 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 FROMWHERE id=1;

3. 更新时必须匹配版本号 + 版本+1

UPDATESET 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. 一句话告诉你怎么用

  1. 数据库加个 version 字段
  2. 实体加 [ConcurrencyCheck]
  3. 更新时 EF Core 自动用版本锁
  4. 并发冲突自动捕获,数据永远不会乱、不会覆盖