# 数据库连接模块使用指南

## 目录

- [简介](#简介)
- [文件结构](#文件结构)
- [基本用法](#基本用法)
  - [系统数据库连接](#系统数据库连接)
  - [团队数据库连接](#团队数据库连接)
- [数据库查询最佳实践](#数据库查询最佳实践)
- [性能考虑](#性能考虑)
- [常见问题](#常见问题)

## 简介

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

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

## 文件结构

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

## 基本用法

### 系统数据库连接

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

```typescript
// 导入系统数据库连接
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);
```

### 团队数据库连接

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

```typescript
// 导入团队数据库连接
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注入的最佳方式：

```typescript
// 良好实践 - 使用参数化查询
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}`
);
```

2. **只查询需要的列**

为提高性能，只查询实际需要的列：

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

// 不良实践 - 查询所有列
const [rows] = await req.db.query(
  'SELECT * FROM users'
);
```

3. **使用事务保证数据一致性**

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

```typescript
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()`：

```typescript
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. 实现请求节流或批处理

### 问：连接池中的连接是如何管理的？

**答**：连接池自动管理连接的创建、分配和回收：
- 首次请求时，创建连接并添加到池中
- 处理完请求后，连接被释放回池中（而不是关闭）
- 连接有最大空闲时间，超时会被关闭
- 连接池有最大连接数限制，超过限制的请求会等待

```typescript
// 不需要手动关闭连接
// 中间件会自动处理释放连接
const connection = await systemPool.getConnection();
try {
  // 使用连接...
  const [rows] = await connection.query('SELECT ...');
  return rows;
} finally {
  // 中间件会自动调用这一行
  connection.release();
}
```

---

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