{T}

代码优化

概述

代码优化是提升 Node.js 应用性能的基础手段。通过优化异步处理、缓存策略、数据库访问、算法复杂度等方面,可以显著提升应用的响应速度和吞吐量。本文档系统介绍各类代码优化技巧和最佳实践。

性能优化策略架构

code
┌─────────────────────────────────────────────────────────────────────┐
│                      代码性能优化策略                                 │
├─────────────────────────────────────────────────────────────────────┤
│                                                                     │
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐│
│  │  异步优化   │  │  缓存优化   │  │  数据库优化  │  │  内存优化   ││
│  ├─────────────┤  ├─────────────┤  ├─────────────┤  ├─────────────┤│
│  │• 并发控制   │  │• 内存缓存   │  │• 连接池     │  │• 对象池     ││
│  │• Promise池 │  │• Redis缓存  │  │• 批量操作   │  │• Stream    ││
│  │• 队列处理   │  │• 多级缓存   │  │• 查询优化   │  │• Buffer    ││
│  │• 错误处理   │  │• 缓存策略   │  │• 事务管理   │  │• GC优化    ││
│  └─────────────┘  └─────────────┘  └─────────────┘  └─────────────┘│
│                                                                     │
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐│
│  │  算法优化   │  │  数据结构   │  │  I/O优化    │  │  网络优化   ││
│  ├─────────────┤  ├─────────────┤  ├─────────────┤  ├─────────────┤│
│  │• 复杂度降低 │  │• Map/Set   │  │• 文件流     │  │• 连接复用   ││
│  │• 循环优化   │  │• TypedArray│  │• 压缩传输   │  │• 批量请求   ││
│  │• 递归转迭代 │  │• 对象索引   │  │• 异步I/O   │  │• DNS缓存   ││
│  │• 记忆化     │  │• WeakMap   │  │• 缓冲策略   │  │• Keep-Alive││
│  └─────────────┘  └─────────────┘  └─────────────┘  └─────────────┘│
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘

异步优化

并发控制模式

顺序执行 vs 并行执行

javascript
/**
 * 异步执行模式对比
 */

// ❌ 顺序执行 - 总时间 = 所有任务时间之和
async function fetchUsersSequential(userIds) {
  const users = [];
  for (const id of userIds) {
    const user = await fetchUser(id); // 串行等待
    users.push(user);
  }
  return users;
}

// ✅ 并行执行 - 总时间 = 最慢任务的时间
async function fetchUsersParallel(userIds) {
  return Promise.all(userIds.map(id => fetchUser(id)));
}

// ✅ 并发限制 - 平衡性能和资源消耗
async function fetchUsersWithLimit(userIds, limit = 5) {
  const results = [];
  
  for (let i = 0; i < userIds.length; i += limit) {
    const batch = userIds.slice(i, i + limit);
    const batchResults = await Promise.all(
      batch.map(id => fetchUser(id))
    );
    results.push(...batchResults);
  }
  
  return results;
}

// 性能对比示例
async function performanceComparison() {
  const userIds = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
  
  console.time('顺序执行');
  await fetchUsersSequential(userIds);
  console.timeEnd('顺序执行'); // ~5000ms (假设每个请求 500ms)
  
  console.time('并行执行');
  await fetchUsersParallel(userIds);
  console.timeEnd('并行执行'); // ~500ms
  
  console.time('并发限制(3)');
  await fetchUsersWithLimit(userIds, 3);
  console.timeEnd('并发限制(3)'); // ~2000ms
}

使用 p-limit 控制并发

bash
npm install p-limit
javascript
const pLimit = require('p-limit');

/**
 * p-limit 使用示例
 */

// 创建并发限制器(最多 5 个并发)
const limit = pLimit(5);

async function fetchUsers(userIds) {
  // 将每个任务包装在 limit 中
  const promises = userIds.map(id => 
    limit(() => fetchUser(id))
  );
  
  return Promise.all(promises);
}

// 实际应用:批量处理文件
async function processFiles(files) {
  const limit = pLimit(10); // 最多同时处理 10 个文件
  
  const tasks = files.map(file => 
    limit(async () => {
      const content = await fs.promises.readFile(file, 'utf8');
      return processContent(content);
    })
  );
  
  return Promise.all(tasks);
}

Promise 池实现

javascript
/**
 * 自定义 Promise 池
 * 更精细的并发控制
 */
class PromisePool {
  /**
   * @param {number} concurrency - 并发数量
   */
  constructor(concurrency) {
    this.concurrency = concurrency;
    this.running = 0;
    this.queue = [];
  }
  
  /**
   * 添加任务到池中
   * @param {Function} task - 返回 Promise 的函数
   * @returns {Promise} 任务执行结果
   */
  async add(task) {
    return new Promise((resolve, reject) => {
      const run = async () => {
        this.running++;
        try {
          const result = await task();
          resolve(result);
        } catch (error) {
          reject(error);
        } finally {
          this.running--;
          this.next();
        }
      };
      
      if (this.running < this.concurrency) {
        run();
      } else {
        this.queue.push(run);
      }
    });
  }
  
  /**
   * 执行下一个任务
   */
  next() {
    if (this.queue.length > 0 && this.running < this.concurrency) {
      const task = this.queue.shift();
      task();
    }
  }
  
  /**
   * 批量执行任务
   * @param {Array<Function>} tasks - 任务数组
   * @returns {Promise<Array>} 所有任务结果
   */
  async all(tasks) {
    return Promise.all(tasks.map(task => this.add(task)));
  }
}

