C# 中怎么解决数据库并发

发布时间:2021-07-07 15:08:15 作者:Leah
来源:亿速云 阅读:294
# C# 中怎么解决数据库并发

## 目录
1. [并发问题概述](#并发问题概述)
2. [数据库并发控制机制](#数据库并发控制机制)
3. [C# 中的事务处理](#c-中的事务处理)
4. [乐观并发控制实现](#乐观并发控制实现)
5. [悲观并发控制实现](#悲观并发控制实现)
6. [EF Core 并发解决方案](#ef-core-并发解决方案)
7. [分布式系统并发挑战](#分布式系统并发挑战)
8. [性能优化与最佳实践](#性能优化与最佳实践)
9. [实际案例解析](#实际案例解析)
10. [总结与展望](#总结与展望)

---

## 并发问题概述
(约800字)

### 什么是数据库并发
数据库并发指多个事务同时访问和修改相同数据资源的现象。在C#应用程序中,当多个用户/线程同时执行以下操作时可能引发并发问题:
- 读取-修改-写入序列
- 批量数据更新
- 长时间运行的事务

### 典型并发问题表现
1. **丢失更新**(Lost Update)
```csharp
// 线程A读取X=100
// 线程B读取X=100
// 线程A写入X=200
// 线程B写入X=150 → A的更新被覆盖
  1. 脏读(Dirty Read)
// 事务A修改X但未提交
// 事务B读取到未提交的X值
  1. 不可重复读(Non-repeatable Read)
// 事务A首次读取X=100
// 事务B修改X=200并提交
// 事务A再次读取X得到不同结果
  1. 幻读(Phantom Read)
// 事务A查询得到N条记录
// 事务B插入新记录
// 事务A相同查询得到N+1条记录

并发问题的影响维度


数据库并发控制机制

(约1000字)

1. 乐观并发控制(Optimistic Concurrency)

适用场景:读多写少、冲突概率低的环境

graph TD
    A[读取数据] --> B[修改数据]
    B --> C{提交时验证}
    C -->|版本匹配| D[提交更新]
    C -->|版本不匹配| E[回滚/重试]

2. 悲观并发控制(Pessimistic Concurrency)

适用场景:写密集、冲突概率高的环境

graph TD
    A[获取锁] --> B[执行操作]
    B --> C[释放锁]

3. 多版本并发控制(MVCC)

现代数据库(如SQL Server、PostgreSQL)的核心机制

隔离级别对比

隔离级别 脏读 不可重复读 幻读 C#设置方式
Read Uncommitted IsolationLevel.ReadUncommitted
Read Committed × IsolationLevel.ReadCommitted
Repeatable Read × × IsolationLevel.RepeatableRead
Serializable × × × IsolationLevel.Serializable

C# 中的事务处理

(约1200字)

ADO.NET 事务实现

using (SqlConnection conn = new SqlConnection(connectionString))
{
    conn.Open();
    SqlTransaction transaction = conn.BeginTransaction(IsolationLevel.Serializable);
    
    try
    {
        using (SqlCommand cmd = new SqlCommand("UPDATE Accounts SET Balance = Balance - 100 WHERE Id = 1", conn, transaction))
        {
            cmd.ExecuteNonQuery();
        }
        
        transaction.Commit();
    }
    catch
    {
        transaction.Rollback();
        throw;
    }
}

TransactionScope 用法

using (TransactionScope scope = new TransactionScope(
    TransactionScopeOption.Required,
    new TransactionOptions { IsolationLevel = IsolationLevel.RepeatableRead }))
{
    // 跨多个连接的事务
    using (SqlConnection conn1 = new SqlConnection(connStr1))
    using (SqlConnection conn2 = new SqlConnection(connStr2))
    {
        // 操作多个数据库
    }
    
    scope.Complete();
}

异步事务处理

async Task TransferFundsAsync()
{
    using (SqlConnection conn = new SqlConnection(connStr))
    {
        await conn.OpenAsync();
        using (SqlTransaction transaction = conn.BeginTransaction())
        {
            try
            {
                var cmd = new SqlCommand("UPDATE...", conn, transaction);
                await cmd.ExecuteNonQueryAsync();
                await transaction.CommitAsync();
            }
            catch
            {
                await transaction.RollbackAsync();
                throw;
            }
        }
    }
}

乐观并发控制实现

(约1500字)

版本号模式

  1. 添加版本字段
ALTER TABLE Products ADD RowVersion rowversion;
  1. C# 检测代码
var product = db.Products.Find(id);
product.Price = newPrice;

try
{
    db.SaveChanges(); // 自动验证RowVersion
}
catch (DbUpdateConcurrencyException ex)
{
    var entry = ex.Entries.Single();
    var dbValues = entry.GetDatabaseValues();
    
    // 处理策略选项
    if (overwrite)
    {
        entry.OriginalValues.SetValues(dbValues);
        db.SaveChanges();
    }
    else
    {
        // 提示用户冲突
    }
}

时间戳替代方案

[Timestamp]
public byte[] Timestamp { get; set; }

自定义验证逻辑

var originalPrice = db.Products
    .AsNoTracking()
    .Where(p => p.Id == id)
    .Select(p => p.Price)
    .FirstOrDefault();

var rows = db.Products
    .Where(p => p.Id == id && p.Price == originalPrice)
    .Update(p => new Product { Price = newPrice });

if (rows == 0)
{
    throw new DbConcurrencyException();
}

悲观并发控制实现

(约1200字)

SQL Server 锁提示

// 使用UPDLOCK保持更新锁
var sql = @"SELECT * FROM Orders WITH (UPDLOCK) 
            WHERE OrderId = @id";
            
using (var cmd = new SqlCommand(sql, conn, transaction))
{
    cmd.Parameters.AddWithValue("@id", orderId);
    // 处理订单...
}

应用层锁实现

// 使用MemoryCache实现简单锁
private static readonly MemoryCache _lockCache = new MemoryCache(new MemoryCacheOptions());

public bool AcquireLock(string key, TimeSpan timeout)
{
    return _lockCache.TryAdd(key, DateTime.Now, timeout);
}

public void ReleaseLock(string key)
{
    _lockCache.Remove(key);
}

分布式锁模式

// 使用Redis实现分布式锁
var redis = ConnectionMultiplexer.Connect("localhost");
var db = redis.GetDatabase();

var token = Guid.NewGuid().ToString();
if (db.LockTake("order_lock", token, TimeSpan.FromSeconds(30)))
{
    try
    {
        // 执行关键操作
    }
    finally
    {
        db.LockRelease("order_lock", token);
    }
}

EF Core 并发解决方案

(约1000字)

并发令牌配置

modelBuilder.Entity<Product>()
    .Property(p => p.RowVersion)
    .IsRowVersion()
    .IsConcurrencyToken();

并发异常处理流程

graph LR
    A[SaveChanges] --> B{并发冲突?}
    B -->|是| C[DbUpdateConcurrencyException]
    C --> D[重载实体状态]
    D --> E[合并/覆盖策略选择]
    E --> F[重试机制]
    B -->|否| G[提交成功]

批量操作并发控制

// 使用ExecuteUpdate避免读-修改-写
context.Products
    .Where(p => p.Category == "Electronics")
    .ExecuteUpdate(p => p.SetProperty(x => x.Price, x => x.Price * 1.1m));

分布式系统并发挑战

(约800字)

CAP理论的影响

解决方案选型

方案 适用场景 C#实现示例
两阶段提交(2PC) 强一致性要求 MSDTC分布式事务
Saga模式 长业务流程 Masstransit+Event Sourcing
乐观并发+重试 最终一致性 Polly重试策略
CRDTs 无冲突复制数据类型 RedisGears模块

性能优化与最佳实践

(约800字)

并发控制选择矩阵

因素 乐观并发 悲观并发
冲突频率
数据量 中小
响应时间要求 敏感 不敏感
系统架构 分布式 单体

监控指标

// 使用性能计数器监控
var pc = new PerformanceCounter(
    "SQLServer:Latches", 
    "Latch Waits/sec", 
    "_Total");
    
float latchWaits = pc.NextValue();

实际案例解析

(约1000字)

电商库存扣减案例

// 使用存储过程实现原子操作
CREATE PROCEDURE DeductInventory
    @ProductId INT,
    @Quantity INT,
    @Version ROWVERSION
AS
BEGIN
    UPDATE Products 
    SET Stock = Stock - @Quantity
    WHERE ProductId = @ProductId 
    AND RowVersion = @Version
    
    RETURN @@ROWCOUNT
END

// C#调用
var rows = db.Database.ExecuteSqlRaw(
    "EXEC DeductInventory @p0, @p1, @p2",
    productId, quantity, version);

总结与展望

(约500字)

技术选型建议

  1. 优先考虑乐观并发
  2. 慎用高隔离级别
  3. 分布式环境采用最终一致性

未来发展趋势

”`

推荐阅读:
  1. c# await,async执行流案例分析
  2. 浅谈C#在网络波动时防重复提交的方法

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

上一篇:c#中怎么调用Oracle带有游标的存储过程

下一篇:c#中怎么获取当前日期时间

相关阅读

您好,登录后才能下订单哦!

密码登录
登录注册
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》