自学教程 学编程从这里开始

Node.js 连接 MySQL

如果你还没有 MySQL 的基础知识,可以参考 MySQL 基础教程。

重要说明:旧版 mysql 包已经不再维护,新项目强烈建议使用 mysql2。mysql2 完全兼容 mysql 的API,性能更好,原生支持 Promise / async‑await、预处理语句,支持 MySQL8.0。

安装驱动

npm install mysql2

不建议再使用 cnpm,直接使用官方 npm。

准备测试表 websites

执行下面SQL导入测试数据表,后续所有示例都操作这张表:

CREATE DATABASE IF NOT EXISTS test;
USE test;
DROP TABLE IF EXISTS websites;
CREATE TABLE websites (
id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(20),
url VARCHAR(255),
alexa INT,
country VARCHAR(10)
);

INSERT INTO websites(name,url,alexa,country) VALUES
('Google','https://www.google.com',1,'USA'),
('淘宝','https://www.taobao.com',13,'CN'),
('runoops教程','http://runoops.com',6988,'CN'),
('微博','http://weibo.com',22,'CN'),
('Facebook','https://www.facebook.com',3,'USA');

基础单次连接(仅适合脚本,不适合Web服务)

方式1:传统回调写法(兼容旧习惯)

const mysql = require('mysql2');// 创建连接
const connection = mysql.createConnection({
host: 'localhost',
user: 'root',
password: '', // 修改为你的数据库密码
database: 'test',
charset: 'utf8mb4' // 推荐utf8mb4,支持emoji
}); connection.connect(function(err) {
if (err) {
console.error('连接失败:',err.stack);
return;
}
console.log('连接成功,threadId:',connection.threadId);
}); connection.query('SELECT 1 + 1 AS solution', function (error, results, fields) {
if (error) throw error;
console.log('The solution is: ', results[0].solution);
});
connection.end();

运行:

node test.js
#输出 The solution is: 2

方式2:Promise / async‑await(现代开发推荐)

const mysql = require('mysql2/promise');async function run() {
const connection = await mysql.createConnection({
host: 'localhost',
user: 'root',
password: '',
database: 'test',
charset:'utf8mb4'
});
const [results] = await connection.query('SELECT 1+1 AS solution');
console.log('The solution is: ', results[0].solution);
await connection.end();
}
run().catch(err=>console.error(err));

数据库连接参数完整说明

参数描述
host数据库主机地址,默认 localhost
user数据库用户名
password数据库密码
port端口,默认 3306
database指定要使用的数据库
charset连接字符集,生产务必使用 utf8mb4
connectTimeout连接超时时间,单位毫秒
timezone时区,默认 local
supportBigNumbers读取 bigint / decimal 大数时开启
bigNumberStringsbigint 以字符串返回,防止JS精度丢失
multipleStatements是否允许一条query执行多条SQL,默认关闭(安全)
waitForConnections连接池:连接耗尽是否排队等待
connectionLimit连接池最大连接数

⚠️ 不要开启 multipleStatements,存在SQL注入风险。

✅ 生产环境优先使用:连接池

Web服务不要用单次createConnection,频繁创建销毁连接性能差,连接池复用连接。

const mysql = require('mysql2/promise');// 创建连接池,全局复用
const pool = mysql.createPool({
host:'localhost',
user:'root',
password:'',
database:'test',
charset:'utf8mb4',
connectionLimit: 10,
waitForConnections: true,
queueLimit:0
}); // 测试连接池
async function testPool(){
const [rows] = await pool.query('SELECT 1+1 AS res');
console.log(rows[0].res);
}
testPool().catch(console.error);
module.exports = pool;

连接池的连接会自动归还,一般不需要手动 .end();程序退出时才调用 await pool.end() 关闭全部连接。

数据库操作 CURD(操作 websites 表)

安全提醒:禁止直接拼接用户输入到SQL字符串,必须使用占位符 ? 传参,防止SQL注入。

查询数据 SELECT

async‑await版本(推荐)

const pool = require('./test');async function queryList(){
const sql = 'SELECT * FROM websites';
const [rows] = await pool.query(sql);
console.log('--------------------------SELECT----------------------------');
console.log(rows);
console.log('------------------------------------------------------------\n\n');
}
queryList().catch(err=>console.error('[SELECT ERROR]',err.message));

回调版本(旧风格)

const mysql = require('mysql2');
const connection = mysql.createConnection({host:'localhost',user:'root',password:'',database:'test'});

