发布于

MongoDB 数据库设计与优化:NoSQL 数据库最佳实践

作者

MongoDB 数据库设计与优化:NoSQL 数据库最佳实践

MongoDB 是一个强大的 NoSQL 文档数据库,为现代应用提供了灵活的数据存储解决方案。本文将深入探讨 MongoDB 的设计原则、优化策略和最佳实践。

MongoDB 基础概念

文档数据模型

// 用户文档示例
const userDocument = {
  _id: ObjectId('507f1f77bcf86cd799439011'),
  username: 'john_doe',
  email: 'john@example.com',
  profile: {
    firstName: 'John',
    lastName: 'Doe',
    age: 30,
    avatar: 'https://example.com/avatar.jpg',
  },
  preferences: {
    theme: 'dark',
    language: 'zh-CN',
    notifications: {
      email: true,
      push: false,
      sms: true,
    },
  },
  addresses: [
    {
      type: 'home',
      street: '123 Main St',
      city: 'Beijing',
      country: 'China',
      zipCode: '100000',
    },
    {
      type: 'work',
      street: '456 Business Ave',
      city: 'Shanghai',
      country: 'China',
      zipCode: '200000',
    },
  ],
  tags: ['developer', 'mongodb', 'javascript'],
  createdAt: new Date('2024-01-01T00:00:00Z'),
  updatedAt: new Date('2024-03-10T12:00:00Z'),
  isActive: true,
}

// 博客文章文档示例
const postDocument = {
  _id: ObjectId('507f1f77bcf86cd799439012'),
  title: 'MongoDB 最佳实践',
  slug: 'mongodb-best-practices',
  content: '这是一篇关于 MongoDB 的文章...',
  excerpt: '学习 MongoDB 的最佳实践',
  author: {
    _id: ObjectId('507f1f77bcf86cd799439011'),
    username: 'john_doe',
    name: 'John Doe',
  },
  categories: ['数据库', 'NoSQL', 'MongoDB'],
  tags: ['mongodb', 'database', 'nosql'],
  metadata: {
    wordCount: 1500,
    readingTime: 8,
    difficulty: 'intermediate',
  },
  seo: {
    metaTitle: 'MongoDB 最佳实践指南',
    metaDescription: '深入学习 MongoDB 数据库设计和优化',
    keywords: ['mongodb', 'nosql', 'database'],
  },
  stats: {
    views: 1250,
    likes: 89,
    shares: 23,
    comments: 15,
  },
  publishedAt: new Date('2024-03-01T10:00:00Z'),
  createdAt: new Date('2024-02-28T15:30:00Z'),
  updatedAt: new Date('2024-03-10T09:15:00Z'),
  status: 'published',
}

集合设计原则

// 1. 嵌入式文档 vs 引用
// 适合嵌入的场景:一对一、一对少量
const orderWithEmbeddedItems = {
  _id: ObjectId('...'),
  orderNumber: 'ORD-2024-001',
  customer: {
    _id: ObjectId('...'),
    name: '张三',
    email: 'zhang@example.com',
  },
  items: [
    {
      productId: ObjectId('...'),
      name: 'MacBook Pro',
      price: 12999,
      quantity: 1,
    },
    {
      productId: ObjectId('...'),
      name: 'Magic Mouse',
      price: 599,
      quantity: 1,
    },
  ],
  total: 13598,
  status: 'completed',
  createdAt: new Date(),
}

// 适合引用的场景:一对多、多对多
const userWithPostReferences = {
  _id: ObjectId('...'),
  username: 'author123',
  email: 'author@example.com',
  // 不嵌入所有文章,而是通过查询获取
  postCount: 150,
  createdAt: new Date(),
}

const postWithAuthorReference = {
  _id: ObjectId('...'),
  title: '我的第一篇文章',
  content: '...',
  authorId: ObjectId('...'), // 引用用户ID
  createdAt: new Date(),
}

// 2. 反范式化设计
const commentWithDenormalizedData = {
  _id: ObjectId('...'),
  postId: ObjectId('...'),
  postTitle: 'MongoDB 最佳实践', // 冗余数据,避免额外查询
  content: '很好的文章!',
  author: {
    _id: ObjectId('...'),
    username: 'reader123',
    avatar: 'https://example.com/avatar.jpg', // 冗余数据
  },
  createdAt: new Date(),
}

