From cbe0ac9f4aca6eea646e59604a32ed3a1c23ac1c Mon Sep 17 00:00:00 2001 From: OpenClaw Agent Date: Wed, 25 Mar 2026 02:37:18 +0000 Subject: [PATCH] =?UTF-8?q?fix:=20=E6=AF=8F=E6=AC=A1=E5=86=99=E5=85=A5?= =?UTF-8?q?=E5=88=9B=E5=BB=BA=E6=96=B0=20SqlSugarClient=20=E5=AE=9E?= =?UTF-8?q?=E4=BE=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 根因分析: - MarketDataRepository 使用注入的 ISqlSugarClient(Scoped) - 多个 Task.Run 并发调用 SavePriceCacheAsync - Storageable 操作使用同一个 SqlSugarScope 实例 - 连接在 await 边界被复用 → 冲突 正确方案: - SavePriceCacheAsync 每次创建新的 SqlSugarClient 实例 - MySQL 连接池会复用底层 TCP 连接,性能开销很小 - 不再需要 SemaphoreSlim 锁 优点: - 完全避免连接冲突 - 代码更简洁 - 并发写入无限制 --- .../Repositories/MarketDataRepository.cs | 6 +++++- .../Services/MarketDataService.cs | 13 +++---------- 2 files changed, 8 insertions(+), 11 deletions(-) diff --git a/AssetManager.Data/Repositories/MarketDataRepository.cs b/AssetManager.Data/Repositories/MarketDataRepository.cs index 2a7feed..63fdeae 100644 --- a/AssetManager.Data/Repositories/MarketDataRepository.cs +++ b/AssetManager.Data/Repositories/MarketDataRepository.cs @@ -54,7 +54,11 @@ public class MarketDataRepository : IMarketDataRepository public async Task 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; } diff --git a/AssetManager.Infrastructure/Services/MarketDataService.cs b/AssetManager.Infrastructure/Services/MarketDataService.cs index 2a15689..54966d4 100755 --- a/AssetManager.Infrastructure/Services/MarketDataService.cs +++ b/AssetManager.Infrastructure/Services/MarketDataService.cs @@ -22,14 +22,11 @@ public class MarketDataService : IMarketDataService private readonly IOkxMarketService _okxService; private readonly IMarketDataRepository _marketDataRepo; - // 静态内存缓存层(跨请求共享,避免并发数据库查询导致连接池冲突) + // 静态内存缓存层(跨请求共享) private static readonly ConcurrentDictionary _memoryCache = new(); // 静态 pending 请求字典(跨请求共享,防止并发请求同一股票) private static readonly ConcurrentDictionary>> _pendingPriceRequests = new(); - - // 静态写入锁(串行化数据库写入) - private static readonly SemaphoreSlim _writeLock = new(1, 1); public MarketDataService( ILogger logger, @@ -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;