为何ASP.NET MVC控制器会导致IIS中AppPool挂起?
多请求下EF6查询MySQL挂起问题排查
环境
- C# ASP.NET MVC 5
- Entity Framework 6
- .NET Framework 4.8
- MySQL
- IIS
问题现象
执行以下代码时,日志仅输出set AppAgent start,无set AppAgent end,说明数据库查询环节无异常但出现挂起:
LogErr("set AppAgent start"); _AppWebsite = db.Websites .Where(x => x.Domain == Request.Url.Host) .SingleOrDefault(); LogErr("set AppAgent end");
复现场景
- 站点发布后发起多请求时易触发挂起
- 站点运行稳定后难以复现
- 重启IIS应用池可恢复
相关背景
- 代码位于自定义
BaseController的OnActionExecuting方法中,通过调用AppWebsite属性触发上述查询 - 曾尝试将代码移至
Initialize方法,问题未解决
涉及表结构(websites表,共5条记录)
CREATE TABLE `websites` ( `Id` int NOT NULL AUTO_INCREMENT, `SellerId` int NOT NULL, `Name` varchar(45) NOT NULL, `Domain` varchar(50) DEFAULT NULL, `MenuH` varchar(150) DEFAULT '', `MenuF` varchar(150) DEFAULT '', `CreatedDate` datetime DEFAULT NULL, `DeletedDate` datetime DEFAULT NULL, `SubEndDate` date DEFAULT '2090-01-01', `Logo` varchar(30) DEFAULT NULL, PRIMARY KEY (`Id`), UNIQUE KEY `i_SellerId` (`SellerId`), KEY `i_Domain` (`Domain`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3;
排查与解决方案
1. 确保DbContext线程安全
EF6的DbContext不是线程安全的,多请求并发时共享实例会直接导致挂起、死锁。
- 每个请求使用独立的
DbContext实例:在BaseController构造函数中初始化,或通过依赖注入框架(如Autofac)将其生命周期配置为每请求一个实例。
2. 优化数据库连接与查询超时
- 在连接字符串中添加连接超时配置,避免无限等待:
<connectionStrings> <add name="YourDbContext" connectionString="server=xxx;database=xxx;uid=xxx;pwd=xxx;Connect Timeout=15;" providerName="MySql.Data.MySqlClient" /> </connectionStrings> - 给EF查询设置命令超时,强制中断长时间挂起的请求:
db.Database.CommandTimeout = 10; // 设置10秒超时 _AppWebsite = db.Websites .Where(x => x.Domain == Request.Url.Host) .SingleOrDefault();
3. 调整数据库连接池参数
多请求并发时连接池耗尽会导致请求等待:
- 在连接字符串中增加连接池配置:
<add name="YourDbContext" connectionString="...;Max Pool Size=100;Min Pool Size=5;" /> - 确保
DbContext被正确释放,避免连接泄漏:protected override void Dispose(bool disposing) { if (disposing && db != null) { db.Dispose(); } base.Dispose(disposing); }
4. 排查MySQL锁阻塞
InnoDB的行锁/表锁可能导致查询阻塞:
- 执行
SHOW ENGINE INNODB STATUS;查看锁等待日志 - 用
EXPLAIN分析查询计划,确认索引生效:EXPLAIN SELECT * FROM websites WHERE Domain = '测试域名';
5. 缓存网站信息减少数据库查询
避免每个请求都触发数据库查询,用MemoryCache缓存结果:
private Website _appWebsite; public Website AppWebsite { get { if (_appWebsite == null) { var cacheKey = $"Website_{Request.Url.Host}"; _appWebsite = MemoryCache.Default.Get(cacheKey) as Website; if (_appWebsite == null) { _appWebsite = db.Websites.Where(x => x.Domain == Request.Url.Host).SingleOrDefault(); MemoryCache.Default.Add(cacheKey, _appWebsite, DateTimeOffset.Now.AddHours(1)); } } return _appWebsite; } }
内容的提问来源于stack exchange,提问作者EamonnM
相关产品推荐
相关产品推荐

