欢迎光临
我们一直在努力

MongoDB(45) 嵌入式文档与引用的优缺点是什么?

嵌入式文档(Embedded Documents)和引用(References)是MongoDB中常用的两种数据建模方式,它们各有优缺点。选择使用哪种方式取决于具体的应用场景和需求。

嵌入式文档

优点
  • 读取效率高:嵌入式文档通常可以在一次查询中获取所有相关数据,减少了数据库查询次数。
  • 事务一致性:由于所有相关数据都在同一个文档中,更新时可以保证数据的一致性,不需要担心分布式事务问题。
  • 简单性:数据模型简单,查询和处理方便。
  • 缺点
  • 文档大小限制:MongoDB文档大小限制为16MB,嵌入式文档如果过大,可能会超过这个限制。
  • 冗余数据:在一些场景下,嵌入式文档会导致数据冗余,增加存储空间。
  • 更新效率低:如果嵌入的数据结构复杂,更新可能会变得比较繁琐和低效。
  • 引用

    优点
  • 数据去重:引用方式将数据分离到不同的集合中,减少了数据冗余。
  • 灵活性高:各个集合独立更新,灵活性更高,适合复杂的数据模型。
  • 文档大小分离:因为数据被分离到不同的文档中,不会受到单个文档大小的限制。
  • 缺点
  • 查询效率低:需要多次查询才能获取所有相关数据,特别是在大数据量下查询效率可能较低。
  • 事务一致性差:跨集合的更新操作不能保证原子性,需要额外处理事务一致性问题。
  • 复杂性高:数据模型复杂,需要处理数据的关联和联合查询。
  • 使用示例

    以下示例展示了在Node.js中如何使用嵌入式文档和引用,并对比它们的优缺点。

    安装MongoDB的Node.js驱动

    npm install mongodb

    嵌入式文档示例
    插入数据

    const { MongoClient } = require('mongodb');

    async function insertEmbeddedData() {
    const uri = "mongodb://localhost:27017";
    const client = new MongoClient(uri, { useUnifiedTopology: true });

    try {
    await client.connect();
    const db = client.db('myDatabase');
    const usersCollection = db.collection('users');

    await usersCollection.deleteMany({}); // 清空集合

    await usersCollection.insertMany([
    {
    userId: 1,
    name: "Alice",
    address: {
    street: "123 Main St",
    city: "New York",
    state: "NY",
    zip: "10001"
    },
    orders: [
    { orderId: 101, amount: 50.5, date: new Date("2022-01-01") },
    { orderId: 102, amount: 100.0, date: new Date("2022-02-01") }
    ]
    },
    {
    userId: 2,
    name: "Bob",
    address: {
    street: "456 Maple Ave",
    city: "San Francisco",
    state: "CA",
    zip: "94101"
    },
    orders: [
    { orderId: 103, amount: 75.0, date: new Date("2022-01-15") },
    { orderId: 104, amount: 200.0, date: new Date("2022-03-01") }
    ]
    }
    ]);

    console.log("Embedded data inserted");
    } finally {
    await client.close();
    }
    }

    insertEmbeddedData().catch(console.error);

    查询数据

    async function queryEmbeddedData() {
    const uri = "mongodb://localhost:27017";
    const client = new MongoClient(uri, { useUnifiedTopology: true });

    try {
    await client.connect();
    const db = client.db('myDatabase');
    const usersCollection = db.collection('users');

    // 查询某个用户及其所有订单
    console.log("\\nQuery a user and their orders:");
    let user = await usersCollection.findOne({ name: "Alice" });
    console.log(user);

    } finally {
    await client.close();
    }
    }

    queryEmbeddedData().catch(console.error);

    嵌入式文档的查询效率很高,因为所有数据都在同一个文档中。

    引用示例
    插入数据

    const { MongoClient, ObjectId } = require('mongodb');

    async function insertReferencedData() {
    const uri = "mongodb://localhost:27017";
    const client = new MongoClient(uri, { useUnifiedTopology: true });

    try {
    await client.connect();
    const db = client.db('myDatabase');
    const usersCollection = db.collection('users');
    const ordersCollection = db.collection('orders');

    await usersCollection.deleteMany({}); // 清空用户集合
    await ordersCollection.deleteMany({}); // 清空订单集合

    // 插入用户数据
    const users = await usersCollection.insertMany([
    { name: "Alice" },
    { name: "Bob" }
    ]);

    // 插入订单数据,并使用用户的ObjectId作为引用
    await ordersCollection.insertMany([
    { userId: users.insertedIds[0], amount: 50.5, date: new Date("2022-01-01") },
    { userId: users.insertedIds[0], amount: 100.0, date: new Date("2022-02-01") },
    { userId: users.insertedIds[1], amount: 75.0, date: new Date("2022-01-15") },
    { userId: users.insertedIds[1], amount: 200.0, date: new Date("2022-03-01") }
    ]);

    console.log("Referenced data inserted");
    } finally {
    await client.close();
    }
    }

    insertReferencedData().catch(console.error);

    查询数据

    async function queryReferencedData() {
    const uri = "mongodb://localhost:27017";
    const client = new MongoClient(uri, { useUnifiedTopology: true });

    try {
    await client.connect();
    const db = client.db('myDatabase');
    const usersCollection = db.collection('users');
    const ordersCollection = db.collection('orders');

    // 查询某个用户及其所有订单
    console.log("\\nQuery a user and their orders:");
    let user = await usersCollection.findOne({ name: "Alice" });
    let orders = await ordersCollection.find({ userId: user._id }).toArray();
    console.log({ user, orders });

    // 查询所有订单及其对应的用户信息
    console.log("\\nQuery all orders and their corresponding users:");
    let results = await ordersCollection.aggregate([
    {
    $lookup: {
    from: 'users', // 要关联的集合
    localField: 'userId', // orders 集合中的字段
    foreignField: '_id', // users 集合中的字段
    as: 'user' // 输出数组字段
    }
    },
    {
    $unwind: '$user' // 展开数组
    }
    ]).toArray();
    console.log(results);

    } finally {
    await client.close();
    }
    }

    queryReferencedData().catch(console.error);

    通过这些示例,我们可以看到两种数据建模方式在操作上的不同:

  • 嵌入式文档适用于小型数据集和简单的数据关系,读取效率高,数据一致性好,但是在数据量大或者关系复杂的情况下会导致文档过大,更新效率低。

  • 引用适用于大型数据集和复杂的数据关系,数据去重和独立更新灵活,但查询效率低,特别是在需要多次查询获取相关数据时。此外,跨集合的事务一致性需要额外处理。

  • 选择合适的数据建模方式取决于具体的应用场景和需求。对于一些应用,可以混合使用这两种方式,结合它们的优点,达到最佳的性能和灵活性。

    赞(0)
    未经允许不得转载:171主机测评 » MongoDB(45) 嵌入式文档与引用的优缺点是什么?
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址