236 lines
6.0 KiB
Markdown
236 lines
6.0 KiB
Markdown
# 数据库连接模块使用指南
|
||
|
||
## 目录
|
||
|
||
- [简介](#简介)
|
||
- [文件结构](#文件结构)
|
||
- [基本用法](#基本用法)
|
||
- [系统数据库连接](#系统数据库连接)
|
||
- [团队数据库连接](#团队数据库连接)
|
||
- [数据库查询最佳实践](#数据库查询最佳实践)
|
||
- [性能考虑](#性能考虑)
|
||
- [常见问题](#常见问题)
|
||
|
||
## 简介
|
||
|
||
数据库连接模块提供了一套高效的数据库连接管理系统,基于连接池实现,用于管理系统级数据库和团队级数据库的连接。主要特点:
|
||
|
||
- 使用高阶函数中间件模式,易于集成到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();
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
文档由阿瑞创建和维护。如有问题,请联系系统管理员。 |