SAAS/README-DB.md

236 lines
6.0 KiB
Markdown
Raw Permalink Normal View History

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