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

236 lines
6.0 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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