SAAS/README-DB.md
2025-05-14 00:30:11 +08:00

6.0 KiB
Raw Permalink Blame History

数据库连接模块使用指南

目录

简介

数据库连接模块提供了一套高效的数据库连接管理系统,基于连接池实现,用于管理系统级数据库和团队级数据库的连接。主要特点:

  • 使用高阶函数中间件模式易于集成到API路由
  • 基于mysql2/promise的连接池,提供高性能的连接复用
  • 自动管理连接的获取和释放,防止连接泄漏
  • 分离的系统和团队数据库连接逻辑,支持多租户架构
  • 完善的日志系统,方便调试和监控

文件结构

src/lib/db/
├── config.ts         # 数据库配置
├── connect-system.ts # 系统数据库连接
├── connect-team.ts   # 团队数据库连接
└── index.ts          # 统一导出入口

基本用法

系统数据库连接

系统数据库用于存储全局数据,如用户账户、工作空间和团队信息。

// 导入系统数据库连接
import { connectSystemDB, RequestWithDB } from '@/lib/db';

// 定义处理器函数
async function handler(req: RequestWithDB) {
  try {
    // 使用req.db访问数据库连接
    const [rows] = await req.db.query('SELECT * FROM your_table');
    
    // 处理结果
    return rows;
  } catch (error) {
    console.error('数据库操作失败:', error);
    throw error;
  }
}

// 使用中间件包装处理函数
export const processData = connectSystemDB(handler);

团队数据库连接

团队数据库用于存储特定团队的数据,每个团队可以使用独立的数据库。

// 导入团队数据库连接
import { connectTeamDB, RequestWithDB } from '@/lib/db';

// 假设已获取团队数据库配置
const teamDbConfig = {
  host: 'localhost',
  name: 'team_db',
  user: 'team_user',
  password: 'team_password'
};

// 定义处理器函数
async function handler(req: RequestWithDB) {
  try {
    // 使用req.db访问数据库连接
    const [rows] = await req.db.query('SELECT * FROM team_data');
    
    // 处理结果
    return rows;
  } catch (error) {
    console.error('数据库操作失败:', error);
    throw error;
  }
}

// 使用中间件包装处理函数并创建处理函数
export const processTeamData = connectTeamDB(
  teamDbConfig.host,
  teamDbConfig.name,
  teamDbConfig.user,
  teamDbConfig.password
)(handler);

数据库查询最佳实践

  1. 使用参数化查询

参数化查询是防止SQL注入的最佳方式

// 良好实践 - 使用参数化查询
const [user] = await req.db.query(
  'SELECT id, username FROM users WHERE id = ?', 
  [userId]
);

// 不良实践 - 容易遭受SQL注入
const [user] = await req.db.query(
  `SELECT * FROM users WHERE id = ${userId}`
);
  1. 只查询需要的列

为提高性能,只查询实际需要的列:

// 良好实践 - 只查询需要的列
const [rows] = await req.db.query(
  'SELECT id, name, email FROM users'
);

// 不良实践 - 查询所有列
const [rows] = await req.db.query(
  'SELECT * FROM users'
);
  1. 使用事务保证数据一致性

当需要进行多个相关操作时,使用事务确保数据一致性:

try {
  await req.db.beginTransaction();
  
  // 执行多个查询
  await req.db.query('INSERT INTO orders (user_id, total) VALUES (?, ?)', [userId, total]);
  const [result] = await req.db.query('SELECT LAST_INSERT_ID() as id');
  const orderId = result[0].id;
  
  for (const item of items) {
    await req.db.query(
      'INSERT INTO order_items (order_id, product_id, quantity) VALUES (?, ?, ?)',
      [orderId, item.productId, item.quantity]
    );
  }
  
  await req.db.commit();
  return orderId;
} catch (error) {
  await req.db.rollback();
  throw error;
}

性能考虑

连接池已经配置为最佳性能,但仍需注意:

  • 系统数据库连接池最大10个连接
  • 团队数据库连接池每个团队最大5个连接
  • 长时间运行的查询可能会耗尽连接池
  • 确保查询有合适的索引
  • 对大型结果集使用分页

连接池监控

连接池状态可以在日志中查看。成功的连接操作会显示为绿色圆点(🟢),失败的操作会显示为红色圆点(🔴)。

🟢 MySQL 已连接: localhost:3306/saas_master
🟢 MySQL 系统数据库连接已获取
🟢 MySQL 系统数据库连接已释放

常见问题

问:如何执行事务?

:使用req.db.beginTransaction()req.db.commit()req.db.rollback()

try {
  await req.db.beginTransaction();
  
  // 执行多个查询
  await req.db.query('INSERT INTO ...');
  await req.db.query('UPDATE ...');
  
  await req.db.commit();
} catch (error) {
  await req.db.rollback();
  throw error;
}

问:如何处理大量并发请求?

:当前连接池配置应该足以处理中等负载。如果需要处理高并发,考虑:

  1. 增加连接池大小(修改connectionLimit
  2. 添加缓存层减少数据库查询
  3. 实现请求节流或批处理

问:连接池中的连接是如何管理的?

:连接池自动管理连接的创建、分配和回收:

  • 首次请求时,创建连接并添加到池中
  • 处理完请求后,连接被释放回池中(而不是关闭)
  • 连接有最大空闲时间,超时会被关闭
  • 连接池有最大连接数限制,超过限制的请求会等待
// 不需要手动关闭连接
// 中间件会自动处理释放连接
const connection = await systemPool.getConnection();
try {
  // 使用连接...
  const [rows] = await connection.query('SELECT ...');
  return rows;
} finally {
  // 中间件会自动调用这一行
  connection.release();
}

文档由阿瑞创建和维护。如有问题,请联系系统管理员。