查询操作与优化

1. 基础查询操作

// 连接 MongoDB
const { MongoClient } = require('mongodb')

class MongoDBService {
  constructor(uri, dbName) {
    this.client = new MongoClient(uri)
    this.dbName = dbName
    this.db = null
  }

  async connect() {
    await this.client.connect()
    this.db = this.client.db(this.dbName)
    console.log('MongoDB 连接成功')
  }

  async disconnect() {
    await this.client.close()
    console.log('MongoDB 连接已关闭')
  }

  // 基础查询
  async findUsers(filter = {}, options = {}) {
    const collection = this.db.collection('users')

    return await collection.find(filter, options).toArray()
  }

  // 分页查询
  async findUsersWithPagination(filter = {}, page = 1, limit = 10) {
    const collection = this.db.collection('users')
    const skip = (page - 1) * limit

    const [users, total] = await Promise.all([
      collection.find(filter).skip(skip).limit(limit).sort({ createdAt: -1 }).toArray(),
      collection.countDocuments(filter),
    ])

    return {
      users,
      pagination: {
        page,
        limit,
        total,
        pages: Math.ceil(total / limit),
      },
    }
  }

  // 复杂查询
  async findPostsWithFilters(filters) {
    const collection = this.db.collection('posts')
    const query = {}

    // 文本搜索
    if (filters.search) {
      query.$text = { $search: filters.search }
    }

    // 分类筛选
    if (filters.category) {
      query.categories = filters.category
    }

    // 标签筛选
    if (filters.tags && filters.tags.length > 0) {
      query.tags = { $in: filters.tags }
    }

    // 日期范围
    if (filters.dateFrom || filters.dateTo) {
      query.publishedAt = {}
      if (filters.dateFrom) {
        query.publishedAt.$gte = new Date(filters.dateFrom)
      }
      if (filters.dateTo) {
        query.publishedAt.$lte = new Date(filters.dateTo)
      }
    }

    // 状态筛选
    if (filters.status) {
      query.status = filters.status
    }

    return await collection.find(query).sort({ publishedAt: -1 }).toArray()
  }

  // 聚合查询
  async getPostStatistics() {
    const collection = this.db.collection('posts')

    return await collection
      .aggregate([
        {
          $match: { status: 'published' },
        },
        {
          $group: {
            _id: {
              year: { $year: '$publishedAt' },
              month: { $month: '$publishedAt' },
            },
            count: { $sum: 1 },
            totalViews: { $sum: '$stats.views' },
            avgViews: { $avg: '$stats.views' },
          },
        },
        {
          $sort: { '_id.year': -1, '_id.month': -1 },
        },
        {
          $project: {
            _id: 0,
            year: '$_id.year',
            month: '$_id.month',
            count: 1,
            totalViews: 1,
            avgViews: { $round: ['$avgViews', 2] },
          },
        },
      ])
      .toArray()
  }
}

2. 高级聚合操作

// 复杂聚合管道
class AdvancedQueries {
  constructor(db) {
    this.db = db
  }

  // 用户活跃度分析
  async getUserActivityAnalysis() {
    const collection = this.db.collection('users')

    return await collection
      .aggregate([
        {
          $lookup: {
            from: 'posts',
            localField: '_id',
            foreignField: 'authorId',
            as: 'posts',
          },
        },
        {
          $lookup: {
            from: 'comments',
            localField: '_id',
            foreignField: 'authorId',
            as: 'comments',
          },
        },
        {
          $addFields: {
            postCount: { $size: '$posts' },
            commentCount: { $size: '$comments' },
            totalViews: { $sum: '$posts.stats.views' },
            avgViewsPerPost: {
              $cond: {
                if: { $gt: [{ $size: '$posts' }, 0] },
                then: { $divide: [{ $sum: '$posts.stats.views' }, { $size: '$posts' }] },
                else: 0,
              },
            },
          },
        },
        {
          $addFields: {
            activityScore: {
              $add: [
                { $multiply: ['$postCount', 10] },
                { $multiply: ['$commentCount', 2] },
                { $divide: ['$totalViews', 100] },
              ],
            },
          },
        },
        {
          $project: {
            username: 1,
            email: 1,
            postCount: 1,
            commentCount: 1,
            totalViews: 1,
            avgViewsPerPost: { $round: ['$avgViewsPerPost', 2] },
            activityScore: { $round: ['$activityScore', 2] },
            createdAt: 1,
          },
        },
        {
          $sort: { activityScore: -1 },
        },
        {
          $limit: 50,
        },
      ])
      .toArray()
  }

