MST

星途 面试题库

面试题:MongoDB复杂条件删除及性能优化

有一个非常大的MongoDB集合,存储了电商平台的订单数据,每个订单文档包含'order_id'、'customer_id'、'order_date'、'products'(订单中的商品数组,每个商品是包含'product_id'、'quantity'、'price'的子文档)、'total_amount'等字段。现在要删除满足以下条件的订单:订单日期在2022年之前,且订单中至少有一个商品的数量大于10且价格小于50,同时这些订单的总金额小于1000。请详细描述删除操作的实现思路,并给出优化性能的建议和理由。
43.8万 热度难度
数据库MongoDB

知识考点

AI 面试

面试题答案

一键面试

实现思路

  1. 构建查询条件
    • 对于订单日期在2022年之前,可使用$lt操作符,将日期转换为ISODate格式,例如{ "order_date": { "$lt": ISODate("2022-01-01") } }
    • 对于订单中至少有一个商品的数量大于10且价格小于50,使用$elemMatch操作符在products数组上匹配子文档,即{ "products": { "$elemMatch": { "quantity": { "$gt": 10 }, "price": { "$lt": 50 } } } }
    • 对于订单总金额小于1000,使用$lt操作符,即{ "total_amount": { "$lt": 1000 } }
    • 然后使用$and操作符将上述三个条件组合起来,得到完整的查询条件{ "$and": [ { "order_date": { "$lt": ISODate("2022-01-01") } }, { "products": { "$elemMatch": { "quantity": { "$gt": 10 }, "price": { "$lt": 50 } } } }, { "total_amount": { "$lt": 1000 } } ] }
  2. 执行删除操作: 在MongoDB中,使用deleteMany方法执行删除操作,例如在Node.js中使用MongoDB驱动:
const { MongoClient } = require('mongodb');
const uri = "mongodb://localhost:27017";
const client = new MongoClient(uri);
async function deleteOrders() {
    try {
        await client.connect();
        const database = client.db('ecommerce');
        const orders = database.collection('orders');
        const query = { "$and": [ { "order_date": { "$lt": ISODate("2022-01-01") } }, { "products": { "$elemMatch": { "quantity": { "$gt": 10 }, "price": { "$lt": 50 } } } }, { "total_amount": { "$lt": 1000 } } ] };
        const result = await orders.deleteMany(query);
        console.log(result.deletedCount + " 个订单被删除");
    } finally {
        await client.close();
    }
}
deleteOrders();

优化性能建议及理由

  1. 索引优化
    • 理由:为order_datetotal_amount以及products.quantityproducts.price字段建立复合索引,如orders.createIndex({ order_date: 1, total_amount: 1, "products.quantity": 1, "products.price": 1 })。这样在查询时,MongoDB可以利用索引快速定位符合条件的文档,减少全表扫描,大大提高查询性能,从而加快删除操作速度。
  2. 批量操作
    • 理由:如果数据量极大,一次性删除大量数据可能会对数据库性能产生较大影响,甚至导致数据库卡顿。可以考虑分批删除,每次删除一定数量(如1000条)的符合条件的订单,直到没有符合条件的订单为止。这样可以减少单次操作对系统资源的占用,保证数据库的稳定性和性能。例如在Node.js中:
async function deleteOrdersInBatches() {
    try {
        await client.connect();
        const database = client.db('ecommerce');
        const orders = database.collection('orders');
        const batchSize = 1000;
        let deletedCount = 0;
        let hasMore = true;
        while (hasMore) {
            const query = { "$and": [ { "order_date": { "$lt": ISODate("2022-01-01") } }, { "products": { "$elemMatch": { "quantity": { "$gt": 10 }, "price": { "$lt": 50 } } } }, { "total_amount": { "$lt": 1000 } } ] };
            const result = await orders.deleteMany(query, { limit: batchSize });
            deletedCount += result.deletedCount;
            hasMore = result.deletedCount === batchSize;
        }
        console.log(deletedCount + " 个订单被删除");
    } finally {
        await client.close();
    }
}
deleteOrdersInBatches();
  1. 选择合适的副本集成员执行操作
    • 理由:如果MongoDB采用副本集架构,尽量在主节点上执行删除操作,因为从节点可能存在数据同步延迟,在从节点执行删除可能导致数据不一致。并且主节点负责写入操作,在主节点执行删除操作能确保操作的原子性和一致性,避免潜在的数据问题。