// 使用示例
async function fetchAllUsers(userIds) {
  const pool = new PromisePool(5); // 5 个并发
  
  const tasks = userIds.map(id => () => fetchUser(id));
  return pool.all(tasks);
}

异步队列处理

javascript
/**
 * 异步任务队列
 * 支持优先级和重试
 */
class AsyncTaskQueue {
  constructor(options = {}) {
    this.concurrency = options.concurrency || 5;
    this.maxRetries = options.maxRetries || 3;
    this.retryDelay = options.retryDelay || 1000;
    this.running = 0;
    this.queue = [];
    this.stats = {
      completed: 0,
      failed: 0,
      retried: 0
    };
  }
  
  /**
   * 添加任务
   * @param {Function} task - 任务函数
   * @param {Object} options - 任务选项
   */
  add(task, options = {}) {
    const taskItem = {
      task,
      priority: options.priority || 0,
      retries: 0,
      resolve: null,
      reject: null
    };
    
    return new Promise((resolve, reject) => {
      taskItem.resolve = resolve;
      taskItem.reject = reject;
      
      this.queue.push(taskItem);
      this.queue.sort((a, b) => b.priority - a.priority); // 按优先级排序
      
      this.process();
    });
  }
  
  /**
   * 处理队列
   */
  async process() {
    while (this.running < this.concurrency && this.queue.length > 0) {
      const taskItem = this.queue.shift();
      this.executeTask(taskItem);
    }
  }
  
  /**
   * 执行任务
   */
  async executeTask(taskItem) {
    this.running++;
    
    try {
      const result = await taskItem.task();
      this.stats.completed++;
      taskItem.resolve(result);
    } catch (error) {
      if (taskItem.retries < this.maxRetries) {
        taskItem.retries++;
        this.stats.retried++;
        
        // 延迟重试
        setTimeout(() => {
          this.queue.unshift(taskItem); // 优先重试
          this.process();
        }, this.retryDelay * taskItem.retries);
      } else {
        this.stats.failed++;
        taskItem.reject(error);
      }
    } finally {
      this.running--;
      this.process();
    }
  }
  
  /**
   * 获取统计信息
   */
  getStats() {
    return { ...this.stats, running: this.running, queued: this.queue.length };
  }
}

// 使用示例
const taskQueue = new AsyncTaskQueue({
  concurrency: 3,
  maxRetries: 3,
  retryDelay: 1000
});

// 添加任务(支持优先级)
taskQueue.add(() => fetchUser(1), { priority: 10 }); // 高优先级
taskQueue.add(() => fetchUser(2), { priority: 1 });  // 低优先级

异步错误处理

javascript
/**
 * 异步错误处理最佳实践
 */

// ❌ 错误示例:未处理的 Promise
async function badExample() {
  fetchData().then(data => processData(data));
  // 如果 fetchData 或 processData 失败,错误会被忽略
}

// ✅ 正确示例:使用 try-catch
async function goodExample() {
  try {
    const data = await fetchData();
    return processData(data);
  } catch (error) {
    console.error('处理失败:', error);
    throw error; // 或返回默认值
  }
}

// ✅ Promise 结果包装器
async function safeAsync(promise) {
  try {
    const data = await promise;
    return { success: true, data, error: null };
  } catch (error) {
    return { success: false, data: null, error };
  }
}

// 使用示例
const result = await safeAsync(fetchUser(1));
if (result.success) {
  console.log('用户数据:', result.data);
} else {
  console.error('获取失败:', result.error);
}

// ✅ 批量处理中的错误隔离
async function fetchAllUsersSafely(userIds) {
  const results = await Promise.allSettled(
    userIds.map(id => fetchUser(id))
  );
  
  return results.map((result, index) => ({
    id: userIds[index],
    success: result.status === 'fulfilled',
    data: result.status === 'fulfilled' ? result.value : null,
    error: result.status === 'rejected' ? result.reason : null
  }));
}

缓存优化

LRU 缓存实现

javascript
/**
 * LRU (Least Recently Used) 缓存
 * 自动淘汰最久未使用的数据
 */
class LRUCache {
  constructor(maxSize = 100) {
    this.maxSize = maxSize;
    this.cache = new Map();
    this.stats = {
      hits: 0,
      misses: 0
    };
  }
  
  /**
   * 获取缓存
   */
  get(key) {
    if (!this.cache.has(key)) {
      this.stats.misses++;
      return null;
    }
    
    // 移到末尾(最近使用)
    const value = this.cache.get(key);
    this.cache.delete(key);
    this.cache.set(key, value);
    
    this.stats.hits++;
    return value;
  }
  
  /**
   * 设置缓存
   */
  set(key, value) {
    // 已存在则删除
    if (this.cache.has(key)) {
      this.cache.delete(key);
    }
    // 超过大小则删除最老的
    else if (this.cache.size >= this.maxSize) {
      const firstKey = this.cache.keys().next().value;
      this.cache.delete(firstKey);
    }
    
    this.cache.set(key, value);
  }
  
  /**
   * 检查是否存在
   */
  has(key) {
    return this.cache.has(key);
  }
  
  /**
   * 删除缓存
   */
  delete(key) {
    return this.cache.delete(key);
  }
  
  /**
   * 清空缓存
   */
  clear() {
    this.cache.clear();
    this.stats = { hits: 0, misses: 0 };
  }
  