  // 热门标签统计
  async getPopularTags(limit = 20) {
    const collection = this.db.collection('posts')

    return await collection
      .aggregate([
        {
          $match: { status: 'published' },
        },
        {
          $unwind: '$tags',
        },
        {
          $group: {
            _id: '$tags',
            count: { $sum: 1 },
            totalViews: { $sum: '$stats.views' },
            avgViews: { $avg: '$stats.views' },
            recentPosts: {
              $push: {
                title: '$title',
                slug: '$slug',
                publishedAt: '$publishedAt',
              },
            },
          },
        },
        {
          $addFields: {
            recentPosts: {
              $slice: [
                {
                  $sortArray: {
                    input: '$recentPosts',
                    sortBy: { publishedAt: -1 },
                  },
                },
                3,
              ],
            },
          },
        },
        {
          $sort: { count: -1 },
        },
        {
          $limit: limit,
        },
        {
          $project: {
            _id: 0,
            tag: '$_id',
            count: 1,
            totalViews: 1,
            avgViews: { $round: ['$avgViews', 2] },
            recentPosts: 1,
          },
        },
      ])
      .toArray()
  }

  // 内容推荐算法
  async getRecommendedPosts(userId, limit = 10) {
    const collection = this.db.collection('posts')

    // 获取用户的阅读历史和偏好
    const userPreferences = await this.getUserPreferences(userId)

    return await collection
      .aggregate([
        {
          $match: {
            status: 'published',
            authorId: { $ne: ObjectId(userId) }, // 排除自己的文章
          },
        },
        {
          $addFields: {
            relevanceScore: {
              $add: [
                // 标签匹配分数
                {
                  $multiply: [
                    { $size: { $setIntersection: ['$tags', userPreferences.preferredTags] } },
                    10,
                  ],
                },
                // 分类匹配分数
                {
                  $multiply: [
                    {
                      $size: {
                        $setIntersection: ['$categories', userPreferences.preferredCategories],
                      },
                    },
                    15,
                  ],
                },
                // 热度分数
                { $divide: ['$stats.views', 100] },
                // 时间衰减分数
                {
                  $multiply: [
                    {
                      $divide: [
                        { $subtract: [new Date(), '$publishedAt'] },
                        1000 * 60 * 60 * 24, // 转换为天数
                      ],
                    },
                    -0.1, // 负权重,越新的文章分数越高
                  ],
                },
              ],
            },
          },
        },
        {
          $match: {
            relevanceScore: { $gt: 0 },
          },
        },
        {
          $sort: { relevanceScore: -1 },
        },
        {
          $limit: limit,
        },
        {
          $project: {
            title: 1,
            slug: 1,
            excerpt: 1,
            author: 1,
            categories: 1,
            tags: 1,
            stats: 1,
            publishedAt: 1,
            relevanceScore: { $round: ['$relevanceScore', 2] },
          },
        },
      ])
      .toArray()
  }

  async getUserPreferences(userId) {
    // 基于用户的阅读历史分析偏好
    const readingHistory = await this.db
      .collection('readingHistory')
      .aggregate([
        {
          $match: { userId: ObjectId(userId) },
        },
        {
          $lookup: {
            from: 'posts',
            localField: 'postId',
            foreignField: '_id',
            as: 'post',
          },
        },
        {
          $unwind: '$post',
        },
        {
          $group: {
            _id: null,
            preferredTags: { $push: '$post.tags' },
            preferredCategories: { $push: '$post.categories' },
          },
        },
        {
          $project: {
            preferredTags: {
              $reduce: {
                input: '$preferredTags',
                initialValue: [],
                in: { $setUnion: ['$$value', '$$this'] },
              },
            },
            preferredCategories: {
              $reduce: {
                input: '$preferredCategories',
                initialValue: [],
                in: { $setUnion: ['$$value', '$$this'] },
              },
            },
          },
        },
      ])
      .toArray()

    return readingHistory[0] || { preferredTags: [], preferredCategories: [] }
  }
}

