个性化阅读
专注于IT技术分析

mongodb覆盖查询、分析查询和原子操作

Hola Hola!在上一篇文章中我们讨论了mongodb的关系建模:数据库关系和引用,在这一章中我们一起讨论mongodb的覆盖查询、分析查询和原子操作。

一、覆盖查询

1、什么是覆盖查询?

根据官方的MongoDB文档,一个覆盖查询如下定义:

  • 查询中的所有字段都是索引的一部分。
  • 查询中返回的所有字段都在同一个索引中。

由于查询中出现的所有字段都是索引的一部分,所以MongoDB匹配查询条件并使用相同的索引返回结果,而不需要实际查看文档内部。由于索引存在于RAM中,因此从索引中获取数据要比通过扫描文档获取数据快得多。

2、使用覆盖查询

为了测试覆盖查询,请考虑以下users集合中的文档:

"_id": ObjectId("cc"),
    "contact": "369",
    "gender": "M",
    "name": "TT",
    "user_name": "CC"
}

我们将首先使用以下查询为用户集合在字段gender和user_name上创建一个复合索引:

db.users.ensureIndex({gender:1, user_name:1})

下面是覆盖查询的例子:

db.users.find({gender:"M"}, {user_name:1, _id:0})

也就是说,对于上面的查询,MongoDB不会查找数据库文档,而是它会从索引数据获取所需的数据,这是非常快的。

由于我们的索引不包含_id字段,所以我们将它显式地排除在查询的结果集中,因为MongoDB在每个查询中默认返回_id字段。因而在上面创建的索引中不会包含以下查询:

db.users.find({gender:"M"}, {user_name:1})

在下列情况中一个索引不能覆盖一个查询:

  • 任何索引字段都是一个数组
  • 任何被索引的字段都是子文档

二、分析查询

分析查询是衡量数据库和索引设计的有效性的一个非常重要的方面,我们将学习经常使用的$explain和$hint查询。

1、使用$explain

$explain操作符提供关于查询、查询中使用的索引和其他统计信息的信息,它在分析索引的优化程度时非常有用。

在面的例子中我们已经使用以下查询为字段gender和user_name上的用户集合创建了一个索引:

db.users.ensureIndex({gender:1, user_name:1})

下面是一个使用$explain查询的例子:

db.users.find({gender:"M"}, {user_name:1, _id:0}).explain()

2、使用$hint

$hint操作符强制查询优化器使用指定的索引来运行查询。当你希望测试具有不同索引的查询的性能时,这尤其有用。下面的查询指定了用于此查询的字段gender和user_name上的索引:

db.users.find({gender:"M"}, {user_name:1, _id:0}).hint({gender:1, user_name:1})

下面是使用$explain和$hint的例子:

db.users.find({gender: "M"}, {user_name: 1, _id: 0}).hint({gender: 1, user_name: 1}).explain()

三、原子操作

MongoDB不支持多文档原子事,但是它确实对单个文档提供原子操作。因此,如果一个文档有100个字段,那么update语句要么更新所有字段要么不更新,从而在文档级别维护原子性。

原子操作的模型数据

维护原子性的建议方法是将所有相关信息保存在一个文档中,该文档使用嵌入式文档经常一起更新。这将确保单个文档的所有更新都是原子性的。考虑以下产品文档的例子:

{
    "_id":1,
    "product_name": "Apple",
    "category": "mobiles",
    "product_total": 5,
    "product_available": 3,
    "product_bought_by": [
       {
          "customer": "AA",
          "date": "2010"
       },
       {
          "customer": "BB",
          "date": "2020"
       }
    ]
 }

在本文档中,我们将购买产品的客户的信息嵌入到product_bought_by字段中。每当新客户购买产品时,我们将首先使用product_available字段检查产品是否仍然可用,如果可用我们将减少product_available字段的值,并将新客户的嵌入文档插入product_bought_by字段。我们将为这个功能使用findAndModify命令,因为它在同一步骤中搜索和更新文档。

>db.products.findAndModify({ 
    query:{_id:2, product_available:{$gt:0}}, 
    update:{ 
       $inc:{product_available:-1}, 
       $push:{product_bought_by:{customer:"BB", date:"2020"}} 
    }    
 })

我们的嵌入式文档和使用findAndModify查询的方法确保只有在产品可用时才更新产品购买信息。在同一个查询中,整个事务都是原子性的。

相反地考虑这样一个场景:我们可能分别保留了产品可用性和关于谁购买了产品的信息。在本例子我们将首先使用第一个查询检查产品是否可用,然后在第二个查询中,我们将更新购买信息。但是有可能在执行这两个查询期间,其他一些用户已经购买了该产品并且该产品不再可用。如果不知道这一点,第二个查询将基于第一个查询的结果更新购买信息 这将使数据库不一致,因为我们出售的产品是不可用的。

赞(0)
未经允许不得转载:srcmini » mongodb覆盖查询、分析查询和原子操作

评论 抢沙发

评论前必须登录!