  /**
   * 获取命中率
   */
  getHitRate() {
    const total = this.stats.hits + this.stats.misses;
    return total === 0 ? 0 : (this.stats.hits / total * 100).toFixed(2);
  }
  
  /**
   * 获取缓存大小
   */
  get size() {
    return this.cache.size;
  }
}

// 使用示例:API 响应缓存
class APICache {
  constructor(ttl = 60000) {
    this.cache = new LRUCache(1000);
    this.ttl = ttl;
  }
  
  async get(key, fetchFn) {
    const cached = this.cache.get(key);
    
    if (cached && Date.now() - cached.timestamp < this.ttl) {
      return cached.data;
    }
    
    const data = await fetchFn();
    this.cache.set(key, {
      data,
      timestamp: Date.now()
    });
    
    return data;
  }
}

const apiCache = new APICache(60000); // 1 分钟过期

async function getUser(userId) {
  return apiCache.get(`user:${userId}`, () => 
    db.query('SELECT * FROM users WHERE id = ?', [userId])
  );
}

Redis 分布式缓存

bash
npm install ioredis
javascript
const Redis = require('ioredis');

/**
 * Redis 缓存管理器
 */
class RedisCache {
  constructor(options = {}) {
    this.redis = new Redis(options.redis || {
      host: 'localhost',
      port: 6379
    });
    this.defaultTTL = options.defaultTTL || 3600;
    this.prefix = options.prefix || 'cache:';
  }
  
  /**
   * 获取缓存
   */
  async get(key) {
    const fullKey = this.prefix + key;
    const cached = await this.redis.get(fullKey);
    
    if (cached) {
      return JSON.parse(cached);
    }
    return null;
  }
  
  /**
   * 设置缓存
   */
  async set(key, value, ttl = this.defaultTTL) {
    const fullKey = this.prefix + key;
    await this.redis.setex(fullKey, ttl, JSON.stringify(value));
  }
  
  /**
   * 删除缓存
   */
  async delete(key) {
    const fullKey = this.prefix + key;
    await this.redis.del(fullKey);
  }
  
  /**
   * 批量删除(支持通配符)
   */
  async deletePattern(pattern) {
    const fullPattern = this.prefix + pattern;
    const keys = await this.redis.keys(fullPattern);
    
    if (keys.length > 0) {
      await this.redis.del(keys);
    }
  }
  
  /**
   * 获取或设置(缓存穿透保护)
   */
  async getOrSet(key, fetchFn, ttl = this.defaultTTL) {
    const cached = await this.get(key);
    if (cached !== null) {
      return cached;
    }
    
    const data = await fetchFn();
    
    // 防止缓存空值
    if (data !== null && data !== undefined) {
      await this.set(key, data, ttl);
    }
    
    return data;
  }
}

// 使用示例
const redisCache = new RedisCache({
  defaultTTL: 3600,
  prefix: 'app:'
});

async function getUserWithCache(userId) {
  return redisCache.getOrSet(
    `user:${userId}`,
    () => db.query('SELECT * FROM users WHERE id = ?', [userId]),
    1800 // 30 分钟
  );
}

多级缓存架构

javascript
/**
 * 多级缓存系统
 * L1: 本地内存缓存(快速但容量有限)
 * L2: Redis 缓存(分布式共享)
 */
class MultiLevelCache {
  constructor(options = {}) {
    // L1 本地缓存
    this.l1Cache = new LRUCache(options.l1Size || 100);
    
    // L2 Redis 缓存
    this.l2Cache = new RedisCache(options.redis);
    
    this.defaultTTL = options.defaultTTL || 3600;
    this.stats = {
      l1Hits: 0,
      l2Hits: 0,
      misses: 0
    };
  }
  
  /**
   * 获取数据
   */
  async get(key) {
    // 先查 L1
    const l1Data = this.l1Cache.get(key);
    if (l1Data !== null) {
      this.stats.l1Hits++;
      return l1Data;
    }
    
    // 再查 L2
    const l2Data = await this.l2Cache.get(key);
    if (l2Data !== null) {
      this.stats.l2Hits++;
      // 回填 L1
      this.l1Cache.set(key, l2Data);
      return l2Data;
    }
    
    this.stats.misses++;
    return null;
  }
  
  /**
   * 设置数据
   */
  async set(key, value, ttl = this.defaultTTL) {
    // 同时写入两级缓存
    this.l1Cache.set(key, value);
    await this.l2Cache.set(key, value, ttl);
  }
  
  /**
   * 删除数据
   */
  async delete(key) {
    this.l1Cache.delete(key);
    await this.l2Cache.delete(key);
  }
  
  /**
   * 获取或设置
   */
  async getOrSet(key, fetchFn, ttl = this.defaultTTL) {
    const data = await this.get(key);
    if (data !== null) {
      return data;
    }
    
    const freshData = await fetchFn();
    if (freshData !== null && freshData !== undefined) {
      await this.set(key, freshData, ttl);
    }
    
    return freshData;
  }
  
  /**
   * 获取缓存统计
   */
  getStats() {
    const total = this.stats.l1Hits + this.stats.l2Hits + this.stats.misses;
    return {
      ...this.stats,
      l1HitRate: total === 0 ? 0 : (this.stats.l1Hits / total * 100).toFixed(2) + '%',
      l2HitRate: total === 0 ? 0 : (this.stats.l2Hits / total * 100).toFixed(2) + '%',
      totalHitRate: total === 0 ? 0 : ((this.stats.l1Hits + this.stats.l2Hits) / total * 100).toFixed(2) + '%'
    };
  }
}