索引策略与优化

1. 索引设计

// 索引管理类
class IndexManager {
  constructor(db) {
    this.db = db
  }

  // 创建基础索引
  async createBasicIndexes() {
    const collections = {
      users: [
        { email: 1 }, // 唯一索引
        { username: 1 }, // 唯一索引
        { 'profile.firstName': 1, 'profile.lastName': 1 }, // 复合索引
        { tags: 1 }, // 数组索引
        { createdAt: -1 }, // 时间索引
        { isActive: 1, createdAt: -1 }, // 复合索引
      ],
      posts: [
        { slug: 1 }, // 唯一索引
        { authorId: 1, status: 1 }, // 复合索引
        { categories: 1 }, // 数组索引
        { tags: 1 }, // 数组索引
        { publishedAt: -1 }, // 时间索引
        { status: 1, publishedAt: -1 }, // 复合索引
        { 'stats.views': -1 }, // 嵌套字段索引
        { title: 'text', content: 'text', excerpt: 'text' }, // 文本索引
      ],
      comments: [
        { postId: 1, createdAt: -1 }, // 复合索引
        { authorId: 1 }, // 外键索引
        { createdAt: -1 }, // 时间索引
      ],
    }

    for (const [collectionName, indexes] of Object.entries(collections)) {
      const collection = this.db.collection(collectionName)

      for (const index of indexes) {
        try {
          const options = {}

          // 设置唯一索引
          if (collectionName === 'users' && (index.email || index.username)) {
            options.unique = true
          }
          if (collectionName === 'posts' && index.slug) {
            options.unique = true
          }

          // 设置稀疏索引
          if (index.publishedAt) {
            options.sparse = true
          }

          await collection.createIndex(index, options)
          console.log(`创建索引成功: ${collectionName}`, index)
        } catch (error) {
          console.error(`创建索引失败: ${collectionName}`, index, error.message)
        }
      }
    }
  }

  // 分析查询性能
  async analyzeQuery(collection, query, options = {}) {
    const coll = this.db.collection(collection)

    // 使用 explain 分析查询计划
    const explanation = await coll.find(query, options).explain('executionStats')

    const stats = explanation.executionStats

    return {
      executionTimeMillis: stats.executionTimeMillis,
      totalDocsExamined: stats.totalDocsExamined,
      totalDocsReturned: stats.totalDocsReturned,
      indexesUsed: explanation.queryPlanner.winningPlan.inputStage?.indexName || 'COLLSCAN',
      efficiency: stats.totalDocsReturned / stats.totalDocsExamined,
      needsIndex: stats.totalDocsExamined > stats.totalDocsReturned * 10,
    }
  }

  // 获取索引使用统计
  async getIndexStats(collectionName) {
    const collection = this.db.collection(collectionName)

    const stats = await collection.aggregate([{ $indexStats: {} }]).toArray()

    return stats.map((stat) => ({
      name: stat.name,
      usageCount: stat.accesses.ops,
      lastUsed: stat.accesses.since,
    }))
  }

  // 查找未使用的索引
  async findUnusedIndexes(collectionName) {
    const stats = await this.getIndexStats(collectionName)

    return stats.filter((stat) => stat.name !== '_id_' && stat.usageCount === 0)
  }

  // 优化建议
  async getOptimizationSuggestions(collectionName) {
    const collection = this.db.collection(collectionName)
    const suggestions = []

    // 检查集合统计信息
    const collStats = await this.db.command({ collStats: collectionName })
    const docCount = collStats.count
    const avgDocSize = collStats.avgObjSize

    // 建议1: 大集合需要合适的索引
    if (docCount > 100000) {
      suggestions.push({
        type: 'performance',
        message: `集合 ${collectionName}${docCount} 个文档,确保查询字段都有索引`,
      })
    }

    // 建议2: 文档大小过大
    if (avgDocSize > 16 * 1024 * 1024 * 0.1) {
      // 10% of 16MB
      suggestions.push({
        type: 'storage',
        message: `平均文档大小 ${Math.round(avgDocSize / 1024)}KB,考虑使用引用而非嵌入`,
      })
    }

    // 建议3: 检查未使用的索引
    const unusedIndexes = await this.findUnusedIndexes(collectionName)
    if (unusedIndexes.length > 0) {
      suggestions.push({
        type: 'maintenance',
        message: `发现 ${unusedIndexes.length} 个未使用的索引,考虑删除以节省存储空间`,
        indexes: unusedIndexes.map((idx) => idx.name),
      })
    }

    return suggestions
  }
}

