fix: 每次写入创建新 SqlSugarClient 实例

根因分析:
- MarketDataRepository 使用注入的 ISqlSugarClient(Scoped)
- 多个 Task.Run 并发调用 SavePriceCacheAsync
- Storageable 操作使用同一个 SqlSugarScope 实例
- 连接在 await 边界被复用 → 冲突

正确方案:
- SavePriceCacheAsync 每次创建新的 SqlSugarClient 实例
- MySQL 连接池会复用底层 TCP 连接,性能开销很小
- 不再需要 SemaphoreSlim 锁

优点:
- 完全避免连接冲突
- 代码更简洁
- 并发写入无限制
This commit is contained in:
OpenClaw Agent 2026-03-25 02:37:18 +00:00
parent 39808c6d5d
commit cbe0ac9f4a
2 changed files with 8 additions and 11 deletions

View File

@ -54,7 +54,11 @@ public class MarketDataRepository : IMarketDataRepository
public async Task<bool> SavePriceCacheAsync(MarketPriceCache cache)
{
cache.Id = GenerateCacheKey(cache.Symbol, cache.AssetType);
var result = await _db.Storageable(cache).ExecuteCommandAsync();
// 使用新的 SqlSugarClient 实例,避免并发写入时连接冲突
// 连接池会复用底层连接,性能开销很小
using var db = SqlSugarConfig.GetSqlSugarClient();
var result = await db.Storageable(cache).ExecuteCommandAsync();
return result > 0;
}

View File

@ -22,15 +22,12 @@ public class MarketDataService : IMarketDataService
private readonly IOkxMarketService _okxService;
private readonly IMarketDataRepository _marketDataRepo;
// 静态内存缓存层(跨请求共享,避免并发数据库查询导致连接池冲突
// 静态内存缓存层(跨请求共享
private static readonly ConcurrentDictionary<string, (MarketPriceCache cache, DateTime expireAt)> _memoryCache = new();
// 静态 pending 请求字典(跨请求共享,防止并发请求同一股票)
private static readonly ConcurrentDictionary<string, Lazy<Task<MarketPriceResponse>>> _pendingPriceRequests = new();
// 静态写入锁(串行化数据库写入)
private static readonly SemaphoreSlim _writeLock = new(1, 1);
public MarketDataService(
ILogger<MarketDataService> logger,
ITencentMarketService tencentService,
@ -203,10 +200,10 @@ public class MarketDataService : IMarketDataService
ExpiredAt = expireAt
}, expireAt);
// 写入数据库缓存(串行化,避免连接冲突)
// 写入数据库缓存(后台执行,失败不影响主流程)
// SavePriceCacheAsync 每次创建新的 SqlSugarClient 实例,避免连接冲突
_ = Task.Run(async () =>
{
await _writeLock.WaitAsync();
try
{
await SavePriceCacheAsync(symbol, assetType, response, source);
@ -215,10 +212,6 @@ public class MarketDataService : IMarketDataService
{
_logger.LogWarning(ex, "[数据库缓存写入失败] Symbol={Symbol},忽略(内存缓存已生效)", symbol);
}
finally
{
_writeLock.Release();
}
});
return response;