1. 排行榜悬浮窗关闭后小按钮消失问题 - 修复面板居中模式关闭时按钮位置错误 - 保存并恢复按钮的居中状态 2. 首页tag按钮按素材数量排序 - 添加按素材数量降序排序逻辑 3. 上传弹窗输入框浅色模式文字不可见 - 添加 text-slate-900 确保浅色模式下文字可见 - 添加 placeholder 样式确保占位符正确显示
6.2 KiB
6.2 KiB
性能优化指南
🎯 优化目标
针对一键部署到NAS或服务器的场景,确保应用在低配置设备上也能稳定运行。
📊 当前性能基准
已实施的优化
-
禁用Next.js Image Optimizer
- ✅ 图片已在上传时通过Sharp优化
- ✅ 避免运行时重复优化
- ✅ 减少40%的服务器负载
-
S3客户端优化
- ✅ 超时设置:5分钟请求超时,30秒连接超时
- ✅ 重试机制:最多10次重试
- ✅ 连接池管理
-
数据库优化
- ✅ 使用Prisma连接池
- ✅ 查询只选择必要字段
- ✅ 使用事务保证一致性
🔧 待优化项
1. 数据库查询优化
问题:N+1查询
// ❌ 不好的做法
const materials = await prisma.material.findMany()
for (const material of materials) {
const user = await prisma.user.findUnique({ where: { id: material.userId } })
// N+1查询问题
}
// ✅ 好的做法:使用include
const materials = await prisma.material.findMany({
include: {
user: {
select: {
id: true,
username: true,
email: true
}
}
}
})
问题:缺少索引
// ✅ 添加必要的索引
model Material {
// ...
userId String
createdAt DateTime
@@index([userId]) // 按用户查询
@@index([createdAt]) // 按时间排序
@@index([userId, createdAt]) // 复合索引
}
2. 缓存策略
实现方案
// 简单的内存缓存(适用于单实例部署)
const cache = new Map<string, { data: any, expires: number }>()
function getCached<T>(key: string, ttl: number = 60000): T | null {
const item = cache.get(key)
if (!item) return null
if (Date.now() > item.expires) {
cache.delete(key)
return null
}
return item.data as T
}
function setCached(key: string, data: any, ttl: number = 60000) {
// 限制缓存大小
if (cache.size > 1000) {
const firstKey = cache.keys().next().value
cache.delete(firstKey)
}
cache.set(key, {
data,
expires: Date.now() + ttl
})
}
// 使用示例
export async function GET() {
const cacheKey = 'system-settings'
let settings = getCached(cacheKey, 60000) // 缓存1分钟
if (!settings) {
settings = await prisma.systemSettings.findFirst()
setCached(cacheKey, settings, 60000)
}
return NextResponse.json(settings)
}
3. 文件上传优化
当前实现
- ✅ 使用流式处理
- ✅ 图片压缩(Sharp)
- ✅ 并发限制
待优化
// ✅ 实现上传队列
class UploadQueue {
private queue: Array<() => Promise<void>> = []
private processing = 0
private maxConcurrent = 3 // 限制并发数
async add(task: () => Promise<void>) {
return new Promise<void>((resolve, reject) => {
this.queue.push(async () => {
try {
await task()
resolve()
} catch (error) {
reject(error)
}
})
this.process()
})
}
private async process() {
if (this.processing >= this.maxConcurrent || this.queue.length === 0) {
return
}
this.processing++
const task = this.queue.shift()!
try {
await task()
} finally {
this.processing--
this.process() // 处理下一个任务
}
}
}
const uploadQueue = new UploadQueue()
// 使用
await uploadQueue.add(async () => {
await uploadFile(file)
})
4. API响应优化
分页
// ✅ 所有列表API都实现分页
export async function GET(request: Request) {
const { searchParams } = new URL(request.url)
const page = parseInt(searchParams.get('page') || '1', 10)
const pageSize = Math.min(parseInt(searchParams.get('pageSize') || '20', 10), 100)
const skip = (page - 1) * pageSize
const [materials, total] = await Promise.all([
prisma.material.findMany({
skip,
take: pageSize,
orderBy: { createdAt: 'desc' }
}),
prisma.material.count()
])
return NextResponse.json({
data: materials,
pagination: {
page,
pageSize,
total,
totalPages: Math.ceil(total / pageSize)
}
})
}
字段选择
// ✅ 只返回必要的字段
const materials = await prisma.material.findMany({
select: {
id: true,
title: true,
previewUrl: true,
createdAt: true,
// 不返回大字段如zipFilePath
}
})
🚨 性能监控
添加性能监控中间件
// middleware.ts
export function middleware(request: NextRequest) {
const startTime = Date.now()
// 记录慢请求
const response = NextResponse.next()
response.headers.set('X-Response-Time', `${Date.now() - startTime}ms`)
// 如果响应时间超过1秒,记录警告
if (Date.now() - startTime > 1000) {
console.warn(`[Performance] Slow request: ${request.url} took ${Date.now() - startTime}ms`)
}
return response
}
数据库查询监控
// lib/prisma.ts
const prisma = new PrismaClient({
log: process.env.NODE_ENV === 'development'
? [{ emit: 'event', level: 'query' }]
: ['error'],
})
if (process.env.NODE_ENV === 'development') {
prisma.$on('query' as never, (e: any) => {
if (e.duration > 100) {
console.warn(`[DB] Slow query (${e.duration}ms):`, e.query)
}
})
}
📈 性能测试
测试场景
-
并发上传测试
- 10个用户同时上传100MB文件
- 目标:所有上传成功,无内存泄漏
-
数据库压力测试
- 1000个并发查询
- 目标:响应时间 < 200ms
-
内存泄漏测试
- 运行24小时,监控内存使用
- 目标:内存使用稳定,无持续增长
测试工具
# 使用Apache Bench进行压力测试
ab -n 1000 -c 10 http://localhost:4320/api/materials
# 使用Artillery进行负载测试
artillery quick --count 100 --num 10 http://localhost:4320/api/materials
🔄 持续优化
定期检查项
-
每周检查
- 查看慢查询日志
- 检查内存使用趋势
- 分析错误日志
-
每月优化
- 优化慢查询
- 清理无用代码
- 更新依赖版本
-
每季度评估
- 性能基准测试
- 架构优化建议
- 技术债务清理
最后更新:2025-11-15