缓存常见问题解决

javascript
/**
 * 缓存穿透、雪崩、击穿解决方案
 */

// 1. 缓存穿透防护
class CachePenetrationGuard {
  constructor(cache) {
    this.cache = cache;
    this.nullCache = new Set(); // 存储空值 key
    this.nullCacheTTL = 60; // 空值缓存时间
  }
  
  async get(key, fetchFn) {
    // 检查空值缓存
    if (this.nullCache.has(key)) {
      return null;
    }
    
    const data = await this.cache.get(key);
    if (data !== null) {
      return data;
    }
    
    const freshData = await fetchFn();
    
    if (freshData === null || freshData === undefined) {
      // 缓存空值,防止穿透
      this.nullCache.add(key);
      setTimeout(() => this.nullCache.delete(key), this.nullCacheTTL * 1000);
      return null;
    }
    
    await this.cache.set(key, freshData);
    return freshData;
  }
}

// 2. 缓存雪崩防护
class CacheAvalancheGuard {
  constructor(cache, baseTTL = 3600) {
    this.cache = cache;
    this.baseTTL = baseTTL;
  }
  
  /**
   * 添加随机 TTL,避免同时过期
   */
  async set(key, value) {
    const randomTTL = this.baseTTL + Math.floor(Math.random() * 300);
    await this.cache.set(key, value, randomTTL);
  }
  
  /**
   * 缓存预热
   */
  async warmup(keys, fetchFn) {
    await Promise.all(keys.map(key => 
      this.cache.getOrSet(key, () => fetchFn(key))
    ));
  }
}

// 3. 缓存击穿防护(互斥锁)
class CacheBreakdownGuard {
  constructor(cache) {
    this.cache = cache;
    this.locks = new Map();
  }
  
  async getOrSet(key, fetchFn, ttl) {
    const data = await this.cache.get(key);
    if (data !== null) {
      return data;
    }
    
    // 获取锁
    const lock = this.getLock(key);
    
    try {
      // 等待锁
      await lock.acquire();
      
      // 双重检查
      const dataAgain = await this.cache.get(key);
      if (dataAgain !== null) {
        return dataAgain;
      }
      
      const freshData = await fetchFn();
      await this.cache.set(key, freshData, ttl);
      
      return freshData;
    } finally {
      lock.release();
    }
  }
  
  getLock(key) {
    if (!this.locks.has(key)) {
      this.locks.set(key, new AsyncLock());
    }
    return this.locks.get(key);
  }
}

/**
 * 简单的异步锁
 */
class AsyncLock {
  constructor() {
    this.locked = false;
    this.queue = [];
  }
  
  async acquire() {
    while (this.locked) {
      await new Promise(resolve => this.queue.push(resolve));
    }
    this.locked = true;
  }
  
  release() {
    this.locked = false;
    const next = this.queue.shift();
    if (next) next();
  }
}

数据库优化

连接池配置

javascript
const mysql = require('mysql2/promise');

/**
 * 数据库连接池配置
 */
const pool = mysql.createPool({
  host: 'localhost',
  user: 'root',
  password: 'password',
  database: 'test',
  
  // 连接池配置
  connectionLimit: 20,      // 最大连接数
  waitForConnections: true, // 连接耗尽时等待
  queueLimit: 0,           // 等待队列长度(0 = 无限)
  
  // 性能优化
  acquireTimeout: 10000,   // 获取连接超时
  timeout: 60000,          // 查询超时
  timezone: '+08:00',      // 时区设置
  
  // 连接维护
  idleTimeout: 60000,      // 空闲连接超时
  enableKeepAlive: true,   // 保持连接活跃
  keepAliveInitialDelay: 0
});

/**
 * 连接池监控
 */
async function getPoolStats() {
  return {
    // mysql2 暂不支持直接获取连接池状态
    // 可以通过自定义包装器实现
  };
}

/**
 * 优雅关闭
 */
async function closePool() {
  await pool.end();
  console.log('数据库连接池已关闭');
}

// 使用连接池
async function query(sql, params) {
  const [rows] = await pool.execute(sql, params);
  return rows;
}

批量操作优化

javascript
/**
 * 批量插入
 */
async function batchInsert(users) {
  // ❌ 单条插入 - 低效
  // for (const user of users) {
  //   await query('INSERT INTO users SET ?', user);
  // }
  
  // ✅ 批量插入 - 高效
  const values = users.map(u => [u.name, u.email, u.age]);
  const sql = 'INSERT INTO users (name, email, age) VALUES ?';
  
  return query(sql, [values]);
}

/**
 * 批量更新(使用 CASE WHEN)
 */
async function batchUpdate(updates) {
  const cases = {};
  const ids = [];
  
  updates.forEach(({ id, field, value }) => {
    if (!cases[field]) cases[field] = [];
    cases[field].push(`WHEN ${id} THEN ?`);
    ids.push(id);
  });
  
  const setClauses = Object.entries(cases)
    .map(([field, whens]) => 
      `${field} = CASE id ${whens.join(' ')} END`
    )
    .join(', ');
  
  const sql = `
    UPDATE users 
    SET ${setClauses} 
    WHERE id IN (?)
  `;
  
  return query(sql, [ids]);
}

/**
 * 批量查询(避免 N+1)
 */