2. 查询优化技巧

// 查询优化工具类
class QueryOptimizer {
  constructor(db) {
    this.db = db
  }

  // 优化分页查询
  async optimizedPagination(collectionName, filter, page, limit, sortField = '_id') {
    const collection = this.db.collection(collectionName)

    // 使用游标分页而非 skip/limit
    if (page === 1) {
      return await collection
        .find(filter)
        .sort({ [sortField]: -1 })
        .limit(limit)
        .toArray()
    } else {
      // 获取上一页的最后一个文档的排序字段值
      const lastDoc = await this.getLastDocumentFromPreviousPage(
        collectionName,
        filter,
        page - 1,
        limit,
        sortField
      )

      if (lastDoc) {
        const cursorFilter = {
          ...filter,
          [sortField]: { $lt: lastDoc[sortField] },
        }

        return await collection
          .find(cursorFilter)
          .sort({ [sortField]: -1 })
          .limit(limit)
          .toArray()
      }
    }
  }

  // 批量操作优化
  async optimizedBulkWrite(collectionName, operations) {
    const collection = this.db.collection(collectionName)
    const batchSize = 1000
    const results = []

    for (let i = 0; i < operations.length; i += batchSize) {
      const batch = operations.slice(i, i + batchSize)

      try {
        const result = await collection.bulkWrite(batch, {
          ordered: false, // 允许并行执行
          writeConcern: { w: 1, j: true }, // 确保写入持久化
        })

        results.push(result)
      } catch (error) {
        console.error(`批量操作失败 (批次 ${Math.floor(i / batchSize) + 1}):`, error)
        throw error
      }
    }

    return results
  }

  // 聚合管道优化
  optimizeAggregationPipeline(pipeline) {
    const optimizedPipeline = [...pipeline]

    // 优化1: 将 $match 阶段尽早执行
    const matchStages = optimizedPipeline.filter((stage) => stage.$match)
    const otherStages = optimizedPipeline.filter((stage) => !stage.$match)

    // 优化2: 将 $project 阶段合并
    const projectStages = otherStages.filter((stage) => stage.$project)
    if (projectStages.length > 1) {
      const mergedProject = projectStages.reduce(
        (acc, stage) => ({
          ...acc,
          ...stage.$project,
        }),
        {}
      )

      const nonProjectStages = otherStages.filter((stage) => !stage.$project)
      otherStages.splice(0, otherStages.length, ...nonProjectStages, { $project: mergedProject })
    }

    // 优化3: 使用 $limit 减少处理的文档数量
    const limitStage = otherStages.find((stage) => stage.$limit)
    if (limitStage) {
      const limitIndex = otherStages.indexOf(limitStage)
      const beforeLimit = otherStages.slice(0, limitIndex)
      const afterLimit = otherStages.slice(limitIndex)

      // 将 $limit 尽可能前移
      const canMoveLimit = beforeLimit.every(
        (stage) => stage.$sort || stage.$match || stage.$project
      )

      if (canMoveLimit) {
        otherStages.splice(limitIndex, 1)
        otherStages.splice(beforeLimit.length - 1, 0, limitStage)
      }
    }

    return [...matchStages, ...otherStages]
  }

  // 查询性能监控
  async monitorSlowQueries(thresholdMs = 100) {
    // 启用慢查询日志
    await this.db.admin().command({
      profile: 2,
      slowms: thresholdMs,
    })

    // 获取慢查询
    const slowQueries = await this.db
      .collection('system.profile')
      .find({
        ts: { $gte: new Date(Date.now() - 24 * 60 * 60 * 1000) }, // 最近24小时
        millis: { $gte: thresholdMs },
      })
      .sort({ ts: -1 })
      .limit(100)
      .toArray()

    return slowQueries.map((query) => ({
      timestamp: query.ts,
      duration: query.millis,
      namespace: query.ns,
      command: query.command,
      docsExamined: query.docsExamined,
      docsReturned: query.docsReturned,
      planSummary: query.planSummary,
    }))
  }
}

