Ubuntu 环境下 Node.js 日志出现数据库连接问题的排查与修复
一 快速定位问题
- 先看 Node.js 侧日志:如果使用 PM2,执行 pm2 logs 查看最新错误堆栈与时间点;如果是直接运行,检查应用输出的 console.error 与 uncaughtException。
- 再看数据库侧日志:MySQL 常见日志路径在 /var/log/mysql/error.log;也可通过 sudo journalctl -u mysql -xe 查看服务启动与运行报错。
- 确认问题表现:是“连接超时/拒绝”、还是“认证失败/库不存在”、抑或“连接池耗尽/已关闭”。这些信息决定后续排查方向。
二 常见根因与对应修复
- 配置错误(主机、端口、用户、密码、库名):核对连接串与代码配置,确保字段名与大小写一致;必要时用最小示例直连验证。
- 数据库未启动或服务名不对:在 Ubuntu 上 MySQL 的服务名通常是 mysql 而非 mysqld,用 sudo systemctl status mysql 检查;若未运行,执行 sudo systemctl start mysql。
- 远程访问被绑定地址或防火墙拦截:
- 注释 MySQL 配置中的 bind-address = 127.0.0.1(文件常见于 /etc/mysql/mysql.conf.d/mysqld.cnf),允许监听外网;
- 开放防火墙端口(如 sudo ufw allow 3306/tcp),云服务器还需在控制台安全组放行。
- 用户权限不足或来源限制:在 MySQL 执行 SHOW GRANTS FOR ‘user’@‘host’;,必要时执行 GRANT ALL PRIVILEGES ON db. TO ‘user’@‘host’; FLUSH PRIVILEGES;*,并确保 host 与 Node.js 来源匹配(如 % 或具体内网/公网 IP)。
- 驱动/认证不兼容(如 MySQL 8.0 默认 caching_sha2_password):升级驱动(如 mysql2/promise),或在测试环境将用户认证方式调整为 mysql_native_password 以验证是否为认证插件问题。
- 异步/连接池使用不当:确保使用 async/await 或 Promise 正确等待连接与释放;连接池应设置合理 connectionLimit 并在异常时回收连接。
- 资源/进程冲突:若数据库异常退出或无法启动,检查 /var/lib/mysql/ibdata1 是否被占用(如 sudo lsof /var/lib/mysql/ibdata1),必要时 sudo pkill -f mysqld 清理僵尸进程后再启动。
- Oracle 场景常见 NJS-045/DPI-1047:安装 libaio1,正确设置 LD_LIBRARY_PATH 指向 Oracle Instant Client,确保架构与版本匹配。
三 面向 MySQL 的排查与修复步骤
- 服务与端口:
- 检查运行状态:sudo systemctl status mysql;
- 本地探测端口:nc -vz 127.0.0.1 3306 或 telnet 127.0.0.1 3306;远程则替换为服务器 IP。
- 配置与网络:
- 编辑 /etc/mysql/mysql.conf.d/mysqld.cnf,注释 bind-address = 127.0.0.1 以允许远程;
- 重启服务:sudo systemctl restart mysql;
- 防火墙放行:sudo ufw allow 3306/tcp(云上同步放行安全组)。
- 权限与账户:
- 登录 MySQL:mysql -u root -p;
- 查看与授权:SHOW GRANTS FOR ‘app’@‘%’; 或 ‘app’@‘服务器IP’;
- 赋权示例:GRANT ALL PRIVILEGES ON mydb. TO ‘app’@‘%’; FLUSH PRIVILEGES;*
- 连接测试最小示例(Node.js + mysql2/promise):
- 代码示例:
- const mysql = require(‘mysql2/promise’);
- const cfg = { host: ‘127.0.0.1’, port: 3306, user: ‘app’, password: ‘******’, database: ‘mydb’ };
- const conn = await mysql.createConnection(cfg);
- const [rows] = await conn.execute(‘SELECT 1’);
- console.log(rows); await conn.end();
- 若远程连接仍失败,优先核对服务器防火墙/云安全组、bind-address、以及用户 host 是否匹配。
四 面向 Oracle 的排查与修复步骤
- 安装依赖与客户端:
- 安装 libaio1:sudo apt-get install libaio1;
- 下载并解压 Oracle Instant Client(Basic 与 SDK),合并至如 /opt/oracle/instantclient;
- 建立符号链接:ln -s libclntsh.so.12.1 libclntsh.so;
- 配置环境变量(/etc/profile 或 ~/.bashrc):
- export LD_LIBRARY_PATH=/opt/oracle/instantclient:$LD_LIBRARY_PATH
- export OCI_LIB_DIR=/opt/oracle/instantclient
- export OCI_INC_DIR=/opt/oracle/instantclient/sdk/include
- 使配置生效:source /etc/profile;
- 安装 Node.js 驱动:npm install oracledb。
- 最小连接示例:
- const oracledb = require(‘oracledb’);
- const cfg = { user: ‘scott’, password: ‘tiger’, connectString: ‘192.168.1.10:1521/orcl’ };
- const conn = await oracledb.getConnection(cfg);
- const res = await conn.execute(‘SELECT SYSDATE FROM DUAL’);
- console.log(res.rows); await conn.close();
- 若仍报 NJS-045/DPI-1047,逐项核对:Instant Client 位数与 Node 一致、环境变量生效、无多版本冲突、依赖已安装。
五 日志与代码层面的优化建议
- 统一日志格式:在 Node.js 中集中记录连接参数掩码(如密码置为 [REDACTED])、错误堆栈、重试次数与耗时;使用 pino/winston 等结构化日志便于检索。
- 可靠重连与退避:对连接失败实现指数退避与最大重试次数;在连接池层面设置 acquireTimeout、idleTimeout、maxReuseCount 等,避免脏连接堆积。
- 健康检查与告警:上线前做 await connection.execute(‘SELECT 1’) 的健康探针;生产环境对“连接失败率/平均耗时”设置阈值告警。
- 最小复现与压测:用独立脚本复现问题,配合 ab/wrk 做连接压力测试,验证连接池与超时配置是否合理。