使用k6压测ASP.NET Core API时遭遇MySQL连接超时问题求助
问题场景
使用k6对ASP.NET Core API执行负载测试(配置为1000虚拟用户,10000次迭代)时,API抛出MySQL连接超时异常,核心错误信息:
MySql.Data.MySqlClient.MySqlException (0x80004005): Unable to connect to any of the specified MySQL hosts.
---> MySql.Data.MySqlClient.MySqlException (0x80004005): Timeout expired. The timeout period elapsed prior to completion of the operation or the server is not responding.
问题根源是高并发下每次请求手动创建新MySQL连接,导致连接池耗尽或MySQL服务器无法处理过量连接请求。
解决方案
1. 改用数据库连接池管理连接
原代码每次请求都手动创建、打开、关闭MySQL连接,高并发下会快速耗尽资源。正确做法是通过ASP.NET Core依赖注入复用连接池中的连接:
注册连接服务(Program.cs)
builder.Services.AddSingleton<IDbConnection>(sp => new MySqlConnection(builder.Configuration.GetConnectionString("MySqlConnection")));
控制器中注入并使用连接
public class WeatherForecastController : ControllerBase { private readonly IDbConnection _db; public WeatherForecastController(IDbConnection db) { _db = db; } [HttpGet(Name = "listStation")] public async Task<IEnumerable<Station>> Get() { var result = await _db.QueryAsync<Station>( "SELECT Code,Name,EnglishName,TelCode FROM `oln_station` where internetShow = 1 order by Name "); return result.ToList(); } }
无需手动调用Open()/Close(),Dapper会自动管理连接池中的连接生命周期。
2. 优化MySQL连接字符串配置
调整连接池参数,避免无限制创建连接,同时设置合理超时:
SERVER=***; DATABASE=***; User=***;PASSWORD=***; Min Pool Size=10; Max Pool Size=100; Connection Timeout=15; Command Timeout=30;
Max Pool Size:连接池最大连接数,需与MySQL服务器max_connections配置匹配(默认MySQL为151,不要超过此值)Min Pool Size:预初始化连接数,减少高并发时创建连接的开销Connection Timeout:连接服务器的超时时间,避免无限等待
3. 调整负载测试参数
原配置的1000虚拟用户远超MySQL默认连接能力,建议先降低并发量,用梯度加压模式测试:
import http from "k6/http"; import { check } from "k6"; export const options = { stages: [ { duration: '30s', target: 50 }, // 30秒内升到50个虚拟用户 { duration: '1m', target: 50 }, // 维持50用户1分钟 { duration: '30s', target: 0 }, // 30秒内降到0用户 ], }; export default () => { const res = http.get('http://localhost:5000/WeatherForecast'); check(res, { 'status was 200': (r) => r.status == 200 }); };
4. 检查MySQL服务器配置
- 执行
SHOW VARIABLES LIKE 'max_connections';查看最大连接数,若默认151无法满足需求,可结合服务器硬件调整至合适值 - 检查
wait_timeout和interactive_timeout配置,避免连接被过早回收
5. 缓存静态查询结果
该接口返回的是静态数据,可缓存结果避免重复查询:
public class WeatherForecastController : ControllerBase { // 懒加载缓存,仅初始化一次 private static readonly Lazy<Task<List<Station>>> _cachedStations = new Lazy<Task<List<Station>>>(async () => { using (var db = new MySqlConnection("你的连接字符串")) { var result = await db.QueryAsync<Station>("SELECT Code,Name,EnglishName,TelCode FROM `oln_station` where internetShow = 1 order by Name "); return result.ToList(); } }); [HttpGet(Name = "listStation")] public async Task<IEnumerable<Station>> Get() { return await _cachedStations.Value; } }
若数据需定期更新,可结合IDistributedCache实现定时刷新缓存。
内容的提问来源于stack exchange,提问作者Ehsan Akbar

