Node.js 面试题详细答案 - Q19
Q19: 如何在 Node.js 中操作数据库?常用的数据库驱动有哪些?
数据库操作概述
Node.js 支持多种数据库操作方式,包括关系型数据库(MySQL、PostgreSQL)和非关系型数据库(MongoDB、Redis)。
关系型数据库
1. MySQL 操作
// 使用 mysql2 驱动
const mysql = require('mysql2/promise')
// 创建连接池
const pool = mysql.createPool({
host: 'localhost',
user: 'root',
password: 'password',
database: 'test',
waitForConnections: true,
connectionLimit: 10,
queueLimit: 0,
})
// 基本查询
async function getUsers() {
try {
const [rows] = await pool.execute('SELECT * FROM users')
return rows
} catch (error) {
console.error('查询错误:', error)
throw error
}
}
// 插入数据
async function createUser(userData) {
try {
const { name, email, age } = userData
const [result] = await pool.execute(
'INSERT INTO users (name, email, age) VALUES (?, ?, ?)',
[name, email, age]
)
return result.insertId
} catch (error) {
console.error('插入错误:', error)
throw error
}
}
// 更新数据
async function updateUser(id, userData) {
try {
const { name, email, age } = userData
const [result] = await pool.execute(
'UPDATE users SET name = ?, email = ?, age = ? WHERE id = ?',
[name, email, age, id]
)
return result.affectedRows
} catch (error) {
console.error('更新错误:', error)
throw error
}
}
// 删除数据
async function deleteUser(id) {
try {
const [result] = await pool.execute('DELETE FROM users WHERE id = ?', [id])
return result.affectedRows
} catch (error) {
console.error('删除错误:', error)
throw error
}
}
2. PostgreSQL 操作
// 使用 pg 驱动
const { Pool } = require('pg')
// 创建连接池
const pool = new Pool({
user: 'postgres',
host: 'localhost',
database: 'test',
password: 'password',
port: 5432,
max: 20,
idleTimeoutMillis: 30000,
connectionTimeoutMillis: 2000,
})
// 基本查询
async function getUsers() {
try {
const result = await pool.query('SELECT * FROM users')
return result.rows
} catch (error) {
console.error('查询错误:', error)
throw error
}
}
// 事务处理
async function transferMoney(fromId, toId, amount) {
const client = await pool.connect()
try {
await client.query('BEGIN')
// 扣除发送方余额
await client.query(
'UPDATE accounts SET balance = balance - $1 WHERE id = $2',
[amount, fromId]
)
// 增加接收方余额
await client.query(
'UPDATE accounts SET balance = balance + $1 WHERE id = $2',
[amount, toId]
)
await client.query('COMMIT')
console.log('转账成功')
} catch (error) {
await client.query('ROLLBACK')
console.error('转账失败:', error)
throw error
} finally {
client.release()
}
}
非关系型数据库
1. MongoDB 操作
// 使用 mongodb 驱动
const { MongoClient } = require('mongodb')
// 连接数据库
async function connectToDatabase() {
const client = new MongoClient('mongodb://localhost:27017')
await client.connect()
return client.db('test')
}
// 基本操作
async function mongoOperations() {
const db = await connectToDatabase()
const collection = db.collection('users')
// 插入文档
const insertResult = await collection.insertOne({
name: 'John',
email: 'john@example.com',
age: 30,
})
console.log('插入结果:', insertResult.insertedId)
// 查询文档
const users = await collection.find({ age: { $gte: 18 } }).toArray()
console.log('查询结果:', users)
// 更新文档
const updateResult = await collection.updateOne(
{ name: 'John' },
{ $set: { age: 31 } }
)
console.log('更新结果:', updateResult.modifiedCount)
// 删除文档
const deleteResult = await collection.deleteOne({ name: 'John' })
console.log('删除结果:', deleteResult.deletedCount)
}
2. Redis 操作
// 使用 redis 驱动
const redis = require('redis')
// 创建客户端
const client = redis.createClient({
host: 'localhost',
port: 6379,
})
// 连接 Redis
client.on('connect', () => {
console.log('Redis 连接成功')
})
client.on('error', (err) => {
console.error('Redis 错误:', err)
})
// 基本操作
async function redisOperations() {
try {
// 设置键值
await client.set(
'user:1',
JSON.stringify({
name: 'John',
email: 'john@example.com',
})
)
// 获取值
const user = await client.get('user:1')
console.log('用户信息:', JSON.parse(user))
// 设置过期时间
await client.setex('session:123', 3600, 'user_data')
// 列表操作
await client.lpush('tasks', 'task1', 'task2', 'task3')
const tasks = await client.lrange('tasks', 0, -1)
console.log('任务列表:', tasks)
// 哈希操作
await client.hset('user:2', 'name', 'Jane', 'email', 'jane@example.com')
const userData = await client.hgetall('user:2')
console.log('用户数据:', userData)
} catch (error) {
console.error('Redis 操作错误:', error)
}
}
ORM 和查询构建器
1. Sequelize (MySQL/PostgreSQL)
// 使用 Sequelize
const { Sequelize, DataTypes } = require('sequelize')
// 创建连接
const sequelize = new Sequelize('test', 'root', 'password', {
host: 'localhost',
dialect: 'mysql',
})
// 定义模型
const User = sequelize.define('User', {
name: {
type: DataTypes.STRING,
allowNull: false,
},
email: {
type: DataTypes.STRING,
allowNull: false,
unique: true,
},
age: {
type: DataTypes.INTEGER,
allowNull: false,
},
})
// 基本操作
async function sequelizeOperations() {
try {
// 同步模型
await User.sync()
// 创建用户
const user = await User.create({
name: 'John',
email: 'john@example.com',
age: 30,
})
console.log('创建用户:', user.toJSON())
// 查询用户
const users = await User.findAll({
where: { age: { [Sequelize.Op.gte]: 18 } },
})
console.log(
'查询结果:',
users.map((u) => u.toJSON())
)
// 更新用户
await User.update({ age: 31 }, { where: { name: 'John' } })
// 删除用户
await User.destroy({
where: { name: 'John' },
})
} catch (error) {
console.error('Sequelize 错误:', error)
}
}
2. Mongoose (MongoDB)
// 使用 Mongoose
const mongoose = require('mongoose')
// 连接数据库
mongoose.connect('mongodb://localhost:27017/test')
// 定义模式
const userSchema = new mongoose.Schema({
name: { type: String, required: true },
email: { type: String, required: true, unique: true },
age: { type: Number, required: true },
})
// 创建模型
const User = mongoose.model('User', userSchema)
// 基本操作
async function mongooseOperations() {
try {
// 创建用户
const user = new User({
name: 'John',
email: 'john@example.com',
age: 30,
})
await user.save()
console.log('创建用户:', user)
// 查询用户
const users = await User.find({ age: { $gte: 18 } })
console.log('查询结果:', users)
// 更新用户
await User.updateOne({ name: 'John' }, { $set: { age: 31 } })
// 删除用户
await User.deleteOne({ name: 'John' })
} catch (error) {
console.error('Mongoose 错误:', error)
}
}
数据库连接管理
1. 连接池配置
// MySQL 连接池
const mysql = require('mysql2/promise')
const pool = mysql.createPool({
host: 'localhost',
user: 'root',
password: 'password',
database: 'test',
waitForConnections: true,
connectionLimit: 10,
queueLimit: 0,
acquireTimeout: 60000,
timeout: 60000,
reconnect: true,
})
// 健康检查
async function checkConnection() {
try {
const connection = await pool.getConnection()
await connection.ping()
connection.release()
console.log('数据库连接正常')
} catch (error) {
console.error('数据库连接失败:', error)
}
}
2. 错误处理
// 统一错误处理
class DatabaseError extends Error {
constructor(message, code, sql) {
super(message)
this.name = 'DatabaseError'
this.code = code
this.sql = sql
}
}
async function safeQuery(sql, params = []) {
try {
const [rows] = await pool.execute(sql, params)
return rows
} catch (error) {
throw new DatabaseError(error.message, error.code, sql)
}
}
性能优化
1. 查询优化
// 使用索引
await pool.execute('CREATE INDEX idx_user_email ON users(email)')
// 分页查询
async function getUsersWithPagination(page = 1, limit = 10) {
const offset = (page - 1) * limit
const [rows] = await pool.execute('SELECT * FROM users LIMIT ? OFFSET ?', [
limit,
offset,
])
return rows
}
// 批量插入
async function batchInsertUsers(users) {
const values = users.map((user) => [user.name, user.email, user.age])
const [result] = await pool.execute(
'INSERT INTO users (name, email, age) VALUES ?',
[values]
)
return result.affectedRows
}
2. 缓存策略
// Redis 缓存
async function getCachedUser(id) {
try {
const cached = await client.get(`user:${id}`)
if (cached) {
return JSON.parse(cached)
}
const user = await getUserFromDatabase(id)
await client.setex(`user:${id}`, 3600, JSON.stringify(user))
return user
} catch (error) {
console.error('缓存错误:', error)
return getUserFromDatabase(id)
}
}
总结
- 关系型数据库:MySQL、PostgreSQL,使用 mysql2、pg 驱动
- 非关系型数据库:MongoDB、Redis,使用 mongodb、redis 驱动
- ORM 工具:Sequelize、Mongoose,简化数据库操作
- 连接管理:使用连接池提高性能
- 错误处理:统一的错误处理机制
- 性能优化:索引、分页、缓存策略
- 事务支持:确保数据一致性
- 最佳实践:连接池配置、错误处理、查询优化