MST

星途 面试题库

面试题:MongoDB数组聚合操作优化

有一个集合orders,文档结构为{ _id: ObjectId('602e16a8c196f45c5076a8c4'), products: [ { productId: 'p1', quantity: 2, price: 10 }, { productId: 'p2', quantity: 1, price: 20 } ] }。现在要通过聚合操作计算每个订单的总金额(quantity * price),并且对总金额大于50的订单进行分组统计订单数量。请写出优化后的聚合管道语句,并阐述优化思路以及在大数据量下可能遇到的性能瓶颈及解决方案。
23.6万 热度难度
数据库MongoDB

知识考点

AI 面试

面试题答案

一键面试

聚合管道语句

db.orders.aggregate([
    // 展开products数组
    { $unwind: "$products" },
    // 计算每个商品的金额
    { $addFields: { amount: { $multiply: ["$products.quantity", "$products.price"] } } },
    // 按订单_id分组并计算每个订单的总金额
    {
        $group: {
            _id: "$_id",
            totalAmount: { $sum: "$amount" }
        }
    },
    // 筛选总金额大于50的订单
    { $match: { totalAmount: { $gt: 50 } } },
    // 分组统计订单数量
    {
        $group: {
            _id: null,
            orderCount: { $sum: 1 }
        }
    }
]);

优化思路

  1. 尽早过滤数据:通过$match操作符尽早筛选出符合条件的数据,可以减少后续操作的数据量。例如在按订单分组计算总金额后,使用$match筛选总金额大于50的订单,避免对不必要的数据进行后续分组统计。
  2. 合理使用$unwind:在计算总金额前,使用$unwind展开products数组,以便能够对每个商品进行金额计算。展开操作尽量放在前面,因为后续的分组操作在数组展开后能更高效地进行。
  3. 减少中间数据量:在$group操作中,只保留必要的字段,如_id和计算出的totalAmount,避免保留大量不必要的原始数据,减少内存占用。

大数据量下可能遇到的性能瓶颈及解决方案

  1. 性能瓶颈

    • 内存消耗$unwind操作会增加数据量,如果products数组很大,可能导致内存消耗剧增。另外,$group操作在大数据量下也可能需要大量内存来存储中间结果。
    • 磁盘I/O:大数据量下,数据无法全部加载到内存,会频繁进行磁盘I/O操作,导致性能下降。
    • 网络传输:如果数据分布在多个节点,大量数据的传输会成为性能瓶颈。
  2. 解决方案

    • 优化内存使用:可以通过调整MongoDB的内存参数,确保有足够的内存来处理数据。同时,在聚合操作中尽量减少中间数据的存储,只保留必要的字段。
    • 磁盘I/O优化:使用固态硬盘(SSD)替代传统机械硬盘,提高磁盘读写速度。此外,可以通过对数据进行合理的分片,减少单个节点的I/O压力。
    • 网络优化:合理配置网络拓扑,确保节点之间有高速稳定的网络连接。对于分布式数据,可以使用数据本地化策略,减少数据在网络中的传输。还可以通过压缩数据在网络上传输,减少传输的数据量。