数据建模最佳实践

1. 关系建模策略

// 不同关系类型的建模策略
class DataModelingStrategies {
  // 一对一关系 - 嵌入式设计
  static oneToOneEmbedded() {
    return {
      user: {
        _id: ObjectId('...'),
        username: 'john_doe',
        email: 'john@example.com',
        profile: {
          // 嵌入式一对一
          firstName: 'John',
          lastName: 'Doe',
          bio: 'Software Developer',
          avatar: 'https://example.com/avatar.jpg',
          socialLinks: {
            twitter: '@johndoe',
            github: 'johndoe',
            linkedin: 'john-doe',
          },
        },
        settings: {
          // 嵌入式一对一
          theme: 'dark',
          language: 'zh-CN',
          timezone: 'Asia/Shanghai',
          notifications: {
            email: true,
            push: false,
            sms: true,
          },
        },
      },
    }
  }

  // 一对多关系 - 嵌入式设计(适用于"多"的一方数量有限)
  static oneToManyEmbedded() {
    return {
      blogPost: {
        _id: ObjectId('...'),
        title: 'MongoDB 数据建模',
        content: '...',
        comments: [
          // 嵌入式一对多
          {
            _id: ObjectId('...'),
            author: 'Alice',
            content: '很好的文章!',
            createdAt: new Date(),
            replies: [
              {
                _id: ObjectId('...'),
                author: 'Bob',
                content: '我也这么认为',
                createdAt: new Date(),
              },
            ],
          },
        ],
      },
    }
  }

  // 一对多关系 - 引用设计(适用于"多"的一方数量很大)
  static oneToManyReferenced() {
    return {
      author: {
        _id: ObjectId('507f1f77bcf86cd799439011'),
        name: 'John Doe',
        email: 'john@example.com',
        bio: 'Prolific writer',
      },
      posts: [
        {
          _id: ObjectId('507f1f77bcf86cd799439012'),
          title: 'First Post',
          content: '...',
          authorId: ObjectId('507f1f77bcf86cd799439011'), // 引用作者
          createdAt: new Date(),
        },
        {
          _id: ObjectId('507f1f77bcf86cd799439013'),
          title: 'Second Post',
          content: '...',
          authorId: ObjectId('507f1f77bcf86cd799439011'), // 引用作者
          createdAt: new Date(),
        },
      ],
    }
  }

  // 多对多关系 - 数组引用
  static manyToManyArrayReferences() {
    return {
      user: {
        _id: ObjectId('507f1f77bcf86cd799439011'),
        username: 'john_doe',
        followingIds: [
          // 关注的用户ID数组
          ObjectId('507f1f77bcf86cd799439012'),
          ObjectId('507f1f77bcf86cd799439013'),
        ],
        followerIds: [
          // 粉丝用户ID数组
          ObjectId('507f1f77bcf86cd799439014'),
          ObjectId('507f1f77bcf86cd799439015'),
        ],
      },
    }
  }

  // 多对多关系 - 中间集合
  static manyToManyJunctionCollection() {
    return {
      users: [
        {
          _id: ObjectId('507f1f77bcf86cd799439011'),
          username: 'john_doe',
        },
        {
          _id: ObjectId('507f1f77bcf86cd799439012'),
          username: 'jane_smith',
        },
      ],
      roles: [
        {
          _id: ObjectId('507f1f77bcf86cd799439021'),
          name: 'admin',
          permissions: ['read', 'write', 'delete'],
        },
        {
          _id: ObjectId('507f1f77bcf86cd799439022'),
          name: 'editor',
          permissions: ['read', 'write'],
        },
      ],
      userRoles: [
        // 中间集合
        {
          _id: ObjectId('507f1f77bcf86cd799439031'),
          userId: ObjectId('507f1f77bcf86cd799439011'),
          roleId: ObjectId('507f1f77bcf86cd799439021'),
          assignedAt: new Date(),
          assignedBy: ObjectId('507f1f77bcf86cd799439012'),
        },
      ],
    }
  }
}