connection.connect();
const sql = 'SELECT * FROM websites';
connection.query(sql,function(err,result){
if(err){
console.log('[SELECT ERROR] - ',err.message);
return;
}
console.log('--------------------------SELECT----------------------------');
console.log(result);
console.log('------------------------------------------------------------\n\n');
});
connection.end();

插入数据 INSERT

async‑await版本:

const pool = require('./test');async function insertData(){
const addSql = 'INSERT INTO websites(name,url,alexa,country) VALUES(?,?,?,?)';
const params = ['Web工具','http://t.runoops.com',53453,'CN'];
const [result] = await pool.query(addSql,params);
console.log('--------------------------INSERT----------------------------');
console.log('INSERT ID:',result.insertId);
console.log('affectedRows:',result.affectedRows);
console.log('-----------------------------------------------------------------\n\n');
}
insertData().catch(err=>console.error('[INSERT ERROR]',err.message));

执行成功后,查看数据表,即可以看到添加的数据:

更新数据 UPDATE

const pool = require('./test');async function updateData(){
const modSql = 'UPDATE websites SET name = ?,url = ? WHERE id = ?';
const params = ['参考手册','[http://ref.runoops.com/',6](http://ref.runoops.com/',6)];
const [result] = await pool.query(modSql,params);
console.log('--------------------------UPDATE----------------------------');
console.log('UPDATE affectedRows',result.affectedRows);
console.log('-----------------------------------------------------------------\n\n');
}
updateData().catch(err=>console.error('[UPDATE ERROR]',err.message));

执行成功后,查看数据表,即可以看到更新的数据:

删除数据 DELETE

const pool = require('./test');async function delData(){
const delSql = 'DELETE FROM websites where id=?';
const params = [6];
const [result] = await pool.query(delSql,params);
console.log('--------------------------DELETE----------------------------');
console.log('DELETE affectedRows',result.affectedRows);
console.log('-----------------------------------------------------------------\n\n');
}
delData().catch(err=>console.error('[DELETE ERROR]',err.message));

执行成功后,查看数据表,即可以看到 id=6 的数据已被删除:

事务示例(InnoDB引擎)

转账、订单等场景,多条SQL必须同时成功或者同时失败:

const pool = require('./test');async function runTransaction(){
const conn = await pool.getConnection();
try{
await conn.beginTransaction();
// 执行多条业务SQL
// await conn.query('UPDATE xxx SET ... WHERE id=?',[...])
await conn.commit();
console.log('事务提交成功');
}catch(err){
await conn.rollback();
console.error('事务回滚',err);
}finally{
conn.release(); // 归还连接到池,必须写
}
}
runTransaction();

错误处理与调试

  1. 所有数据库操作务必包裹 try/catch,避免异常直接崩溃整个服务;
  2. 报错对象包含:err.code 错误码、err.sqlState、err.sql(执行的SQL语句,开发环境打印用于调试);
  3. 开发环境打印完整错误,生产环境不要把原始错误返回前端,只打日志;
  4. 常见错误:
    • ER_ACCESS_DENIED_ERROR:账号密码错误,或远程访问权限未授权;
    • ER_NO_SUCH_TABLE:表不存在;
    • ER_DUP_ENTRY:唯一索引重复;
    • ETIMEDOUT:连接超时,检查防火墙、MySQL服务、连接池参数。

示例统一捕获:

async function safeQuery(){
  try {
    const [rows] = await pool.query('SELECT * FROM websites');
    return rows;
  }catch(err){
    console.error('MySQL异常',{code:err.code,msg:err.message,sql:err.sql});
    throw new Error('数据库操作失败,请稍后重试');
  }
}

性能与安全优化建议

  1. Web项目必须使用连接池,禁止每次请求新建/关闭连接;
  2. SQL永远使用占位符?,不要字符串拼接SQL,防止注入;
  3. 避免SELECT *,只查询业务需要的字段;
  4. 查询大数据必须加LIMIT,禁止全表扫描;
  5. where条件、排序字段建立索引;避免%xxx前置通配符,会索引失效;
  6. 批量数据优先批量插入,不要循环单条插入;
  7. 数据库密码不要硬编码写代码,放到环境变量 process.env;
  8. 事务内代码尽量精简,缩短事务时间,减少锁冲突、死锁;
  9. 字符集统一 utf8mb4,支持中文和emoji表情;
  10. 开启MySQL慢查询日志,监控慢SQL。

mysql2 与旧mysql包对比

包Promise维护状态推荐度
mysql需要额外封装停止维护❌老项目兼容
mysql2原生支持async/await持续更新✅新项目首选
标签: