数据库集成✅
本主题涵盖Node.js与各种数据库的集成方案,包括关系型数据库、NoSQL数据库的连接和操作方法。
Node.js如何连接和操作MySQL数据库?
答案
核心概念
Node.js通过数据库驱动程序连接MySQL,常用的驱动包括mysql2
、mysql
等。在生产环境中,通常使用连接池来管理数据库连接,提升性能和并发处理能力。
连接方式
1. 基础连接
const mysql = require('mysql2/promise');
const connection = await mysql.createConnection({
host: 'localhost',
user: 'root',
password: 'password',
database: 'mydb'
});
2. 连接池管理
const pool = mysql.createPool({
host: 'localhost',
user: 'root',
password: 'password',
database: 'mydb',
connectionLimit: 10,
acquireTimeout: 60000,
reconnect: true
});
3. 事务处理
const connection = await pool.getConnection();
await connection.beginTransaction();
try {
await connection.execute('INSERT INTO users ...');
await connection.execute('INSERT INTO profiles ...');
await connection.commit();
} catch (error) {
await connection.rollback();
throw error;
} finally {
connection.release();
}
示例实现:
- MySQL操作演示
- 最佳实践
连接池配置:
const poolConfig = {
connectionLimit: 10, // 最大连接数
acquireTimeout: 60000, // 获取连接超时时间
timeout: 60000, // 查询超时时间
reconnect: true, // 自动重连
multipleStatements: false // 禁用多语句查询(安全)
};
SQL注入防护:
// ❌ 错误 - SQL注入风险
const sql = `SELECT * FROM users WHERE id = ${userId}`;
// ✅ 正确 - 使用参数化查询
const sql = 'SELECT * FROM users WHERE id = ?';
const result = await connection.execute(sql, [userId]);
错误处理:
try {
const result = await connection.execute(sql, params);
return result[0];
} catch (error) {
if (error.code === 'ER_DUP_ENTRY') {
throw new Error('记录已存在');
}
throw error;
}
数据访问模式:
DAO模式(Data Access Object):
- 封装数据库操作逻辑
- 提供统一的数据访问接口
- 便于单元测试和维护
查询构建器:
- 动态构建SQL查询
- 类型安全的查询接口
- 支持复杂的查询条件
面试官视角:
该题考察候选人对Node.js数据库集成的理解:
- 要点清单: 掌握数据库驱动使用;理解连接池原理;能处理事务操作;了解安全防护
- 加分项: 有生产环境经验;了解性能优化;掌握数据库设计;能处理并发问题
- 常见失误: 不理解连接池;忽视SQL注入;缺乏事务处理;不考虑错误处理
延伸阅读:
- mysql2官方文档 — Node.js MySQL驱动
- 《Node.js数据库编程》 — 数据库最佳实践
Node.js如何操作MongoDB数据库?
答案
核心概念
MongoDB是面向文档的NoSQL数据库,Node.js通过官方驱动mongodb
或ODM框架Mongoose
进行操作。MongoDB的文档结构灵活,支持复杂的数据类型和嵌套结构。
连接和基础操作
1. 官方驱动连接
const { MongoClient } = require('mongodb');
const client = new MongoClient(uri, {
maxPoolSize: 10,
serverSelectionTimeoutMS: 5000
});
await client.connect();
const db = client.db('myapp');
const collection = db.collection('users');
2. 文档操作
// 创建
await collection.insertOne(document);
await collection.insertMany(documents);
// 查询
await collection.findOne({ _id: objectId });
await collection.find(filter).toArray();
// 更新
await collection.updateOne(filter, { $set: update });
await collection.updateMany(filter, { $set: update });
// 删除
await collection.deleteOne(filter);
await collection.deleteMany(filter);
3. 聚合查询
const pipeline = [
{ $match: { status: 'active' } },
{ $group: { _id: '$category', count: { $sum: 1 } } },
{ $sort: { count: -1 } }
];
await collection.aggregate(pipeline).toArray();
示例实现:
- MongoDB操作演示
- MongoDB特性
文档结构:
const userDocument = {
_id: ObjectId("..."),
username: "john_doe",
profile: {
firstName: "John",
lastName: "Doe",
avatar: "https://example.com/avatar.jpg"
},
tags: ["developer", "nodejs"],
addresses: [
{ type: "home", city: "Beijing" },
{ type: "work", city: "Shanghai" }
],
createdAt: new Date()
};
索引管理:
// 单字段索引
await collection.createIndex({ username: 1 });
// 复合索引
await collection.createIndex({
"profile.lastName": 1,
"profile.firstName": 1
});
// 文本索引
await collection.createIndex({
title: "text",
content: "text"
});
聚合管道:
const pipeline = [
{ $unwind: "$tags" },
{ $group: { _id: "$tags", count: { $sum: 1 } } },
{ $match: { count: { $gte: 5 } } },
{ $sort: { count: -1 } }
];
MongoDB vs 关系型数据库:
特性 | MongoDB | MySQL |
---|---|---|
数据模型 | 文档型 | 关系型 |
Schema | 灵活 | 固定 |
查询语言 | MQL | SQL |
事务支持 | 4.0+ | 完整支持 |
扩展性 | 水平扩展 | 垂直扩展 |
适用场景 | 内容管理、实时分析 | 事务系统、报表 |
Mongoose ODM:
const mongoose = require('mongoose');
const userSchema = new mongoose.Schema({
username: { type: String, required: true, unique: true },
email: { type: String, required: true, unique: true },
profile: {
firstName: String,
lastName: String,
avatar: String
},
createdAt: { type: Date, default: Date.now }
});
const User = mongoose.model('User', userSchema);
// 使用模型
const user = new User(userData);
await user.save();
性能优化:
- 索引策略: 为常用查询字段创建索引
- 查询优化: 使用explain()分析查询性能
- 连接池: 合理配置连接池大小
- 聚合优化: 在管道早期进行过滤操作
面试官视角
该题考察候选人对NoSQL数据库的理解:
- 要点清单: 掌握MongoDB基本操作;理解文档模型;了解聚合查询;知道索引优化
- 加分项: 有大数据处理经验;了解分片和复制;掌握性能调优;理解CAP理论
- 常见失误: 不理解文档结构;滥用嵌套文档;忽视索引重要性;不了解聚合管道
延伸阅读
- MongoDB官方文档 — MongoDB完整指南
- Mongoose文档 — Node.js MongoDB ODM
数据库连接池的作用和配置是什么?
答案
核心概念
数据库连接池是一种资源管理技术,预先创建和管理一定数量的数据库连接,供应用程序复用。连接池可以显著提升数据库操作性能,减少连接建立和销毁的开销。
连接池原理
1. 生命周期管理
应用启动 → 预创建连接 → 连接池就绪 → 分配连接 → 回收连接 → 应用关闭
2. 连接分配流程
- 应用请求数据库操作
- 从池中获取空闲连接
- 执行数据库操作
- 将连接归还到池中
- 连接可被其他请求复用
3. 关键配置参数
const poolConfig = {
// 基础配置
connectionLimit: 10, // 最大连接数
acquireTimeout: 60000, // 获取连接超时时间
timeout: 60000, // SQL执行超时时间
// 连接管理
reconnect: true, // 自动重连
idleTimeout: 300000, // 空闲连接超时(5分钟)
maxLifetime: 1800000, // 连接最大生命周期(30分钟)
// 队列管理
queueLimit: 0, // 等待队列大小(0=无限制)
// 健康检查
testOnBorrow: true, // 获取连接时测试
validationQuery: 'SELECT 1' // 连接验证查询
};
优势和价值:
1. 性能提升
- 减少连接开销: 避免频繁创建/销毁连接
- 复用现有连接: 连接可被多个请求共享
- 并发处理: 支持多个并发数据库操作
- 资源优化: 合理利用系统资源
2. 稳定性保障
- 连接限制: 防止数据库连接数过多
- 故障恢复: 自动重建失效连接
- 超时控制: 防止连接长时间占用
- 监控统计: 实时监控连接池状态
配置最佳实践:
MySQL连接池配置:
const mysql = require('mysql2');
const pool = mysql.createPool({
host: process.env.DB_HOST,
port: process.env.DB_PORT || 3306,
user: process.env.DB_USER,
password: process.env.DB_PASSWORD,
database: process.env.DB_NAME,
// 连接池大小 = CPU核心数 * 2 + 有效磁盘数
connectionLimit: 20,
// 超时设置
acquireTimeout: 60000,
timeout: 60000,
// 连接管理
reconnect: true,
multipleStatements: false, // 安全考虑
// SSL配置(生产环境)
ssl: process.env.NODE_ENV === 'production' ? {
rejectUnauthorized: false
} : false
});
MongoDB连接池配置:
const { MongoClient } = require('mongodb');
const client = new MongoClient(uri, {
// 连接池配置
maxPoolSize: 50, // 最大连接数
minPoolSize: 5, // 最小连接数
maxIdleTimeMS: 300000, // 连接空闲超时
// 服务器选择
serverSelectionTimeoutMS: 5000,
socketTimeoutMS: 45000,
// 重试配置
retryWrites: true,
retryReads: true,
// 压缩
compressors: ['zlib']
});
监控和调优:
1. 关键指标
// 获取连接池状态
const getPoolStats = () => ({
totalConnections: pool._allConnections.length,
activeConnections: pool._allConnections.length - pool._freeConnections.length,
freeConnections: pool._freeConnections.length,
queuedRequests: pool._connectionQueue.length
});
// 定期监控
setInterval(() => {
const stats = getPoolStats();
console.log('连接池状态:', stats);
// 告警逻辑
if (stats.queuedRequests > 10) {
console.warn('连接池队列积压严重');
}
}, 30000);
2. 性能调优建议
- 连接数设置: 根据CPU核心数和并发量调整
- 超时配置: 平衡响应时间和资源占用
- 连接验证: 启用连接健康检查
- 监控告警: 设置关键指标监控
常见问题和解决方案:
1. 连接泄漏
// ❌ 错误 - 连接未释放
const connection = await pool.getConnection();
const results = await connection.query(sql);
// 忘记释放连接
// ✅ 正确 - 确保连接释放
const connection = await pool.getConnection();
try {
const results = await connection.query(sql);
return results;
} finally {
connection.release(); // 确保释放
}
2. 连接超时
// 增加超时时间和重试机制
const queryWithRetry = async (sql, params, retries = 3) => {
for (let i = 0; i < retries; i++) {
try {
return await pool.execute(sql, params);
} catch (error) {
if (error.code === 'PROTOCOL_CONNECTION_LOST' && i < retries - 1) {
console.warn(`连接丢失,重试第${i + 1}次`);
continue;
}
throw error;
}
}
};
面试官视角:
该题考察候选人对数据库连接管理的理解:
- 要点清单: 理解连接池原理;掌握配置参数;了解性能优化;能监控和调优
- 加分项: 有生产环境调优经验;了解不同数据库差异;能处理连接问题;有监控经验
- 常见失误: 不理解连接池价值;配置不合理;忽视监控;不处理连接泄漏
延伸阅读:
- 数据库连接池原理 — 连接池基础概念
- 《高性能MySQL》 — MySQL性能优化指南