async function getUsersWithPosts() {
  // ❌ N+1 查询
  // const users = await query('SELECT * FROM users');
  // for (const user of users) {
  //   user.posts = await query('SELECT * FROM posts WHERE user_id = ?', [user.id]);
  // }
  
  // ✅ 优化方案 1:批量查询
  const users = await query('SELECT id, name FROM users');
  const userIds = users.map(u => u.id);
  
  const posts = await query(
    'SELECT user_id, title FROM posts WHERE user_id IN (?)',
    [userIds]
  );
  
  // 组装数据
  const postsByUser = {};
  posts.forEach(post => {
    if (!postsByUser[post.user_id]) postsByUser[post.user_id] = [];
    postsByUser[post.user_id].push(post);
  });
  
  return users.map(user => ({
    ...user,
    posts: postsByUser[user.id] || []
  }));
  
  // ✅ 优化方案 2:JOIN 查询
  const results = await query(`
    SELECT u.id, u.name, p.title
    FROM users u
    LEFT JOIN posts p ON u.id = p.user_id
  `);
  
  // 结果需要去重合并
}

查询优化技巧

javascript
/**
 * 查询优化示例
 */

// 1. 只查询需要的字段
// ❌ 查询所有字段
// const users = await query('SELECT * FROM users WHERE age > 18');

// ✅ 只查询需要的字段
const users = await query('SELECT id, name FROM users WHERE age > 18');

// 2. 分页优化
// ❌ 大偏移量分页
// const users = await query('SELECT * FROM users LIMIT 10000, 20');

// ✅ 基于游标的分页
async function paginateWithCursor(lastId, limit = 20) {
  return query(
    'SELECT * FROM users WHERE id > ? ORDER BY id LIMIT ?',
    [lastId, limit]
  );
}

// ✅ 延迟关联(先查 ID,再查详情)
async function paginateWithDefer(offset, limit) {
  const [ids] = await query(
    'SELECT id FROM users LIMIT ?, ?',
    [offset, limit]
  );
  
  return query(
    'SELECT * FROM users WHERE id IN (?)',
    [ids.map(i => i.id)]
  );
}

// 3. 索引使用优化
// ✅ 使用覆盖索引
const users = await query(
  'SELECT id, name FROM users WHERE age > 18 AND status = "active"'
);

// ✅ 避免索引失效
// ❌ 在索引列上使用函数
// WHERE YEAR(created_at) = 2023

// ✅ 使用范围查询
// WHERE created_at >= '2023-01-01' AND created_at < '2024-01-01'

// 4. 事务优化
async function transferMoney(fromId, toId, amount) {
  const connection = await pool.getConnection();
  
  try {
    await connection.beginTransaction();
    
    // 悲观锁
    const [from] = await connection.execute(
      'SELECT balance FROM accounts WHERE id = ? FOR UPDATE',
      [fromId]
    );
    
    if (from[0].balance < amount) {
      throw new Error('余额不足');
    }
    
    await connection.execute(
      'UPDATE accounts SET balance = balance - ? WHERE id = ?',
      [amount, fromId]
    );
    
    await connection.execute(
      'UPDATE accounts SET balance = balance + ? WHERE id = ?',
      [amount, toId]
    );
    
    await connection.commit();
  } catch (error) {
    await connection.rollback();
    throw error;
  } finally {
    connection.release();
  }
}

JSON 处理优化

快速 JSON 序列化

bash
npm install fast-json-stringify
javascript
const fastJson = require('fast-json-stringify');

/**
 * 使用 JSON Schema 加速序列化
 */

// 定义 Schema
const userSchema = fastJson({
  type: 'object',
  properties: {
    id: { type: 'number' },
    name: { type: 'string' },
    email: { type: 'string' },
    age: { type: 'number' },
    tags: {
      type: 'array',
      items: { type: 'string' }
    }
  }
});

// 性能对比
const user = {
  id: 1,
  name: 'Alice',
  email: 'alice@example.com',
  age: 25,
  tags: ['developer', 'nodejs']
};

// 原生 JSON.stringify
console.time('JSON.stringify');
for (let i = 0; i < 100000; i++) {
  JSON.stringify(user);
}
console.timeEnd('JSON.stringify');

// fast-json-stringify(快 2-3 倍)
console.time('fast-json-stringify');
for (let i = 0; i < 100000; i++) {
  userSchema(user);
}
console.timeEnd('fast-json-stringify');

流式 JSON 解析

bash
npm install stream-json
javascript
const { chain } = require('stream-chain');
const { parser } = require('stream-json');
const { streamArray } = require('stream-json/streamers/StreamArray');
const fs = require('fs');

/**
 * 处理大型 JSON 文件
 */

// ❌ 一次性加载 - 内存占用大
// const data = JSON.parse(fs.readFileSync('large.json', 'utf8'));

// ✅ 流式解析 - 内存占用小
async function processLargeJSON(filePath) {
  const pipeline = chain([
    fs.createReadStream(filePath),
    parser(),
    streamArray()
  ]);
  
  let count = 0;
  
  for await (const { key, value } of pipeline) {
    // 逐条处理
    await processItem(value);
    count++;
    
    // 批量提交
    if (count % 1000 === 0) {
      console.log(`已处理 ${count} 条记录`);
    }
  }
  
  console.log(`处理完成,共 ${count} 条记录`);
}

/**
 * 提取特定字段
 */
const { pick } = require('stream-json/filters/Pick');

async function extractFields(filePath) {
  const pipeline = chain([
    fs.createReadStream(filePath),
    parser(),
    pick({ filter: 'users' }),
    streamArray()
  ]);
  
  const users = [];
  
  for await (const { value } of pipeline) {
    users.push({
      id: value.id,
      name: value.name
    });
  }
  
  return users;
}

