ToolkitX
知识库工具箱

数据库操作

MySQL、MongoDB、Redis 集成

25min·进阶

01. Node.js 连接数据库的基础

Node.js 操作数据库跟其他语言原理一样,但因为是异步的,所有数据库操作都是非阻塞的——用回调、Promise 或 async/await。 三种常见模式: 1. 原生驱动——mysql2、pg (PostgreSQL)、mongodb(MongoDB 官方驱动)。直接发 SQL 或命令,最底层最灵活。 2. 查询构建器——Knex.js,用 JS 链式调用拼 SQL,不用手写原始 SQL 字符串。 3. ORM——Prisma、Sequelize、TypeORM。把数据库表映射成 JS 类/对象,用面向对象的方式操作数据库。 多数现代 Node.js 项目会用 ORM 或至少查询构建器——手写 SQL 虽然灵活但容易注入、不易维护。但关键的性能查询仍然可能需要原生 SQL。
bash
# 安装常用数据库驱动
npm install mysql2          # MySQL
npm install pg               # PostgreSQL
npm install mongodb          # MongoDB
npm install prisma @prisma/client  # Prisma ORM
npm install knex             # Knex 查询构建器

02. MySQL——mysql2 驱动

mysql2 是 Node.js 连接 MySQL 最常用的驱动。它支持 Promise API、连接池、预处理语句(防 SQL 注入)。 连接池是生产环境必备——每次请求创建连接太浪费,连接池维护一组复用连接。mysql2/promise 提供 async/await 友好的 API。 预处理语句(Prepared Statement)用 ? 占位符,防止 SQL 注入。mysql2.execute('SELECT * FROM users WHERE id = ?', [userId]) 自动转义参数。 事务用连接池的 getConnection 拿到一个连接,用 beginTransaction、commit、rollback 管控。事务结束后 connection.release() 还回连接池。
javascript
const mysql = require('mysql2/promise');

// 连接池
const pool = mysql.createPool({
  host: 'localhost',
  user: 'root',
  database: 'mydb',
  waitForConnections: true,
  connectionLimit: 10,
});

// 查询
const [rows] = await pool.execute(
  'SELECT * FROM users WHERE age > ?', [18]
);

// 插入
const [result] = await pool.execute(
  'INSERT INTO users (name, email) VALUES (?, ?)',
  ['Alice', '[email protected]']
);
console.log(result.insertId);

// 事务
const conn = await pool.getConnection();
try {
  await conn.beginTransaction();
  await conn.execute('UPDATE accounts SET balance = balance - ? WHERE id = ?', [100, 1]);
  await conn.execute('UPDATE accounts SET balance = balance + ? WHERE id = ?', [100, 2]);
  await conn.commit();
} catch (err) {
  await conn.rollback();
  throw err;
} finally {
  conn.release();
}
连接池的 connectionLimit 不是越大越好——MySQL 默认最大连接数 151。设太大会耗尽 MySQL 的连接资源。

03. PostgreSQL——pg 驱动与连接池

pg 是 Node.js 连接 PostgreSQL 的标准驱动。跟 mysql2 类似但 PG 特有的功能: pg.Pool 管理连接池。参数化查询用 $1、$2 占位符(不是 ?)。 PG 支持 LISTEN/NOTIFY——数据库能主动推送消息给 Node.js。适合实时更新场景。 JSONB 查询的结果自动解析为 JS 对象,不需要额外处理。PG 的 COPY 命令可以做高速批量插入。 pg-promise 是 pg 的 Promise 封装,提供更丰富的 API。但原生的 pg 已经支持 Promise 和 async/await,不一定需要额外库。
javascript
const { Pool } = require('pg');

const pool = new Pool({
  host: 'localhost',
  database: 'mydb',
  user: 'postgres',
  max: 10,
});

// 查询
const { rows } = await pool.query(
  'SELECT * FROM users WHERE age > $1', [18]
);

// JSONB 查询
const { rows } = await pool.query(
  "SELECT * FROM products WHERE attributes @> $1",
  [{ color: 'blue' }]
);
// attributes 字段自动从 JSONB 转为 JS 对象

// LISTEN/NOTIFY
const client = await pool.connect();
await client.query('LISTEN new_order');
client.on('notification', (msg) => {
  console.log('New order:', msg.payload);
});
pg 的 $1 占位符是 PostgreSQL 特有的参数化方式。记住不要拼字符串传参——参数化既防注入又让数据库缓存执行计划。

04. MongoDB——mongodb 驱动

mongodb 是 MongoDB 官方 Node.js 驱动。它操作 MongoDB 的 JSON 文档风格跟写 JavaScript 对象一样自然。 MongoClient.connect 创建连接(通常是全局单例)。db.collection 获取集合引用。然后就是 find、insertOne、updateOne、deleteMany 这些操作。 MongoDB 驱动返回的是 Cursor 对象——惰性加载,不会一次性把全部数据拉回来。用 .toArray() 转数组或用 for await...of 迭代。 跟关系型数据库不同,MongoDB 不需要预定义 schema。但你可以用 JSON Schema Validation 在集合上定义校验规则——在 driver 这层不用管,但好的实践是配合 Mongoose 加 schema。
javascript
const { MongoClient } = require('mongodb');

const client = new MongoClient('mongodb://localhost:27017');
await client.connect();
const db = client.db('mydb');
const users = db.collection('users');

// 查询
const adults = await users.find({ age: { $gt: 18 } }).toArray();

// 插入
const result = await users.insertOne({
  name: 'Alice',
  email: '[email protected]',
  age: 36
});
console.log(result.insertedId);

// 游标迭代(大数据集)
const cursor = users.find({ age: { $gt: 18 } });
for await (const doc of cursor) {
  console.log(doc.name);
}

05. ORM 选型——Prisma vs Sequelize vs Knex

Prisma——新一代 ORM,强类型是其最大卖点。定义 schema 文件(schema.prisma),一键生成 TypeScript 类型和客户端代码。类型安全贯穿所有查询,IDE 自动补全完美。适合 TypeScript 项目。 Sequelize——最老牌的 Node.js ORM。支持 MySQL、PG、SQLite、MSSQL。功能全面但 API 设计较老(回调风格为主),TypeScript 支持一般。适合老项目或不需要强类型的团队。 Knex——查询构建器,不是完全体 ORM。在原始 SQL 和 ORM 之间——链式调用生成 SQL 但不会把结果映射成 Model 对象。适合需要写复杂 SQL 但又不想管理原始字符串的场景。 选型建议:新项目用 Prisma(TypeScript 加持),需要灵活控制的用 Knex,历史遗留项目看看 Sequelize。
javascript
// Prisma——定义 schema
// schema.prisma
model User {
  id    Int     @id @default(autoincrement())
  email String  @unique
  name  String?
  posts Post[]
}

// 使用 Prisma Client
import { PrismaClient } from '@prisma/client';
const prisma = new PrismaClient();

const users = await prisma.user.findMany({
  where: { age: { gt: 18 } },
  include: { posts: true }
});

// Knex——查询构建器
const knex = require('knex')({ client: 'pg', connection: {...} });
const users = await knex('users')
  .where('age', '>', 18)
  .orderBy('created_at', 'desc')
  .limit(10);
Prisma 的编译器模型——你改 schema 后跑 prisma generate,它重新生成类型安全的 client。所有查询都有编译时类型检查,运行时少很多 bug。

知识测验

1/5正确 0

Node.js 连接 MySQL 推荐用哪个驱动?

下一节

WebSocket 实时通信

下一节