您好,登录后才能下订单哦!
密码登录
登录注册
点击 登录注册 即表示同意《亿速云用户服务条款》
# 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的更新被覆盖
// 事务A修改X但未提交
// 事务B读取到未提交的X值
// 事务A首次读取X=100
// 事务B修改X=200并提交
// 事务A再次读取X得到不同结果
// 事务A查询得到N条记录
// 事务B插入新记录
// 事务A相同查询得到N+1条记录
(约1000字)
适用场景:读多写少、冲突概率低的环境
graph TD
A[读取数据] --> B[修改数据]
B --> C{提交时验证}
C -->|版本匹配| D[提交更新]
C -->|版本不匹配| E[回滚/重试]
适用场景:写密集、冲突概率高的环境
graph TD
A[获取锁] --> B[执行操作]
B --> C[释放锁]
现代数据库(如SQL Server、PostgreSQL)的核心机制
隔离级别 | 脏读 | 不可重复读 | 幻读 | C#设置方式 |
---|---|---|---|---|
Read Uncommitted | ✓ | ✓ | ✓ | IsolationLevel.ReadUncommitted |
Read Committed | × | ✓ | ✓ | IsolationLevel.ReadCommitted |
Repeatable Read | × | × | ✓ | IsolationLevel.RepeatableRead |
Serializable | × | × | × | IsolationLevel.Serializable |
(约1200字)
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;
}
}
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字)
ALTER TABLE Products ADD RowVersion rowversion;
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字)
// 使用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);
}
}
(约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字)
方案 | 适用场景 | 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字)
”`
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。