Heroku升级MySQL 8后NestJS+TypeORM出现Error: read ECONNRESET求助
NestJS + TypeORM 部署Heroku MySQL8后随机出现read ECONNRESET错误
问题描述
使用NestJS和TypeORM开发后端应用,部署在Heroku平台,数据库采用MySQL。此前使用MySQL 5.6时运行正常,升级至MySQL 8后,不同查询中随机出现Error: read ECONNRESET错误,该问题已持续2周。Heroku支持仅表示可能是临时网络问题,未给出有效解决办法,现寻求技术支持。
Heroku官方提供的解决方案
- 购买新的ClearDB插件:
- 进入Heroku控制台
- 打开目标应用
- 切换到「Resources」标签页
- 在插件搜索栏搜索并添加ClearDB MySQL插件
- 根据数据库需求选择合适的套餐
- 备份数据:
在执行任何变更前,确保拥有MySQL 5.6数据库的最新备份- 迁移数据:
使用备份将数据从MySQL 5.6迁移到MySQL 8,可参考ClearDB官方文档或联系Heroku支持协助
控制台错误日志
2024-03-11T12:17:30.136457+00:00 app[web.1]: error: Error: read ECONNRESET 2024-03-11T12:17:30.138441+00:00 app[web.1]: [31m[Nest] 21 - [39m 03/11/2024, 9:17:30 AM [31m ERROR[39m [38;5;3m[ExceptionsHandler] [39m[31mread ECONNRESET[39m 2024-03-11T12:17:30.138483+00:00 app[web.1]: QueryFailedError: read ECONNRESET 2024-03-11T12:17:30.138483+00:00 app[web.1]: at Query.onResult (/app/node_modules/typeorm/driver/mysql/MysqlQueryRunner.js:165:37) 2024-03-11T12:17:30.138484+00:00 app[web.1]: at PoolConnection._notifyError (/app/node_modules/mysql2/lib/connection.js:228:21) 2024-03-11T12:17:30.138484+00:00 app[web.1]: at PoolConnection._handleFatalError (/app/node_modules/mysql2/lib/connection.js:183:10) 2024-03-11T12:17:30.138484+00:00 app[web.1]: at PoolConnection._handleNetworkError (/app/node_modules/mysql2/lib/connection.js:196:10) 2024-03-11T12:17:30.138485+00:00 app[web.1]: at Socket.emit (node:events:518:28) 2024-03-11T12:17:30.138485+00:00 app[web.1]: at emitErrorNT (node:internal/streams/destroy:169:8) 2024-03-11T12:17:30.138486+00:00 app[web.1]: at emitErrorCloseNT (node:internal/streams/destroy:128:3) 2024-03-11T12:17:30.138486+00:00 app[web.1]: at process.processTicksAndRejections (node:internal/process/task_queues:82:21)
额外技术排查与修复建议
1. 调整TypeORM连接池配置
MySQL 8的连接超时机制与5.6存在差异,结合Heroku网络环境的连接回收特性,修改数据库连接配置:
// NestJS数据库模块配置示例 TypeOrmModule.forRoot({ type: 'mysql', url: process.env.DATABASE_URL, entities: [__dirname + '/**/*.entity{.ts,.js}'], synchronize: false, pool: { max: 5, // 降低连接池上限,避免触发Heroku资源限制 min: 0, idle: 10000, // 空闲连接10秒后回收 acquire: 30000, // 获取连接超时时间设为30秒 evict: 1000, // 每秒检查并回收空闲连接 }, extra: { connectionLimit: 5, enableKeepAlive: true, // 开启连接保活 keepAliveInitialDelay: 30000, // 30秒后发送心跳包维持连接 }, })
2. 适配MySQL 8认证插件
MySQL 8默认使用caching_sha2_password认证,部分旧版mysql2驱动存在兼容问题:
- 确保
mysql2版本升级至^3.0.0以上 - 若仍有问题,可临时强制使用旧认证方式(过渡方案):
extra: { authPlugins: { mysql_clear_password: () => () => Buffer.from(process.env.DB_PASSWORD + '\0') } }
3. 添加查询重试机制
针对随机网络错误,通过NestJS异常过滤器实现查询重试:
@Catch(QueryFailedError) export class QueryFailedRetryFilter implements ExceptionFilter { async catch(exception: QueryFailedError, host: ArgumentsHost) { const ctx = host.switchToHttp(); const response = ctx.getResponse<Response>(); const request = ctx.getRequest<Request>(); // 判断是否为ECONNRESET错误 if ((exception.driverError as any).code === 'ECONNRESET') { // 根据业务场景实现重试逻辑,示例为重新调用service方法 try { // 此处替换为实际业务的查询逻辑 const result = await yourService.retryYourQuery(request.params); return response.status(HttpStatus.OK).json(result); } catch (retryErr) { return response.status(HttpStatus.INTERNAL_SERVER_ERROR).json({ statusCode: HttpStatus.INTERNAL_SERVER_ERROR, timestamp: new Date().toISOString(), path: request.url, message: '查询失败且重试无效', }); } } // 处理其他类型的查询错误 response.status(HttpStatus.INTERNAL_SERVER_ERROR).json({ statusCode: HttpStatus.INTERNAL_SERVER_ERROR, timestamp: new Date().toISOString(), path: request.url, message: exception.message, }); } }
4. 检查Heroku环境配置
- 确认Dyno与MySQL实例处于同一区域,减少跨区域网络延迟
- 执行
heroku logs --tail查看实时日志,排查是否存在频繁连接断开记录 - 检查ClearDB套餐的连接数配额,避免超出限制导致连接被强制断开
内容的提问来源于stack exchange,提问作者Alan
相关产品推荐
相关产品推荐