正则表达式优化

javascript
/**
 * 正则表达式优化技巧
 */

// 1. 预编译正则表达式
// ❌ 每次调用都编译
function validateEmailBad(email) {
  return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
}

// ✅ 预编译
const EMAIL_REGEX = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;

function validateEmail(email) {
  return EMAIL_REGEX.test(email);
}

// 2. 正则缓存
const regexCache = new Map();

function getRegex(pattern, flags = '') {
  const key = pattern + flags;
  if (!regexCache.has(key)) {
    regexCache.set(key, new RegExp(pattern, flags));
  }
  return regexCache.get(key);
}

// 3. 避免回溯灾难
// ❌ 可能导致灾难性回溯
const badRegex = /(a+)+$/;

// ✅ 使用原子组或占有量词
const goodRegex = /(a++)+$/;  // 占有量词

// 4. 常用正则表达式库
const patterns = {
  email: /^[^\s@]+@[^\s@]+\.[^\s@]+$/,
  phone: /^1[3-9]\d{9}$/,
  url: /^https?:\/\/[\w\-]+(\.[\w\-]+)+[/#?]?.*$/,
  ip: /^(\d{1,3}\.){3}\d{1,}$/,
  uuid: /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i
};

// 5. 性能测试
function testRegexPerformance(regex, str, iterations = 100000) {
  console.time('Regex test');
  for (let i = 0; i < iterations; i++) {
    regex.test(str);
  }
  console.timeEnd('Regex test');
}

函数优化

记忆化(Memoization)

javascript
/**
 * 函数记忆化
 * 缓存函数计算结果
 */

// 简单实现
function memoize(fn) {
  const cache = new Map();
  
  return function(...args) {
    const key = JSON.stringify(args);
    
    if (cache.has(key)) {
      return cache.get(key);
    }
    
    const result = fn.apply(this, args);
    cache.set(key, result);
    
    return result;
  };
}

// 支持异步函数
function memoizeAsync(fn) {
  const cache = new Map();
  const pending = new Map();
  
  return async function(...args) {
    const key = JSON.stringify(args);
    
    if (cache.has(key)) {
      return cache.get(key);
    }
    
    // 避免并发重复计算
    if (pending.has(key)) {
      return pending.get(key);
    }
    
    const promise = fn.apply(this, args);
    pending.set(key, promise);
    
    try {
      const result = await promise;
      cache.set(key, result);
      return result;
    } finally {
      pending.delete(key);
    }
  };
}

// 使用示例:斐波那契数列
const fibonacci = memoize(function(n) {
  if (n <= 1) return n;
  return fibonacci(n - 1) + fibonacci(n - 2);
});

console.time('Fibonacci 40');
console.log(fibonacci(40)); // 瞬间完成
console.timeEnd('Fibonacci 40');

防抖与节流

javascript
/**
 * 防抖(Debounce)
 * 延迟执行,重复调用重置计时器
 */
function debounce(fn, delay, immediate = false) {
  let timer = null;
  
  return function(...args) {
    const callNow = immediate && !timer;
    
    clearTimeout(timer);
    
    timer = setTimeout(() => {
      timer = null;
      if (!immediate) {
        fn.apply(this, args);
      }
    }, delay);
    
    if (callNow) {
      fn.apply(this, args);
    }
  };
}

/**
 * 节流(Throttle)
 * 固定间隔执行
 */
function throttle(fn, interval, options = {}) {
  let lastTime = 0;
  let timer = null;
  const { leading = true, trailing = true } = options;
  
  return function(...args) {
    const now = Date.now();
    
    if (!lastTime && !leading) {
      lastTime = now;
    }
    
    const remaining = interval - (now - lastTime);
    
    if (remaining <= 0) {
      if (timer) {
        clearTimeout(timer);
        timer = null;
      }
      lastTime = now;
      fn.apply(this, args);
    } else if (!timer && trailing) {
      timer = setTimeout(() => {
        lastTime = leading ? Date.now() : 0;
        timer = null;
        fn.apply(this, args);
      }, remaining);
    }
  };
}

// 使用示例
const searchDebounced = debounce(search, 300);
const scrollThrottled = throttle(handleScroll, 100);

// 搜索输入
input.addEventListener('input', searchDebounced);

// 滚动事件
window.addEventListener('scroll', scrollThrottled);

惰性函数

javascript
/**
 * 惰性函数
 * 首次执行时确定最终实现
 */
function createXHR() {
  if (typeof XMLHttpRequest !== 'undefined') {
    return function() {
      return new XMLHttpRequest();
    };
  } else if (typeof ActiveXObject !== 'undefined') {
    return function() {
      return new ActiveXObject('Microsoft.XMLHTTP');
    };
  }
  
  return function() {
    throw new Error('XHR not supported');
  };
}

const getXHR = createXHR();

// 首次调用后,后续调用直接使用确定的方法
const xhr = getXHR();

// 另一个示例:事件监听
function addEvent(element, type, handler) {
  if (element.addEventListener) {
    addEvent = function(element, type, handler) {
      element.addEventListener(type, handler, false);
    };
  } else if (element.attachEvent) {
    addEvent = function(element, type, handler) {
      element.attachEvent('on' + type, handler);
    };
  }
  
  addEvent(element, type, handler);
}

数组与对象优化

数组操作优化

javascript
/**
 * 数组操作性能优化
 */

// 1. 避免在循环中修改数组
// ❌ 正序删除会跳过元素
// for (let i = 0; i < arr.length; i++) {
//   if (condition) arr.splice(i, 1);
// }

// ✅ 倒序删除
for (let i = arr.length - 1; i >= 0; i--) {
  if (condition) arr.splice(i, 1);
}

// ✅ 使用 filter 创建新数组
const filtered = arr.filter(item => !condition);

// 2. 数组查找优化
// ❌ O(n) 复杂度
const found = arr.includes(target);
const index = arr.findIndex(item => item.id === targetId);

// ✅ O(1) 复杂度 - 使用 Set/Map
const set = new Set(arr);
const found = set.has(target);

const map = new Map(arr.map(item => [item.id, item]));
const item = map.get(targetId);

// 3. 数组去重
// ❌ 性能较差
const unique = arr.filter((item, index) => arr.indexOf(item) === index);

// ✅ 使用 Set
const unique = [...new Set(arr)];

// 4. 大数组排序
// ❌ 默认排序(字符串比较)
arr.sort();

// ✅ 数值排序
arr.sort((a, b) => a - b);

// 5. 避免使用 delete 删除数组元素
// ❌ 使用 delete(留下空洞)
// delete arr[2]; // [1, 2, empty, 4, 5]

// ✅ 使用 splice
arr.splice(2, 1); // [1, 2, 4, 5]

TypedArray 优化

javascript
/**
 * TypedArray - 类型化数组
 * 用于处理大量数值数据,性能更好
 */

// 普通数组
const normalArray = new Array(1000000);
for (let i = 0; i < 1000000; i++) {
  normalArray[i] = i;
}

// TypedArray(内存占用更小,操作更快)
const typedArray = new Float64Array(1000000);
for (let i = 0; i < 1000000; i++) {
  typedArray[i] = i;
}

// TypedArray 类型
const types = {
  Int8Array: '8位有符号整数',
  Uint8Array: '8位无符号整数',
  Int16Array: '16位有符号整数',
  Uint16Array: '16位无符号整数',
  Int32Array: '32位有符号整数',
  Uint32Array: '32位无符号整数',
  Float32Array: '32位浮点数',
  Float64Array: '64位浮点数'
};

// 使用示例:图像处理
function processImageData(width, height) {
  // RGBA 每像素 4 字节
  const pixels = new Uint8ClampedArray(width * height * 4);
  
  for (let i = 0; i < pixels.length; i += 4) {
    pixels[i] = 255;     // R
    pixels[i + 1] = 0;   // G
    pixels[i + 2] = 0;   // B
    pixels[i + 3] = 255; // A
  }
  
  return pixels;
}

对象优化

javascript
/**
 * 对象操作优化
 */

// 1. 避免频繁添加/删除属性
// ❌ 动态添加属性(隐藏类变化,性能下降)
function Point(x, y) {
  this.x = x;
  if (y) this.y = y; // 条件添加
}

// ✅ 保持对象结构一致
function Point(x, y) {
  this.x = x;
  this.y = y || 0; // 总是初始化
}

// 2. 使用 Object.create(null) 创建纯净对象
const cleanObj = Object.create(null);
cleanObj.key = 'value';
// 没有 prototype,更省内存

// 3. 属性访问优化
// ❌ 动态属性名
obj[dynamicKey] = value;

// ✅ 静态属性名
obj.staticKey = value;

// 4. 对象克隆
// 浅拷贝
const shallow = { ...obj };
const shallow2 = Object.assign({}, obj);

// 深拷贝
const deep = JSON.parse(JSON.stringify(obj));
// 或使用 structuredClone(Node.js 17+)
const deep2 = structuredClone(obj);

字符串优化

字符串拼接

javascript
/**
 * 字符串拼接优化
 */

// ❌ 大量字符串使用 + 拼接
let str = '';
for (let i = 0; i < 10000; i++) {
  str += 'a';
}

// ✅ 使用数组 join
const parts = [];
for (let i = 0; i < 10000; i++) {
  parts.push('a');
}
const str = parts.join('');

// ✅ 使用 repeat(重复相同字符)
const str = 'a'.repeat(10000);

// ✅ 使用模板字符串(可读性好,性能相近)
const message = `Hello ${name}, you have ${count} messages`;

字符串查找

javascript
/**
 * 字符串查找优化
 */

const text = 'Hello World, this is a test string';

// 1. 是否包含子串
// ✅ includes(ES6,语义清晰)
const hasWorld = text.includes('World');

// 2. 查找位置
const index = text.indexOf('World');

// 3. 开头/结尾匹配
// ✅ startsWith / endsWith(ES6)
const startsWithHello = text.startsWith('Hello');
const endsWithTest = text.endsWith('test');

// 4. 批量替换
const replaced = text.replace(/Hello|World/g, match => 
  match.toUpperCase()
);

性能测试工具

Benchmark.js

bash
npm install benchmark
javascript
const Benchmark = require('benchmark');

/**
 * 使用 Benchmark.js 进行基准测试
 */
const suite = new Benchmark.Suite();

// 测试对象
const arr = new Array(10000).fill(0).map((_, i) => i);

suite
  .add('Array#forEach', function() {
    let sum = 0;
    arr.forEach(item => sum += item);
  })
  .add('for loop', function() {
    let sum = 0;
    for (let i = 0; i < arr.length; i++) {
      sum += arr[i];
    }
  })
  .add('for...of', function() {
    let sum = 0;
    for (const item of arr) {
      sum += item;
    }
  })
  .add('reduce', function() {
    arr.reduce((sum, item) => sum + item, 0);
  })
  .on('cycle', function(event) {
    console.log(String(event.target));
  })
  .on('complete', function() {
    console.log('最快的是: ' + this.filter('fastest').map('name'));
  })
  .run({ async: true });

压力测试

bash
# 使用 autocannon 进行压力测试
npm install -g autocannon

# 基本用法
autocannon -c 100 -d 30 http://localhost:3000

# 参数说明
# -c, --connections: 并发连接数
# -d, --duration: 测试持续时间(秒)
# -p, --pipelining: 管道请求数
# -m, --method: HTTP 方法
# -H, --header: 请求头
# -b, --body: 请求体
javascript
// 编程方式使用 autocannon
const autocannon = require('autocannon');

async function runBenchmark() {
  const result = await autocannon({
    url: 'http://localhost:3000',
    connections: 100,
    duration: 30,
    requests: [
      {
        method: 'GET',
        path: '/api/users'
      },
      {
        method: 'POST',
        path: '/api/users',
        headers: {
          'Content-Type': 'application/json'
        },
        body: JSON.stringify({ name: 'Test' })
      }
    ]
  });
  
  console.log('测试结果:', {
    总请求数: result.requests.total,
    平均延迟: result.latency.average + 'ms',
    P99延迟: result.latency.p99 + 'ms',
    吞吐量: result.throughput.average + ' bytes/sec'
  });
}

clinic.js 诊断

bash
# 安装 clinic.js
npm install -g clinic

# Doctor - 诊断 I/O 问题
clinic doctor -- node app.js

# Bubbleprof - 分析异步操作
clinic bubbleprof -- node app.js

# Flame - 火焰图分析 CPU
clinic flame -- node app.js

# Heapprofiler - 堆内存分析
clinic heapprofiler -- node app.js

常见问题解答

Q1: 何时使用并行处理,何时使用顺序处理?

A: 根据任务特性选择:

任务类型推荐方式原因
独立的 I/O 操作并行I/O 等待时间可重叠
有依赖关系的操作顺序保证执行顺序
计算密集型并发限制避免耗尽 CPU
批量数据库查询并行+限制提高性能,避免数据库压力

Q2: 缓存应该设置多大的 TTL?

A: 根据数据特性设置:

javascript
// 热点数据:短 TTL,高频率更新
const hotDataTTL = 60; // 1 分钟

// 稳定数据:长 TTL
const stableDataTTL = 86400; // 1 天

// 用户相关:中等 TTL
const userDataTTL = 1800; // 30 分钟

// 配置数据:很长 TTL,手动失效
const configTTL = 604800; // 1 周

Q3: 如何选择合适的数据结构?

A: 根据操作特点选择:

需求推荐数据结构时间复杂度
快速查找Map / SetO(1)
有序数据数组 + 排序O(log n)
频繁插入删除链表(数组 splice)O(n)
去重SetO(1)
键值对Object / MapO(1)
大量数值TypedArray内存优化

Q4: 如何避免内存泄漏?

A: 注意以下场景:

  1. 全局变量:避免在全局存储数据
  2. 闭包:注意大对象的引用
  3. 事件监听器:及时移除不需要的监听器
  4. 定时器:及时清除定时器
  5. 缓存:设置合理的容量限制和过期策略

Q5: 如何评估优化效果?

A: 使用以下方法:

  1. 基准测试:使用 Benchmark.js 对比优化前后
  2. 压力测试:使用 autocannon 测试吞吐量和延迟
  3. 内存分析:使用 clinic.js heapprofiler 分析内存
  4. CPU 分析:使用 clinic.js flame 分析 CPU 热点
  5. 监控数据:对比生产环境的性能指标

最佳实践总结

优化优先级

code
┌─────────────────────────────────────────────────────────────────┐
│                    性能优化优先级金字塔                           │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│                     ┌─────────┐                                │
│                     │ 算法优化 │ ← 影响最大                      │
│                     └────┬────┘                                │
│                    ┌─────┴─────┐                               │
│                    │ 架构优化  │                                │
│                    └─────┬─────┘                               │
│                 ┌────────┴────────┐                            │
│                 │   数据库优化    │                             │
│                 └────────┬────────┘                            │
│            ┌─────────────┴─────────────┐                       │
│            │       缓存优化            │                        │
│            └─────────────┬─────────────┘                       │
│       ┌──────────────────┴──────────────────┐                  │
│       │            异步并发优化              │                   │
│       └──────────────────┬──────────────────┘                  │
│  ┌───────────────────────┴───────────────────────┐             │
│  │              代码细节优化                       │             │
│  └───────────────────────────────────────────────┘             │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

优化检查清单

  • 异步优化

    • 使用 Promise.all 并行处理独立任务
    • 控制并发数量避免资源耗尽
    • 正确处理异步错误
  • 缓存优化

    • 使用多级缓存减少延迟
    • 设置合理的 TTL 和容量
    • 处理缓存穿透、雪崩、击穿
  • 数据库优化

    • 使用连接池管理连接
    • 批量操作减少数据库交互
    • 避免 N+1 查询问题
  • 内存优化

    • 使用 Stream 处理大数据
    • 避免内存泄漏
    • 合理使用 Buffer 和 TypedArray
  • 代码质量

    • 算法复杂度优化
    • 使用高效的数据结构
    • 避免重复计算(记忆化)
  • 性能测试

    • 基准测试对比优化效果
    • 压力测试验证系统容量
    • 持续监控生产环境性能

参考资源