Q19: 如何在 Node.js 中操作数据库?常用的数据库驱动有哪些?

57 阅读4分钟

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,简化数据库操作
  • 连接管理:使用连接池提高性能
  • 错误处理:统一的错误处理机制
  • 性能优化:索引、分页、缓存策略
  • 事务支持:确保数据一致性
  • 最佳实践:连接池配置、错误处理、查询优化