373 lines
9.9 KiB
TypeScript
373 lines
9.9 KiB
TypeScript
/**
|
||
* 数据库性能测试脚本
|
||
* 用于验证索引优化效果
|
||
*/
|
||
|
||
import { PrismaClient } from '@prisma/client';
|
||
|
||
const prisma = new PrismaClient();
|
||
|
||
interface TestResult {
|
||
name: string;
|
||
duration: number;
|
||
recordCount: number;
|
||
}
|
||
|
||
async function measureQuery(name: string, queryFn: () => Promise<unknown>): Promise<TestResult> {
|
||
const start = Date.now();
|
||
const result = await queryFn();
|
||
const duration = Date.now() - start;
|
||
|
||
const recordCount = Array.isArray(result) ? result.length : 1;
|
||
|
||
console.log(` ✓ ${name}: ${duration}ms (${recordCount} 条记录)`);
|
||
|
||
return { name, duration, recordCount };
|
||
}
|
||
|
||
async function main() {
|
||
console.log('🚀 开始性能测试...\n');
|
||
|
||
const results: TestResult[] = [];
|
||
|
||
// ============================================
|
||
// 产品查询测试
|
||
// ============================================
|
||
console.log('📦 产品查询测试:');
|
||
|
||
results.push(
|
||
await measureQuery('产品列表(无过滤)', async () => {
|
||
return await prisma.product.findMany({
|
||
take: 20,
|
||
});
|
||
})
|
||
);
|
||
|
||
results.push(
|
||
await measureQuery('产品列表(软删除过滤)', async () => {
|
||
return await prisma.product.findMany({
|
||
where: { deletedAt: null },
|
||
take: 20,
|
||
});
|
||
})
|
||
);
|
||
|
||
results.push(
|
||
await measureQuery('产品列表(部门+可见性)', async () => {
|
||
const dept = await prisma.department.findFirst();
|
||
return await prisma.product.findMany({
|
||
where: {
|
||
departmentId: dept?.id,
|
||
visibility: 'TEAM',
|
||
deletedAt: null,
|
||
},
|
||
take: 20,
|
||
});
|
||
})
|
||
);
|
||
|
||
results.push(
|
||
await measureQuery('产品列表(按创建时间排序)', async () => {
|
||
return await prisma.product.findMany({
|
||
where: { deletedAt: null },
|
||
orderBy: { createdAt: 'desc' },
|
||
take: 20,
|
||
});
|
||
})
|
||
);
|
||
|
||
results.push(
|
||
await measureQuery('产品统计(活跃产品数)', async () => {
|
||
return await prisma.product.count({
|
||
where: { deletedAt: null },
|
||
});
|
||
})
|
||
);
|
||
|
||
// ============================================
|
||
// SKU 查询测试
|
||
// ============================================
|
||
console.log('\n📦 SKU 查询测试:');
|
||
|
||
results.push(
|
||
await measureQuery('SKU 列表(按产品)', async () => {
|
||
const product = await prisma.product.findFirst();
|
||
return await prisma.productSKU.findMany({
|
||
where: { productId: product?.id },
|
||
});
|
||
})
|
||
);
|
||
|
||
results.push(
|
||
await measureQuery('SKU 列表(产品+状态)', async () => {
|
||
const product = await prisma.product.findFirst();
|
||
return await prisma.productSKU.findMany({
|
||
where: {
|
||
productId: product?.id,
|
||
isActive: true,
|
||
deletedAt: null,
|
||
},
|
||
});
|
||
})
|
||
);
|
||
|
||
results.push(
|
||
await measureQuery('SKU 统计(活跃 SKU 数)', async () => {
|
||
return await prisma.productSKU.count({
|
||
where: {
|
||
isActive: true,
|
||
deletedAt: null,
|
||
},
|
||
});
|
||
})
|
||
);
|
||
|
||
// ============================================
|
||
// 用户查询测试
|
||
// ============================================
|
||
console.log('\n👤 用户查询测试:');
|
||
|
||
results.push(
|
||
await measureQuery('用户列表(按部门)', async () => {
|
||
const dept = await prisma.department.findFirst();
|
||
return await prisma.user.findMany({
|
||
where: { departmentId: dept?.id },
|
||
});
|
||
})
|
||
);
|
||
|
||
results.push(
|
||
await measureQuery('用户列表(部门+状态)', async () => {
|
||
const dept = await prisma.department.findFirst();
|
||
return await prisma.user.findMany({
|
||
where: {
|
||
departmentId: dept?.id,
|
||
status: 'ACTIVE',
|
||
},
|
||
});
|
||
})
|
||
);
|
||
|
||
results.push(
|
||
await measureQuery('用户列表(按角色)', async () => {
|
||
return await prisma.user.findMany({
|
||
where: { role: 'DESIGNER' },
|
||
});
|
||
})
|
||
);
|
||
|
||
results.push(
|
||
await measureQuery('用户统计(活跃用户数)', async () => {
|
||
return await prisma.user.count({
|
||
where: { status: 'ACTIVE' },
|
||
});
|
||
})
|
||
);
|
||
|
||
// ============================================
|
||
// 反馈查询测试
|
||
// ============================================
|
||
console.log('\n💬 反馈查询测试:');
|
||
|
||
results.push(
|
||
await measureQuery('反馈列表(按产品)', async () => {
|
||
const product = await prisma.product.findFirst();
|
||
return await prisma.feedback.findMany({
|
||
where: { productId: product?.id },
|
||
});
|
||
})
|
||
);
|
||
|
||
results.push(
|
||
await measureQuery('反馈列表(产品+状态)', async () => {
|
||
const product = await prisma.product.findFirst();
|
||
return await prisma.feedback.findMany({
|
||
where: {
|
||
productId: product?.id,
|
||
status: 'PENDING',
|
||
},
|
||
});
|
||
})
|
||
);
|
||
|
||
results.push(
|
||
await measureQuery('反馈列表(优先级+状态)', async () => {
|
||
return await prisma.feedback.findMany({
|
||
where: {
|
||
priority: 'HIGH',
|
||
status: 'PENDING',
|
||
},
|
||
});
|
||
})
|
||
);
|
||
|
||
results.push(
|
||
await measureQuery('反馈统计(待处理数)', async () => {
|
||
return await prisma.feedback.count({
|
||
where: { status: 'PENDING' },
|
||
});
|
||
})
|
||
);
|
||
|
||
results.push(
|
||
await measureQuery('反馈统计(按状态分组)', async () => {
|
||
return await prisma.feedback.groupBy({
|
||
by: ['status'],
|
||
_count: true,
|
||
});
|
||
})
|
||
);
|
||
|
||
// ============================================
|
||
// 操作日志查询测试
|
||
// ============================================
|
||
console.log('\n📝 操作日志查询测试:');
|
||
|
||
results.push(
|
||
await measureQuery('日志列表(按用户)', async () => {
|
||
const user = await prisma.user.findFirst();
|
||
return await prisma.activityLog.findMany({
|
||
where: { userId: user?.id },
|
||
take: 20,
|
||
});
|
||
})
|
||
);
|
||
|
||
results.push(
|
||
await measureQuery('日志列表(用户+时间排序)', async () => {
|
||
const user = await prisma.user.findFirst();
|
||
return await prisma.activityLog.findMany({
|
||
where: { userId: user?.id },
|
||
orderBy: { createdAt: 'desc' },
|
||
take: 20,
|
||
});
|
||
})
|
||
);
|
||
|
||
results.push(
|
||
await measureQuery('日志列表(按类型)', async () => {
|
||
return await prisma.activityLog.findMany({
|
||
where: { type: 'PRODUCT_CREATE' },
|
||
take: 20,
|
||
});
|
||
})
|
||
);
|
||
|
||
results.push(
|
||
await measureQuery('日志列表(类型+时间排序)', async () => {
|
||
return await prisma.activityLog.findMany({
|
||
where: { type: 'PRODUCT_CREATE' },
|
||
orderBy: { createdAt: 'desc' },
|
||
take: 20,
|
||
});
|
||
})
|
||
);
|
||
|
||
results.push(
|
||
await measureQuery('日志统计(按类型分组)', async () => {
|
||
return await prisma.activityLog.groupBy({
|
||
by: ['type'],
|
||
_count: true,
|
||
});
|
||
})
|
||
);
|
||
|
||
// ============================================
|
||
// 复杂查询测试
|
||
// ============================================
|
||
console.log('\n🔍 复杂查询测试:');
|
||
|
||
results.push(
|
||
await measureQuery('产品详情(含 SKU)', async () => {
|
||
const product = await prisma.product.findFirst();
|
||
return await prisma.product.findUnique({
|
||
where: { id: product?.id },
|
||
include: {
|
||
skus: true,
|
||
assemblySteps: true,
|
||
panels: true,
|
||
},
|
||
});
|
||
})
|
||
);
|
||
|
||
results.push(
|
||
await measureQuery('用户详情(含部门和小组)', async () => {
|
||
const user = await prisma.user.findFirst();
|
||
return await prisma.user.findUnique({
|
||
where: { id: user?.id },
|
||
include: {
|
||
department: true,
|
||
team: true,
|
||
},
|
||
});
|
||
})
|
||
);
|
||
|
||
results.push(
|
||
await measureQuery('部门树(含子部门)', async () => {
|
||
return await prisma.department.findMany({
|
||
where: { parentId: null },
|
||
include: {
|
||
children: {
|
||
include: {
|
||
children: true,
|
||
},
|
||
},
|
||
},
|
||
});
|
||
})
|
||
);
|
||
|
||
// ============================================
|
||
// 性能总结
|
||
// ============================================
|
||
console.log('\n📊 性能总结:');
|
||
console.log('─'.repeat(60));
|
||
|
||
const avgDuration = results.reduce((sum, r) => sum + r.duration, 0) / results.length;
|
||
const maxDuration = Math.max(...results.map((r) => r.duration));
|
||
const minDuration = Math.min(...results.map((r) => r.duration));
|
||
|
||
console.log(` 平均响应时间: ${avgDuration.toFixed(2)}ms`);
|
||
console.log(` 最快查询: ${minDuration}ms`);
|
||
console.log(` 最慢查询: ${maxDuration}ms`);
|
||
|
||
const fastQueries = results.filter((r) => r.duration < 50).length;
|
||
const mediumQueries = results.filter((r) => r.duration >= 50 && r.duration < 100).length;
|
||
const slowQueries = results.filter((r) => r.duration >= 100).length;
|
||
|
||
console.log(`\n 性能分布:`);
|
||
console.log(` < 50ms: ${fastQueries} 个查询 (${((fastQueries / results.length) * 100).toFixed(1)}%)`);
|
||
console.log(` 50-100ms: ${mediumQueries} 个查询 (${((mediumQueries / results.length) * 100).toFixed(1)}%)`);
|
||
console.log(` > 100ms: ${slowQueries} 个查询 (${((slowQueries / results.length) * 100).toFixed(1)}%)`);
|
||
|
||
console.log('\n─'.repeat(60));
|
||
|
||
if (avgDuration < 100) {
|
||
console.log('✅ 性能优秀!平均响应时间 < 100ms');
|
||
} else if (avgDuration < 200) {
|
||
console.log('⚠️ 性能良好,但有优化空间');
|
||
} else {
|
||
console.log('❌ 性能需要优化');
|
||
}
|
||
|
||
// 找出最慢的查询
|
||
const slowestQueries = results.sort((a, b) => b.duration - a.duration).slice(0, 5);
|
||
console.log('\n🐌 最慢的 5 个查询:');
|
||
slowestQueries.forEach((r, i) => {
|
||
console.log(` ${i + 1}. ${r.name}: ${r.duration}ms`);
|
||
});
|
||
|
||
console.log('\n✅ 性能测试完成!');
|
||
}
|
||
|
||
main()
|
||
.catch((e) => {
|
||
console.error('❌ 性能测试失败:', e);
|
||
process.exit(1);
|
||
})
|
||
.finally(async () => {
|
||
await prisma.$disconnect();
|
||
});
|