2. 性能优化模式

// 性能优化设计模式
class PerformancePatterns {
  // 模式1: 计算字段模式
  static computedFieldPattern() {
    return {
      // 在写入时计算并存储聚合值
      order: {
        _id: ObjectId('...'),
        items: [
          { productId: ObjectId('...'), price: 100, quantity: 2 },
          { productId: ObjectId('...'), price: 50, quantity: 1 },
        ],
        // 预计算的字段
        itemCount: 2,
        totalAmount: 250,
        averageItemPrice: 125,
        createdAt: new Date(),
      },
    }
  }

  // 模式2: 桶模式(时间序列数据)
  static bucketPattern() {
    return {
      // 将时间序列数据按时间段分桶存储
      temperatureReadings: {
        _id: ObjectId('...'),
        sensorId: 'sensor_001',
        date: new Date('2024-03-10'),
        hour: 14, // 14:00-14:59 的数据桶
        readings: [
          { minute: 0, temperature: 23.5, humidity: 65 },
          { minute: 1, temperature: 23.6, humidity: 64 },
          { minute: 2, temperature: 23.4, humidity: 66 },
          // ... 最多60个读数
        ],
        count: 60,
        avgTemperature: 23.5,
        minTemperature: 23.1,
        maxTemperature: 23.9,
      },
    }
  }

  // 模式3: 近似计数模式
  static approximateCountingPattern() {
    return {
      // 使用近似计数避免精确计数的性能开销
      post: {
        _id: ObjectId('...'),
        title: '热门文章',
        content: '...',
        stats: {
          viewCount: 15420, // 精确计数
          viewCountApprox: '15K+', // 近似显示
          likeCount: 892,
          likeCountApprox: '800+',
          lastUpdated: new Date(),
        },
      },
    }
  }

  // 模式4: 预聚合模式
  static preAggregationPattern() {
    return {
      // 原始数据
      salesTransaction: {
        _id: ObjectId('...'),
        productId: ObjectId('...'),
        categoryId: ObjectId('...'),
        amount: 150,
        date: new Date('2024-03-10T10:30:00Z'),
      },

      // 预聚合的日报表
      dailySalesReport: {
        _id: ObjectId('...'),
        date: new Date('2024-03-10'),
        totalSales: 15420,
        transactionCount: 156,
        averageTransactionAmount: 98.85,
        topCategories: [
          { categoryId: ObjectId('...'), sales: 5200, count: 52 },
          { categoryId: ObjectId('...'), sales: 3800, count: 38 },
        ],
        topProducts: [
          { productId: ObjectId('...'), sales: 1200, count: 8 },
          { productId: ObjectId('...'), sales: 980, count: 12 },
        ],
      },
    }
  }

  // 模式5: 版本控制模式
  static versioningPattern() {
    return {
      // 文档版本控制
      document: {
        _id: ObjectId('...'),
        title: '重要文档',
        currentVersion: 3,
        versions: [
          {
            version: 1,
            content: '初始内容',
            author: 'Alice',
            createdAt: new Date('2024-03-01T10:00:00Z'),
          },
          {
            version: 2,
            content: '修订内容',
            author: 'Bob',
            createdAt: new Date('2024-03-05T14:30:00Z'),
            changes: ['添加了新章节', '修正了错误'],
          },
          {
            version: 3,
            content: '最新内容',
            author: 'Alice',
            createdAt: new Date('2024-03-10T09:15:00Z'),
            changes: ['更新了统计数据'],
          },
        ],
      },
    }
  }
}

总结

MongoDB 数据库设计与优化需要考虑多个方面:

  1. 数据建模:根据查询模式选择嵌入式或引用式设计
  2. 索引策略:创建合适的索引以支持查询性能
  3. 查询优化:使用聚合管道和查询优化技巧
  4. 性能模式:应用计算字段、桶模式等优化模式
  5. 监控调优:持续监控性能并进行优化

掌握这些技能,你就能设计出高性能、可扩展的 MongoDB 应用!


MongoDB 是现代应用的理想数据库选择,值得深入学习和实践。