# 解班客项目性能优化文档 ## 📋 文档概述 本文档详细说明解班客项目的性能优化策略、监控方案和优化实践,涵盖前端、后端、数据库和基础设施的全方位性能优化。 ### 文档目标 - 建立完整的性能监控体系 - 提供系统性的性能优化方案 - 制定性能基准和优化目标 - 建立性能问题诊断和解决流程 ## 🎯 性能目标和指标 ### 核心性能指标 #### 前端性能指标 ```javascript // 前端性能目标 const FRONTEND_PERFORMANCE_TARGETS = { // 页面加载性能 pageLoad: { FCP: 1.5, // 首次内容绘制 < 1.5s LCP: 2.5, // 最大内容绘制 < 2.5s FID: 100, // 首次输入延迟 < 100ms CLS: 0.1, // 累积布局偏移 < 0.1 TTI: 3.5 // 可交互时间 < 3.5s }, // 资源加载 resources: { jsBundle: 250, // JS包大小 < 250KB cssBundle: 50, // CSS包大小 < 50KB images: 100, // 图片大小 < 100KB fonts: 30 // 字体大小 < 30KB }, // 运行时性能 runtime: { fps: 60, // 帧率 >= 60fps memoryUsage: 50, // 内存使用 < 50MB cpuUsage: 30 // CPU使用率 < 30% } } // 后端性能指标 const BACKEND_PERFORMANCE_TARGETS = { // API响应时间 api: { p50: 200, // 50%请求 < 200ms p95: 500, // 95%请求 < 500ms p99: 1000 // 99%请求 < 1000ms }, // 吞吐量 throughput: { rps: 1000, // 每秒请求数 >= 1000 concurrent: 500 // 并发用户数 >= 500 }, // 资源使用 resources: { cpu: 70, // CPU使用率 < 70% memory: 80, // 内存使用率 < 80% disk: 85 // 磁盘使用率 < 85% } } // 数据库性能指标 const DATABASE_PERFORMANCE_TARGETS = { // 查询性能 query: { select: 50, // SELECT查询 < 50ms insert: 100, // INSERT操作 < 100ms update: 150, // UPDATE操作 < 150ms delete: 100 // DELETE操作 < 100ms }, // 连接池 connection: { poolSize: 20, // 连接池大小 maxWait: 5000, // 最大等待时间 < 5s activeRatio: 0.8 // 活跃连接比例 < 80% } } ``` ### 性能监控仪表板 ```javascript // 性能监控配置 class PerformanceMonitor { constructor() { this.metrics = new Map() this.alerts = new Map() this.collectors = [] } // 初始化监控 initialize() { // 前端性能监控 this.initWebVitalsMonitoring() // 后端性能监控 this.initServerMonitoring() // 数据库性能监控 this.initDatabaseMonitoring() // 基础设施监控 this.initInfrastructureMonitoring() } // Web Vitals监控 initWebVitalsMonitoring() { // 使用Web Vitals库 import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => { getCLS(this.onCLS.bind(this)) getFID(this.onFID.bind(this)) getFCP(this.onFCP.bind(this)) getLCP(this.onLCP.bind(this)) getTTFB(this.onTTFB.bind(this)) }) // 自定义性能指标 this.monitorCustomMetrics() } // 处理CLS指标 onCLS(metric) { this.recordMetric('CLS', metric.value) if (metric.value > FRONTEND_PERFORMANCE_TARGETS.pageLoad.CLS) { this.triggerAlert('CLS_HIGH', { value: metric.value, threshold: FRONTEND_PERFORMANCE_TARGETS.pageLoad.CLS, url: window.location.href }) } } // 处理FID指标 onFID(metric) { this.recordMetric('FID', metric.value) if (metric.value > FRONTEND_PERFORMANCE_TARGETS.pageLoad.FID) { this.triggerAlert('FID_HIGH', { value: metric.value, threshold: FRONTEND_PERFORMANCE_TARGETS.pageLoad.FID, url: window.location.href }) } } // 自定义性能监控 monitorCustomMetrics() { // 监控资源加载时间 this.monitorResourceTiming() // 监控API请求性能 this.monitorAPIPerformance() // 监控内存使用 this.monitorMemoryUsage() // 监控帧率 this.monitorFrameRate() } // 资源加载时间监控 monitorResourceTiming() { const observer = new PerformanceObserver((list) => { for (const entry of list.getEntries()) { if (entry.entryType === 'resource') { const loadTime = entry.responseEnd - entry.startTime this.recordMetric('resource_load_time', { name: entry.name, type: this.getResourceType(entry.name), loadTime: loadTime, size: entry.transferSize }) // 检查是否超过阈值 if (this.isResourceLoadTimeSlow(entry.name, loadTime)) { this.triggerAlert('SLOW_RESOURCE', { resource: entry.name, loadTime: loadTime }) } } } }) observer.observe({ entryTypes: ['resource'] }) } // API性能监控 monitorAPIPerformance() { const originalFetch = window.fetch window.fetch = async (...args) => { const startTime = performance.now() const url = args[0] try { const response = await originalFetch(...args) const endTime = performance.now() const duration = endTime - startTime this.recordMetric('api_request', { url: url, method: args[1]?.method || 'GET', status: response.status, duration: duration, success: response.ok }) // 检查API响应时间 if (duration > 1000) { // 超过1秒 this.triggerAlert('SLOW_API', { url: url, duration: duration, status: response.status }) } return response } catch (error) { const endTime = performance.now() const duration = endTime - startTime this.recordMetric('api_request', { url: url, method: args[1]?.method || 'GET', duration: duration, success: false, error: error.message }) this.triggerAlert('API_ERROR', { url: url, error: error.message, duration: duration }) throw error } } } // 内存使用监控 monitorMemoryUsage() { if ('memory' in performance) { setInterval(() => { const memory = performance.memory this.recordMetric('memory_usage', { used: memory.usedJSHeapSize, total: memory.totalJSHeapSize, limit: memory.jsHeapSizeLimit, usage_ratio: memory.usedJSHeapSize / memory.jsHeapSizeLimit }) // 检查内存使用率 const usageRatio = memory.usedJSHeapSize / memory.jsHeapSizeLimit if (usageRatio > 0.8) { // 超过80% this.triggerAlert('HIGH_MEMORY_USAGE', { usage: memory.usedJSHeapSize, limit: memory.jsHeapSizeLimit, ratio: usageRatio }) } }, 30000) // 每30秒检查一次 } } // 帧率监控 monitorFrameRate() { let lastTime = performance.now() let frameCount = 0 const measureFPS = () => { frameCount++ const currentTime = performance.now() if (currentTime - lastTime >= 1000) { // 每秒计算一次 const fps = Math.round((frameCount * 1000) / (currentTime - lastTime)) this.recordMetric('fps', fps) if (fps < 30) { // 低于30fps this.triggerAlert('LOW_FPS', { fps: fps }) } frameCount = 0 lastTime = currentTime } requestAnimationFrame(measureFPS) } requestAnimationFrame(measureFPS) } // 记录指标 recordMetric(name, value) { const timestamp = Date.now() if (!this.metrics.has(name)) { this.metrics.set(name, []) } this.metrics.get(name).push({ timestamp: timestamp, value: value }) // 保持最近1000条记录 const records = this.metrics.get(name) if (records.length > 1000) { records.splice(0, records.length - 1000) } // 发送到监控服务 this.sendToMonitoringService(name, value, timestamp) } // 触发告警 triggerAlert(type, data) { const alert = { type: type, data: data, timestamp: Date.now(), url: window.location.href, userAgent: navigator.userAgent } console.warn('Performance Alert:', alert) // 发送告警到监控服务 this.sendAlertToService(alert) } // 发送数据到监控服务 async sendToMonitoringService(metric, value, timestamp) { try { await fetch('/api/monitoring/metrics', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ metric: metric, value: value, timestamp: timestamp, url: window.location.href, session_id: this.getSessionId() }) }) } catch (error) { console.error('Failed to send metric to monitoring service:', error) } } // 发送告警到服务 async sendAlertToService(alert) { try { await fetch('/api/monitoring/alerts', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(alert) }) } catch (error) { console.error('Failed to send alert to monitoring service:', error) } } // 获取会话ID getSessionId() { let sessionId = sessionStorage.getItem('performance_session_id') if (!sessionId) { sessionId = 'session_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9) sessionStorage.setItem('performance_session_id', sessionId) } return sessionId } } // 初始化性能监控 const performanceMonitor = new PerformanceMonitor() performanceMonitor.initialize() ``` ## 🚀 前端性能优化 ### 代码分割和懒加载 #### Vue路由懒加载 ```javascript // router/index.js - 路由懒加载配置 import { createRouter, createWebHistory } from 'vue-router' const routes = [ { path: '/', name: 'Home', component: () => import(/* webpackChunkName: "home" */ '@/views/Home.vue') }, { path: '/animals', name: 'Animals', component: () => import(/* webpackChunkName: "animals" */ '@/views/Animals.vue') }, { path: '/adopt', name: 'Adopt', component: () => import(/* webpackChunkName: "adopt" */ '@/views/Adopt.vue') }, { path: '/profile', name: 'Profile', component: () => import(/* webpackChunkName: "profile" */ '@/views/Profile.vue'), meta: { requiresAuth: true } }, { path: '/admin', name: 'Admin', component: () => import(/* webpackChunkName: "admin" */ '@/views/admin/AdminLayout.vue'), children: [ { path: 'dashboard', component: () => import(/* webpackChunkName: "admin-dashboard" */ '@/views/admin/Dashboard.vue') }, { path: 'users', component: () => import(/* webpackChunkName: "admin-users" */ '@/views/admin/Users.vue') }, { path: 'animals', component: () => import(/* webpackChunkName: "admin-animals" */ '@/views/admin/Animals.vue') } ] } ] const router = createRouter({ history: createWebHistory(), routes }) export default router ``` #### 组件懒加载 ```vue ``` #### 虚拟滚动组件 ```vue ``` ### 图片优化和懒加载 #### 图片懒加载指令 ```javascript // directives/lazyload.js - 图片懒加载指令 export const lazyload = { mounted(el, binding) { const { value: src, modifiers } = binding // 创建占位符 const placeholder = modifiers.placeholder ? '/images/placeholder.svg' : 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMzAwIiBoZWlnaHQ9IjIwMCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZGRkIi8+PHRleHQgeD0iNTAlIiB5PSI1MCUiIGZvbnQtZmFtaWx5PSJBcmlhbCIgZm9udC1zaXplPSIxNCIgZmlsbD0iIzk5OSIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZHk9Ii4zZW0iPkxvYWRpbmcuLi48L3RleHQ+PC9zdmc+' el.src = placeholder // 创建Intersection Observer const observer = new IntersectionObserver((entries) => { entries.forEach(entry => { if (entry.isIntersecting) { const img = entry.target // 预加载图片 const imageLoader = new Image() imageLoader.onload = () => { // 添加淡入效果 img.style.transition = 'opacity 0.3s' img.style.opacity = '0' img.src = src // 图片加载完成后淡入 img.onload = () => { img.style.opacity = '1' img.classList.add('loaded') } } imageLoader.onerror = () => { // 加载失败时显示错误图片 img.src = '/images/error.svg' img.classList.add('error') } imageLoader.src = src // 停止观察 observer.unobserve(img) } }) }, { rootMargin: '50px' // 提前50px开始加载 }) observer.observe(el) // 保存observer引用以便清理 el._lazyloadObserver = observer }, unmounted(el) { if (el._lazyloadObserver) { el._lazyloadObserver.disconnect() } } } // 图片优化组件 // components/OptimizedImage.vue ``` ```vue ``` ### 缓存策略优化 #### Service Worker缓存 ```javascript // public/sw.js - Service Worker缓存策略 const CACHE_NAME = 'jiebanke-v1.0.0' const STATIC_CACHE = 'jiebanke-static-v1.0.0' const DYNAMIC_CACHE = 'jiebanke-dynamic-v1.0.0' const IMAGE_CACHE = 'jiebanke-images-v1.0.0' // 需要缓存的静态资源 const STATIC_ASSETS = [ '/', '/manifest.json', '/css/app.css', '/js/app.js', '/images/logo.svg', '/images/placeholder.svg', '/images/error.svg', '/fonts/roboto-regular.woff2', '/fonts/roboto-medium.woff2' ] // 缓存策略配置 const CACHE_STRATEGIES = { // 静态资源:缓存优先 static: { pattern: /\.(css|js|woff2?|svg|png|jpg|jpeg|gif|ico)$/, strategy: 'cacheFirst', maxAge: 30 * 24 * 60 * 60 * 1000, // 30天 maxEntries: 100 }, // API请求:网络优先 api: { pattern: /^https?:\/\/.*\/api\//, strategy: 'networkFirst', maxAge: 5 * 60 * 1000, // 5分钟 maxEntries: 50 }, // 图片:缓存优先 images: { pattern: /\.(png|jpg|jpeg|gif|webp|svg)$/, strategy: 'cacheFirst', maxAge: 7 * 24 * 60 * 60 * 1000, // 7天 maxEntries: 200 }, // HTML页面:网络优先 pages: { pattern: /\.html$|\/$/, strategy: 'networkFirst', maxAge: 24 * 60 * 60 * 1000, // 1天 maxEntries: 20 } } // 安装事件 self.addEventListener('install', (event) => { console.log('Service Worker installing...') event.waitUntil( caches.open(STATIC_CACHE) .then((cache) => { console.log('Caching static assets') return cache.addAll(STATIC_ASSETS) }) .then(() => { console.log('Static assets cached') return self.skipWaiting() }) ) }) // 激活事件 self.addEventListener('activate', (event) => { console.log('Service Worker activating...') event.waitUntil( caches.keys() .then((cacheNames) => { return Promise.all( cacheNames.map((cacheName) => { // 删除旧版本缓存 if (cacheName !== STATIC_CACHE && cacheName !== DYNAMIC_CACHE && cacheName !== IMAGE_CACHE) { console.log('Deleting old cache:', cacheName) return caches.delete(cacheName) } }) ) }) .then(() => { console.log('Service Worker activated') return self.clients.claim() }) ) }) // 拦截请求 self.addEventListener('fetch', (event) => { const { request } = event const url = new URL(request.url) // 只处理同源请求 if (url.origin !== location.origin) { return } // 根据请求类型选择缓存策略 const strategy = getStrategy(request) if (strategy) { event.respondWith(handleRequest(request, strategy)) } }) // 获取缓存策略 function getStrategy(request) { const url = request.url for (const [name, config] of Object.entries(CACHE_STRATEGIES)) { if (config.pattern.test(url)) { return { name, ...config } } } return null } // 处理请求 async function handleRequest(request, strategy) { switch (strategy.strategy) { case 'cacheFirst': return cacheFirst(request, strategy) case 'networkFirst': return networkFirst(request, strategy) case 'staleWhileRevalidate': return staleWhileRevalidate(request, strategy) default: return fetch(request) } } // 缓存优先策略 async function cacheFirst(request, strategy) { const cacheName = getCacheName(strategy.name) const cache = await caches.open(cacheName) const cachedResponse = await cache.match(request) if (cachedResponse) { // 检查缓存是否过期 const cacheTime = await getCacheTime(cache, request) if (cacheTime && Date.now() - cacheTime < strategy.maxAge) { return cachedResponse } } try { const networkResponse = await fetch(request) if (networkResponse.ok) { // 缓存新响应 await cache.put(request, networkResponse.clone()) await setCacheTime(cache, request, Date.now()) // 清理过期缓存 await cleanupCache(cache, strategy.maxEntries) } return networkResponse } catch (error) { // 网络失败时返回缓存 if (cachedResponse) { return cachedResponse } throw error } } // 网络优先策略 async function networkFirst(request, strategy) { const cacheName = getCacheName(strategy.name) const cache = await caches.open(cacheName) try { const networkResponse = await fetch(request) if (networkResponse.ok) { // 缓存响应 await cache.put(request, networkResponse.clone()) await setCacheTime(cache, request, Date.now()) // 清理过期缓存 await cleanupCache(cache, strategy.maxEntries) } return networkResponse } catch (error) { // 网络失败时尝试从缓存获取 const cachedResponse = await cache.match(request) if (cachedResponse) { const cacheTime = await getCacheTime(cache, request) if (!cacheTime || Date.now() - cacheTime < strategy.maxAge) { return cachedResponse } } throw error } } // 过期重新验证策略 async function staleWhileRevalidate(request, strategy) { const cacheName = getCacheName(strategy.name) const cache = await caches.open(cacheName) const cachedResponse = await cache.match(request) // 后台更新缓存 const fetchPromise = fetch(request).then(async (networkResponse) => { if (networkResponse.ok) { await cache.put(request, networkResponse.clone()) await setCacheTime(cache, request, Date.now()) await cleanupCache(cache, strategy.maxEntries) } return networkResponse }) // 如果有缓存,立即返回;否则等待网络响应 return cachedResponse || fetchPromise } // 获取缓存名称 function getCacheName(strategyName) { switch (strategyName) { case 'static': return STATIC_CACHE case 'images': return IMAGE_CACHE default: return DYNAMIC_CACHE } } // 设置缓存时间 async function setCacheTime(cache, request, time) { const timeKey = `${request.url}:timestamp` await cache.put(timeKey, new Response(time.toString())) } // 获取缓存时间 async function getCacheTime(cache, request) { const timeKey = `${request.url}:timestamp` const timeResponse = await cache.match(timeKey) if (timeResponse) { const timeText = await timeResponse.text() return parseInt(timeText, 10) } return null } // 清理过期缓存 async function cleanupCache(cache, maxEntries) { const keys = await cache.keys() // 过滤出非时间戳的键 const contentKeys = keys.filter(key => !key.url.includes(':timestamp')) if (contentKeys.length > maxEntries) { // 删除最旧的条目 const keysToDelete = contentKeys.slice(0, contentKeys.length - maxEntries) for (const key of keysToDelete) { await cache.delete(key) // 同时删除对应的时间戳 await cache.delete(`${key.url}:timestamp`) } } } // 消息处理 self.addEventListener('message', (event) => { if (event.data && event.data.type === 'SKIP_WAITING') { self.skipWaiting() } }) ``` ## 🔧 后端性能优化 ### 数据库查询优化 #### 查询优化策略 ```javascript // models/Animal.js - 动物模型优化 const { Model, DataTypes, Op } = require('sequelize') const sequelize = require('../config/database') class Animal extends Model { // 优化的查询方法 static async findWithPagination(options = {}) { const { page = 1, limit = 20, filters = {}, sort = 'created_at', order = 'DESC', include = [] } = options const offset = (page - 1) * limit // 构建查询条件 const where = this.buildWhereClause(filters) // 优化的查询配置 const queryOptions = { where, limit: parseInt(limit), offset: parseInt(offset), order: [[sort, order]], include: this.buildIncludeClause(include), // 使用索引提示 attributes: { include: [ // 计算字段 [ sequelize.literal(`( SELECT COUNT(*) FROM adoptions WHERE adoptions.animal_id = Animal.id AND adoptions.status = 'completed' )`), 'adoption_count' ] ] }, // 子查询优化 subQuery: false, // 启用查询缓存 benchmark: true, logging: process.env.NODE_ENV === 'development' } // 执行查询 const result = await this.findAndCountAll(queryOptions) return { data: result.rows, pagination: { page: parseInt(page), limit: parseInt(limit), total: result.count, pages: Math.ceil(result.count / limit) } } } // 构建WHERE子句 static buildWhereClause(filters) { const where = {} // 状态过滤 if (filters.status) { where.status = filters.status } // 类型过滤 if (filters.type) { where.type = filters.type } // 年龄范围过滤 if (filters.minAge || filters.maxAge) { where.age = {} if (filters.minAge) { where.age[Op.gte] = filters.minAge } if (filters.maxAge) { where.age[Op.lte] = filters.maxAge } } // 地区过滤 if (filters.location) { where.location = { [Op.like]: `%${filters.location}%` } } // 关键词搜索 if (filters.keyword) { where[Op.or] = [ { name: { [Op.like]: `%${filters.keyword}%` } }, { description: { [Op.like]: `%${filters.keyword}%` } }, { breed: { [Op.like]: `%${filters.keyword}%` } } ] } // 日期范围过滤 if (filters.dateFrom || filters.dateTo) { where.created_at = {} if (filters.dateFrom) { where.created_at[Op.gte] = new Date(filters.dateFrom) } if (filters.dateTo) { where.created_at[Op.lte] = new Date(filters.dateTo) } } return where } // 构建INCLUDE子句 static buildIncludeClause(include) { const includes = [] if (include.includes('images')) { includes.push({ model: require('./AnimalImage'), as: 'images', attributes: ['id', 'url', 'is_primary'], where: { is_deleted: false }, required: false, // 只获取主图片以提高性能 limit: 1, order: [['is_primary', 'DESC'], ['created_at', 'ASC']] }) } if (include.includes('shelter')) { includes.push({ model: require('./Shelter'), as: 'shelter', attributes: ['id', 'name', 'location', 'contact_phone'] }) } if (include.includes('adoptions')) { includes.push({ model: require('./Adoption'), as: 'adoptions', attributes: ['id', 'status', 'created_at'], where: { status: { [Op.ne]: 'cancelled' } }, required: false }) } return includes } // 批量更新优化 static async batchUpdate(updates) { const transaction = await sequelize.transaction() try { const results = [] // 分批处理,避免单次更新过多记录 const batchSize = 100 for (let i = 0; i < updates.length; i += batchSize) { const batch = updates.slice(i, i + batchSize) const batchPromises = batch.map(update => { return this.update( update.data, { where: { id: update.id }, transaction, // 只返回受影响的行数,不返回完整记录 returning: false } ) }) const batchResults = await Promise.all(batchPromises) results.push(...batchResults) } await transaction.commit() return results } catch (error) { await transaction.rollback() throw error } } // 统计查询优化 static async getStatistics(filters = {}) { const where = this.buildWhereClause(filters) // 使用原生SQL进行复杂统计查询 const [results] = await sequelize.query(` SELECT COUNT(*) as total_count, COUNT(CASE WHEN status = 'available' THEN 1 END) as available_count, COUNT(CASE WHEN status = 'adopted' THEN 1 END) as adopted_count, COUNT(CASE WHEN status = 'pending' THEN 1 END) as pending_count, AVG(age) as average_age, COUNT(CASE WHEN type = 'dog' THEN 1 END) as dog_count, COUNT(CASE WHEN type = 'cat' THEN 1 END) as cat_count, COUNT(CASE WHEN created_at >= DATE_SUB(NOW(), INTERVAL 30 DAY) THEN 1 END) as recent_count FROM animals WHERE ${this.buildSQLWhereClause(where)} `) return results[0] } // 构建SQL WHERE子句 static buildSQLWhereClause(where) { // 这里需要根据实际的where对象构建SQL条件 // 简化示例 return '1=1' // 实际实现需要更复杂的逻辑 } } // 定义模型 Animal.init({ id: { type: DataTypes.INTEGER, primaryKey: true, autoIncrement: true }, name: { type: DataTypes.STRING(100), allowNull: false, validate: { notEmpty: true, len: [1, 100] } }, type: { type: DataTypes.ENUM('dog', 'cat', 'other'), allowNull: false, // 添加索引 index: true }, breed: { type: DataTypes.STRING(100), allowNull: true }, age: { type: DataTypes.INTEGER, allowNull: true, validate: { min: 0, max: 30 }, // 添加索引用于范围查询 index: true }, gender: { type: DataTypes.ENUM('male', 'female', 'unknown'), allowNull: false }, size: { type: DataTypes.ENUM('small', 'medium', 'large'), allowNull: false }, color: { type: DataTypes.STRING(50), allowNull: true }, description: { type: DataTypes.TEXT, allowNull: true }, health_status: { type: DataTypes.STRING(200), allowNull: true }, vaccination_status: { type: DataTypes.BOOLEAN, defaultValue: false }, sterilization_status: { type: DataTypes.BOOLEAN, defaultValue: false }, status: { type: DataTypes.ENUM('available', 'adopted', 'pending', 'unavailable'), allowNull: false, defaultValue: 'available', // 添加索引 index: true }, location: { type: DataTypes.STRING(200), allowNull: true, // 添加索引用于地区查询 index: true }, shelter_id: { type: DataTypes.INTEGER, allowNull: true, references: { model: 'shelters', key: 'id' }, // 外键索引 index: true }, rescue_date: { type: DataTypes.DATE, allowNull: true }, is_deleted: { type: DataTypes.BOOLEAN, defaultValue: false, // 软删除索引 index: true }, created_at: { type: DataTypes.DATE, allowNull: false, defaultValue: DataTypes.NOW, // 时间索引 index: true }, updated_at: { type: DataTypes.DATE, allowNull: false, defaultValue: DataTypes.NOW } }, { sequelize, modelName: 'Animal', tableName: 'animals', timestamps: true, createdAt: 'created_at', updatedAt: 'updated_at', // 复合索引 indexes: [ { name: 'idx_animals_status_type', fields: ['status', 'type'] }, { name: 'idx_animals_location_status', fields: ['location', 'status'] }, { name: 'idx_animals_created_status', fields: ['created_at', 'status'] }, { name: 'idx_animals_age_type', fields: ['age', 'type'] } ], // 默认作用域 defaultScope: { where: { is_deleted: false } }, // 命名作用域 scopes: { available: { where: { status: 'available', is_deleted: false } }, withImages: { include: [{ model: require('./AnimalImage'), as: 'images', where: { is_deleted: false }, required: false }] } } }) module.exports = Animal ``` ### 缓存策略实现 #### Redis缓存服务 ```javascript // services/CacheService.js - 缓存服务 const Redis = require('ioredis') const logger = require('../utils/logger') class CacheService { constructor() { this.redis = new Redis({ host: process.env.REDIS_HOST || 'localhost', port: process.env.REDIS_PORT || 6379, password: process.env.REDIS_PASSWORD, db: process.env.REDIS_DB || 0, retryDelayOnFailover: 100, maxRetriesPerRequest: 3, lazyConnect: true, // 连接池配置 family: 4, keepAlive: true, // 集群配置(如果使用Redis集群) enableOfflineQueue: false }) // 缓存配置 this.config = { // 默认过期时间(秒) defaultTTL: 3600, // 1小时 // 不同类型数据的TTL ttl: { user: 1800, // 用户信息 30分钟 animal: 3600, // 动物信息 1小时 statistics: 300, // 统计数据 5分钟 search: 600, // 搜索结果 10分钟 session: 86400, // 会话 24小时 config: 7200 // 配置信息 2小时 }, // 缓存键前缀 prefix: { user: 'user:', animal: 'animal:', list: 'list:', search: 'search:', statistics: 'stats:', session: 'session:', lock: 'lock:' } } this.setupEventHandlers() } // 设置事件处理器 setupEventHandlers() { this.redis.on('connect', () => { logger.info('Redis connected') }) this.redis.on('error', (error) => { logger.error('Redis error:', error) }) this.redis.on('close', () => { logger.warn('Redis connection closed') }) } // 生成缓存键 generateKey(type, identifier, suffix = '') { const prefix = this.config.prefix[type] || '' return `${prefix}${identifier}${suffix ? ':' + suffix : ''}` } // 设置缓存 async set(key, value, ttl = null) { try { const serializedValue = JSON.stringify(value) const expireTime = ttl || this.config.defaultTTL await this.redis.setex(key, expireTime, serializedValue) logger.debug(`Cache set: ${key} (TTL: ${expireTime}s)`) return true } catch (error) { logger.error('Cache set error:', error) return false } } // 获取缓存 async get(key) { try { const value = await this.redis.get(key) if (value === null) { logger.debug(`Cache miss: ${key}`) return null } logger.debug(`Cache hit: ${key}`) return JSON.parse(value) } catch (error) { logger.error('Cache get error:', error) return null } } // 删除缓存 async del(key) { try { const result = await this.redis.del(key) logger.debug(`Cache deleted: ${key}`) return result > 0 } catch (error) { logger.error('Cache delete error:', error) return false } } // 批量删除缓存 async delPattern(pattern) { try { const keys = await this.redis.keys(pattern) if (keys.length > 0) { const result = await this.redis.del(...keys) logger.debug(`Cache pattern deleted: ${pattern} (${keys.length} keys)`) return result } return 0 } catch (error) { logger.error('Cache pattern delete error:', error) return 0 } } // 检查缓存是否存在 async exists(key) { try { const result = await this.redis.exists(key) return result === 1 } catch (error) { logger.error('Cache exists check error:', error) return false } } // 设置缓存过期时间 async expire(key, ttl) { try { const result = await this.redis.expire(key, ttl) return result === 1 } catch (error) { logger.error('Cache expire error:', error) return false } } // 获取或设置缓存(缓存穿透保护) async getOrSet(key, fetchFunction, ttl = null) { try { // 先尝试从缓存获取 let value = await this.get(key) if (value !== null) { return value } // 使用分布式锁防止缓存击穿 const lockKey = this.generateKey('lock', key) const lockAcquired = await this.acquireLock(lockKey, 10) // 10秒锁 if (!lockAcquired) { // 如果获取锁失败,等待一段时间后重试 await this.sleep(100) value = await this.get(key) if (value !== null) { return value } } try { // 执行数据获取函数 value = await fetchFunction() // 缓存结果(即使是null也要缓存,防止缓存穿透) const cacheValue = value !== null ? value : { __null: true } const expireTime = ttl || this.getTTL(key) await this.set(key, cacheValue, expireTime) return value } finally { // 释放锁 if (lockAcquired) { await this.releaseLock(lockKey) } } } catch (error) { logger.error('Cache getOrSet error:', error) // 如果缓存操作失败,直接执行函数 return await fetchFunction() } } // 获取TTL getTTL(key) { // 根据键名确定TTL for (const [type, prefix] of Object.entries(this.config.prefix)) { if (key.startsWith(prefix)) { return this.config.ttl[type] || this.config.defaultTTL } } return this.config.defaultTTL } // 获取分布式锁 async acquireLock(lockKey, expireTime = 10) { try { const result = await this.redis.set(lockKey, '1', 'EX', expireTime, 'NX') return result === 'OK' } catch (error) { logger.error('Lock acquire error:', error) return false } } // 释放分布式锁 async releaseLock(lockKey) { try { await this.redis.del(lockKey) return true } catch (error) { logger.error('Lock release error:', error) return false } } // 睡眠函数 sleep(ms) { return new Promise(resolve => setTimeout(resolve, ms)) } // 缓存用户信息 async cacheUser(userId, userData) { const key = this.generateKey('user', userId) return await this.set(key, userData, this.config.ttl.user) } // 获取用户缓存 async getUser(userId) { const key = this.generateKey('user', userId) return await this.get(key) } // 删除用户缓存 async deleteUser(userId) { const key = this.generateKey('user', userId) return await this.del(key) } // 缓存动物列表 async cacheAnimalList(filters, data) { const key = this.generateKey('list', 'animals', this.hashFilters(filters)) return await this.set(key, data, this.config.ttl.animal) } // 获取动物列表缓存 async getAnimalList(filters) { const key = this.generateKey('list', 'animals', this.hashFilters(filters)) return await this.get(key) } // 删除动物相关缓存 async deleteAnimalCache(animalId = null) { if (animalId) { // 删除特定动物缓存 const key = this.generateKey('animal', animalId) await this.del(key) } // 删除所有动物列表缓存 await this.delPattern(this.generateKey('list', 'animals', '*')) } // 哈希过滤器参数 hashFilters(filters) { const crypto = require('crypto') const filterString = JSON.stringify(filters, Object.keys(filters).sort()) return crypto.createHash('md5').update(filterString).digest('hex') } // 缓存统计数据 async cacheStatistics(type, data) { const key = this.generateKey('statistics', type) return await this.set(key, data, this.config.ttl.statistics) } // 获取统计缓存 async getStatistics(type) { const key = this.generateKey('statistics', type) return await this.get(key) } // 批量操作 async mget(keys) { try { const values = await this.redis.mget(...keys) return values.map(value => value ? JSON.parse(value) : null) } catch (error) { logger.error('Cache mget error:', error) return new Array(keys.length).fill(null) } } async mset(keyValuePairs, ttl = null) { try { const pipeline = this.redis.pipeline() const expireTime = ttl || this.config.defaultTTL for (const [key, value] of keyValuePairs) { pipeline.setex(key, expireTime, JSON.stringify(value)) } await pipeline.exec() return true } catch (error) { logger.error('Cache mset error:', error) return false } } // 关闭连接 async close() { await this.redis.quit() } } module.exports = new CacheService() ``` ### API响应优化 #### 响应压缩和优化中间件 ```javascript // middleware/optimization.js - 性能优化中间件 const compression = require('compression') const helmet = require('helmet') const rateLimit = require('express-rate-limit') const slowDown = require('express-slow-down') const responseTime = require('response-time') const cacheService = require('../services/CacheService') const logger = require('../utils/logger') // 响应压缩中间件 const compressionMiddleware = compression({ // 压缩级别 (1-9, 9最高) level: 6, // 压缩阈值,小于1KB的响应不压缩 threshold: 1024, // 过滤器函数 filter: (req, res) => { // 不压缩已经压缩的内容 if (req.headers['x-no-compression']) { return false } // 只压缩文本内容 const contentType = res.getHeader('content-type') if (contentType) { return /text|json|javascript|css|xml|svg/.test(contentType) } return compression.filter(req, res) } }) // 安全头中间件 const securityMiddleware = helmet({ contentSecurityPolicy: { directives: { defaultSrc: ["'self'"], styleSrc: ["'self'", "'unsafe-inline'", "https://fonts.googleapis.com"], fontSrc: ["'self'", "https://fonts.gstatic.com"], imgSrc: ["'self'", "data:", "https:"], scriptSrc: ["'self'"], connectSrc: ["'self'", "https://api.jiebanke.com"] } }, hsts: { maxAge: 31536000, includeSubDomains: true, preload: true } }) // 速率限制中间件 const rateLimitMiddleware = rateLimit({ windowMs: 15 * 60 * 1000, // 15分钟 max: 1000, // 每个IP最多1000次请求 message: { error: 'Too many requests', message: '请求过于频繁,请稍后再试' }, standardHeaders: true, legacyHeaders: false, // 自定义键生成器 keyGenerator: (req) => { // 优先使用用户ID,其次使用IP return req.user?.id || req.ip }, // 跳过成功的请求 skipSuccessfulRequests: false, // 跳过失败的请求 skipFailedRequests: true }) // API速率限制(更严格) const apiRateLimitMiddleware = rateLimit({ windowMs: 15 * 60 * 1000, // 15分钟 max: 500, // API请求限制更严格 message: { error: 'API rate limit exceeded', message: 'API请求频率超限,请稍后再试' } }) // 慢速响应中间件 const slowDownMiddleware = slowDown({ windowMs: 15 * 60 * 1000, // 15分钟 delayAfter: 100, // 100次请求后开始延迟 delayMs: 500, // 每次增加500ms延迟 maxDelayMs: 20000 // 最大延迟20秒 }) // 响应时间中间件 const responseTimeMiddleware = responseTime((req, res, time) => { // 记录慢查询 if (time > 1000) { // 超过1秒 logger.warn('Slow request detected', { method: req.method, url: req.url, responseTime: time, userAgent: req.get('User-Agent'), ip: req.ip }) } // 添加响应时间头 res.set('X-Response-Time', `${time}ms`) }) // 缓存中间件 const cacheMiddleware = (options = {}) => { const { ttl = 300, // 默认5分钟 keyGenerator = (req) => `${req.method}:${req.originalUrl}`, condition = () => true, vary = ['Accept-Encoding'] } = options return async (req, res, next) => { // 只缓存GET请求 if (req.method !== 'GET') { return next() } // 检查缓存条件 if (!condition(req)) { return next() } const cacheKey = keyGenerator(req) try { // 尝试从缓存获取 const cachedResponse = await cacheService.get(cacheKey) if (cachedResponse) { // 设置缓存头 res.set('X-Cache', 'HIT') res.set('Cache-Control', `public, max-age=${ttl}`) // 设置Vary头 if (vary.length > 0) { res.vary(vary) } // 返回缓存的响应 return res.status(cachedResponse.status).json(cachedResponse.data) } // 缓存未命中,继续处理请求 res.set('X-Cache', 'MISS') // 拦截响应 const originalJson = res.json res.json = function(data) { // 只缓存成功的响应 if (res.statusCode >= 200 && res.statusCode < 300) { const responseData = { status: res.statusCode, data: data } // 异步缓存,不阻塞响应 cacheService.set(cacheKey, responseData, ttl).catch(error => { logger.error('Cache set error:', error) }) } // 调用原始json方法 return originalJson.call(this, data) } next() } catch (error) { logger.error('Cache middleware error:', error) next() } } } // ETag中间件 const etagMiddleware = (req, res, next) => { const originalJson = res.json res.json = function(data) { if (req.method === 'GET' && res.statusCode === 200) { const crypto = require('crypto') const etag = crypto.createHash('md5').update(JSON.stringify(data)).digest('hex') res.set('ETag', `"${etag}"`) // 检查If-None-Match头 const clientEtag = req.get('If-None-Match') if (clientEtag === `"${etag}"`) { return res.status(304).end() } } return originalJson.call(this, data) } next() } // 请求日志中间件 const requestLogMiddleware = (req, res, next) => { const startTime = Date.now() // 记录请求开始 logger.info('Request started', { method: req.method, url: req.url, ip: req.ip, userAgent: req.get('User-Agent'), contentLength: req.get('Content-Length') }) // 监听响应结束 res.on('finish', () => { const duration = Date.now() - startTime logger.info('Request completed', { method: req.method, url: req.url, statusCode: res.statusCode, duration: duration, contentLength: res.get('Content-Length') }) }) next() } // 健康检查中间件 const healthCheckMiddleware = (req, res, next) => { if (req.path === '/health' || req.path === '/ping') { return res.json({ status: 'ok', timestamp: new Date().toISOString(), uptime: process.uptime(), memory: process.memoryUsage(), version: process.env.npm_package_version || '1.0.0' }) } next() } // 错误处理中间件 const errorHandlingMiddleware = (error, req, res, next) => { logger.error('Request error', { error: error.message, stack: error.stack, method: req.method, url: req.url, ip: req.ip }) // 根据错误类型返回不同响应 if (error.name === 'ValidationError') { return res.status(400).json({ error: 'Validation Error', message: error.message, details: error.details }) } if (error.name === 'UnauthorizedError') { return res.status(401).json({ error: 'Unauthorized', message: '认证失败' }) } if (error.name === 'ForbiddenError') { return res.status(403).json({ error: 'Forbidden', message: '权限不足' }) } if (error.name === 'NotFoundError') { return res.status(404).json({ error: 'Not Found', message: '资源不存在' }) } // 默认服务器错误 res.status(500).json({ error: 'Internal Server Error', message: process.env.NODE_ENV === 'production' ? '服务器内部错误' : error.message }) } module.exports = { compressionMiddleware, securityMiddleware, rateLimitMiddleware, apiRateLimitMiddleware, slowDownMiddleware, responseTimeMiddleware, cacheMiddleware, etagMiddleware, requestLogMiddleware, healthCheckMiddleware, errorHandlingMiddleware } ``` ## 📊 监控和分析 ### 性能监控仪表板 ```javascript // utils/PerformanceAnalyzer.js - 性能分析工具 class PerformanceAnalyzer { constructor() { this.metrics = new Map() this.alerts = [] this.thresholds = { responseTime: { warning: 500, // 500ms critical: 1000 // 1s }, errorRate: { warning: 0.01, // 1% critical: 0.05 // 5% }, throughput: { warning: 100, // 100 RPS critical: 50 // 50 RPS }, memoryUsage: { warning: 0.8, // 80% critical: 0.9 // 90% } } } // 记录性能指标 recordMetric(name, value, tags = {}) { const timestamp = Date.now() const metric = { name, value, timestamp, tags } if (!this.metrics.has(name)) { this.metrics.set(name, []) } this.metrics.get(name).push(metric) // 保持最近1000条记录 const records = this.metrics.get(name) if (records.length > 1000) { records.splice(0, records.length - 1000) } // 检查阈值 this.checkThresholds(name, value, tags) } // 检查阈值 checkThresholds(metricName, value, tags) { const threshold = this.thresholds[metricName] if (!threshold) return let level = null if (value >= threshold.critical) { level = 'critical' } else if (value >= threshold.warning) { level = 'warning' } if (level) { this.triggerAlert({ metric: metricName, value, level, threshold: threshold[level], tags, timestamp: Date.now() }) } } // 触发告警 triggerAlert(alert) { this.alerts.push(alert) // 保持最近100条告警 if (this.alerts.length > 100) { this.alerts.splice(0, this.alerts.length - 100) } // 发送告警通知 this.sendAlert(alert) } // 发送告警 async sendAlert(alert) { const message = `性能告警: ${alert.metric} = ${alert.value} (阈值: ${alert.threshold})` console.warn(message, alert) // 这里可以集成邮件、短信、钉钉等告警通道 try { // 发送到监控系统 await fetch('/api/monitoring/alerts', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(alert) }) } catch (error) { console.error('Failed to send alert:', error) } } // 获取性能报告 getPerformanceReport(timeRange = 3600000) { // 默认1小时 const now = Date.now() const startTime = now - timeRange const report = { timeRange: { start: new Date(startTime).toISOString(), end: new Date(now).toISOString() }, metrics: {}, alerts: this.alerts.filter(alert => alert.timestamp >= startTime), summary: {} } // 分析各项指标 for (const [name, records] of this.metrics.entries()) { const filteredRecords = records.filter(record => record.timestamp >= startTime) if (filteredRecords.length === 0) continue const values = filteredRecords.map(record => record.value) report.metrics[name] = { count: filteredRecords.length, min: Math.min(...values), max: Math.max(...values), avg: values.reduce((sum, val) => sum + val, 0) / values.length, p50: this.percentile(values, 0.5), p95: this.percentile(values, 0.95), p99: this.percentile(values, 0.99) } } // 生成摘要 report.summary = this.generateSummary(report) return report } // 计算百分位数 percentile(values, p) { const sorted = values.slice().sort((a, b) => a - b) const index = Math.ceil(sorted.length * p) - 1 return sorted[index] || 0 } // 生成性能摘要 generateSummary(report) { const summary = { status: 'healthy', issues: [], recommendations: [] } // 检查响应时间 const responseTime = report.metrics.responseTime if (responseTime) { if (responseTime.p95 > this.thresholds.responseTime.critical) { summary.status = 'critical' summary.issues.push('95%的请求响应时间超过1秒') summary.recommendations.push('优化数据库查询和缓存策略') } else if (responseTime.p95 > this.thresholds.responseTime.warning) { summary.status = 'warning' summary.issues.push('95%的请求响应时间超过500ms') summary.recommendations.push('检查慢查询和网络延迟') } } // 检查错误率 const errorRate = report.metrics.errorRate if (errorRate && errorRate.avg > this.thresholds.errorRate.warning) { summary.status = summary.status === 'critical' ? 'critical' : 'warning' summary.issues.push(`错误率过高: ${(errorRate.avg * 100).toFixed(2)}%`) summary.recommendations.push('检查应用日志和错误处理') } // 检查内存使用 const memoryUsage = report.metrics.memoryUsage if (memoryUsage && memoryUsage.max > this.thresholds.memoryUsage.critical) { summary.status = 'critical' summary.issues.push('内存使用率超过90%') summary.recommendations.push('检查内存泄漏和优化内存使用') } return summary } } module.exports = PerformanceAnalyzer ``` ## 🎯 总结 本性能优化文档提供了解班客项目的全方位性能优化方案,包括: ### 核心优化策略 1. **前端优化**:代码分割、懒加载、虚拟滚动、图片优化 2. **后端优化**:数据库查询优化、缓存策略、API响应优化 3. **监控体系**:性能指标监控、告警机制、性能分析 ### 性能目标 - 页面加载时间 < 2.5秒 - API响应时间 < 500ms (95%) - 系统可用性 > 99.9% - 并发用户数 > 500 ### 下一步计划 1. 实施性能监控系统 2. 优化关键路径性能 3. 建立性能基准测试 4. 持续性能优化迭代 通过系统性的性能优化,确保解班客项目能够为用户提供快速、稳定、高质量的服务体验。