MongoDB教程-7

正如在MongoDB关系的最后一章中所看到的,为了在MongoDB中实现规范化的数据库结构,我们使用了引用关系的概念,也被称为手动引用,在这个概念中,我们手动将被引用文档的id存储在其他文档中。然而,在一个文档包含来自不同集合的引用的情况下,我们可以使用MongoDB DBRefs。

DBRefs与手工引用
作为一个例子,我们将使用DBRefs而不是手动引用,考虑一个数据库,我们在不同的集合(address_home、address_office、address_mailing等)中存储不同类型的地址(家庭、办公室、邮件等)。现在,当一个用户集合的文档引用一个地址时,它也需要根据地址类型来指定查找哪个集合。在这种情况下,如果一个文档引用了许多集合的文档,我们应该使用DBRefs。

使用DBRefs
在 DBRefs 中有三个字段 --

$ref - 这个字段指定了被引用文档的集合

$id - 这个字段指定了被引用文档的_id字段

$db - 这是一个可选的字段,包含被引用文档所在的数据库的名称。

考虑一个具有DBRef字段地址的用户文档样本,如代码片段所示

{"_id":ObjectId("53402597d852426020000002"),"address": {"$ref": "address_home","$id": ObjectId("534009e4d852427820000002"),"$db": "tutorialspoint"},"contact": "987654321","dob": "01-01-1991","name": "Tom Benzamin"
}

这里的地址DBRef字段指定引用的地址文件位于tutorialspoint数据库下的address_home集合中,其id为534009e4d852427820000002。

下面的代码动态地在$ref参数指定的集合(在我们的例子中是address_home)中寻找一个id为DBRef中$id参数指定的文档。

>var user = db.users.findOne({"name":"Tom Benzamin"})
>var dbRef = user.address
>db[dbRef.$ref].findOne({"_id":(dbRef.$id)})

上述代码返回存在于address_home集合中的以下地址文件 -

{"_id" : ObjectId("534009e4d852427820000002"),"building" : "22 A, Indiana Apt","pincode" : 123456,"city" : "Los Angeles","state" : "California"
}

什么是覆盖式查询?
根据MongoDB的官方文档,覆盖式查询是一个查询,其中

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

使用覆盖式查询
为了测试覆盖式查询,请考虑用户集合中的以下文档

{"_id": ObjectId("53402597d852426020000003"),"contact": "987654321","dob": "01-01-1991","gender": "M","name": "Tom Benzamin","user_name": "tombenzamin"
}

我们将首先使用下面的查询为用户集合的性别和用户名称字段创建一个复合索引------。

>db.users.createIndex({gender:1,user_name:1})
{"createdCollectionAutomatically" : false,"numIndexesBefore" : 1,"numIndexesAfter" : 2,"ok" : 1
}

现在,这个索引将涵盖以下查询 --

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

这就是说,对于上述查询,MongoDB不会去寻找数据库文件。相反,它将从索引数据中获取所需的数据,这是非常快的。

由于我们的索引不包括_id字段,我们已经明确地将它从我们的查询结果集中排除,因为MongoDB默认在每个查询中返回_id字段。所以下面的查询不会被包含在上面创建的索引中------。

>db.users.find({gender:"M"},{user_name:1})
{ "_id" : ObjectId("53402597d852426020000003"), "user_name" : "tombenzamin" }

最后,请记住,一个索引不能覆盖一个查询,如果---。

任何被索引的字段是一个数组
任何一个被索引的字段是一个子文件

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

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

在上一章中,我们已经为用户集合中的字段gender和user_name创建了一个索引,使用的查询方式如下

>db.users.createIndex({gender:1,user_name:1})
{"numIndexesBefore" : 2,"numIndexesAfter" : 2,"note" : "all indexes already exist","ok" : 1
}

我们现在将在以下查询中使用$explain --

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

上述 explain() 查询返回以下分析结果 -

{"queryPlanner" : {"plannerVersion" : 1,"namespace" : "mydb.users","indexFilterSet" : false,"parsedQuery" : {"gender" : {"$eq" : "M"}},"queryHash" : "B4037D3C","planCacheKey" : "DEAAE17C","winningPlan" : {"stage" : "PROJECTION_COVERED","transformBy" : {"user_name" : 1,"_id" : 0},"inputStage" : {"stage" : "IXSCAN","keyPattern" : {"gender" : 1,"user_name" : 1},"indexName" : "gender_1_user_name_1","isMultiKey" : false,"multiKeyPaths" : {"gender" : [ ],"user_name" : [ ]},"isUnique" : false,"isSparse" : false,"isPartial" : false,"indexVersion" : 2,"direction" : "forward","indexBounds" : {"gender" : ["[\"M\", \"M\"]"],"user_name" : ["[MinKey, MaxKey]"]}}},"rejectedPlans" : [ ]},"serverInfo" : {"host" : "Krishna","port" : 27017,"version" : "4.2.1","gitVersion" : "edf6d45851c0b9ee15548f0f847df141764a317e"},"ok" : 1
}

我们现在来看看这个结果集中的字段 -

indexOnly的真值表示这个查询使用了索引。

cursor字段指定了使用的游标类型。BTreeCursor类型表示使用了一个索引,并且给出了所使用的索引的名称。BasicCursor表示在没有使用任何索引的情况下进行了一次全扫描。

n表示返回的匹配文档的数量。

nscannedObjects表示扫描的文件总数。

nscanned表示扫描的文档或索引条目的总数。

使用$hint


$hint操作符强制查询优化器使用指定的索引来运行查询。当你想用不同的索引来测试一个查询的性能时,这特别有用。

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

为了分析上述查询,使用$explain -

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

由此得出以下结果−

{"queryPlanner" : {"plannerVersion" : 1,"namespace" : "mydb.users","indexFilterSet" : false,"parsedQuery" : {"gender" : {"$eq" : "M"}},"queryHash" : "B4037D3C","planCacheKey" : "DEAAE17C","winningPlan" : {"stage" : "PROJECTION_COVERED","transformBy" : {"user_name" : 1,"_id" : 0},"inputStage" : {"stage" : "IXSCAN","keyPattern" : {"gender" : 1,"user_name" : 1},"indexName" : "gender_1_user_name_1","isMultiKey" : false,"multiKeyPaths" : {"gender" : [ ],"user_name" : [ ]},"isUnique" : false,"isSparse" : false,"isPartial" : false,"indexVersion" : 2,"direction" : "forward","indexBounds" : {"gender" : ["[\"M\", \"M\"]"],"user_name" : ["[MinKey, MaxKey]"]}}},"rejectedPlans" : [ ]},"serverInfo" : {"host" : "Krishna","port" : 27017,"version" : "4.2.1",109"gitVersion" : "edf6d45851c0b9ee15548f0f847df141764a317e"},"ok" : 1
}

原子操作的模型数据
推荐的维护原子性的方法是将所有相关的信息,经常更新的信息,使用嵌入式文件保存在一个文件中。这将确保单个文档的所有更新都是原子性的。

假设我们创建了一个名为productDetails的集合,并在其中插入了一个文档,如下所示

>db.createCollection("products")
{ "ok" : 1 }
> db.productDetails.insert({"_id":1,"product_name": "Samsung S3","category": "mobiles","product_total": 5,"product_available": 3,"product_bought_by": [{"customer": "john","date": "7-Jan-2014"},{"customer": "mark","date": "8-Jan-2014"}]}
)
WriteResult({ "nInserted" : 1 })
>

在这份文件中,我们在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:"rob",date:"9-Jan-2014"}} }    
})

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

与此相反,考虑一下这样的情况:我们可能把产品的可用性和谁购买了该产品的信息分开保存。在这种情况下,我们将首先使用第一个查询来检查产品是否可用。然后在第二个查询中,我们将更新购买信息。然而,有可能在这两个查询的执行过程中,有其他用户购买了该产品,而该产品已不再可用。在不知道这一点的情况下,我们的第二个查询将根据我们第一个查询的结果来更新购买信息。这将使数据库不一致,因为我们已经售出了一个不可用的产品。

高级索引

我们在名为用户的集合中插入了以下文件,如下图所示

db.users.insert({"address": {"city": "Los Angeles","state": "California","pincode": "123"},"tags": ["music","cricket","blogs"],"name": "Tom Benzamin"}
)

上述文件包含一个地址子文件和一个标签阵列。

阵列字段的索引
假设我们想根据用户的标签来搜索用户文档。为此,我们将在集合中的标签数组上创建一个索引。

在数组上创建索引又会为其每个字段创建单独的索引条目。所以在我们的例子中,当我们在tags数组上创建索引时,将为其值music、cricket和blogs创建单独的索引。

要在tags数组上创建一个索引,请使用以下代码

>db.users.createIndex({"tags":1})
{
"createdCollectionAutomatically" : false,
"numIndexesBefore" : 2,
"numIndexesAfter" : 3,
"ok" : 1
}
>

在创建索引之后,我们可以像这样在集合的tags字段上进行搜索

> db.users.find({tags:"cricket"}).pretty()
{"_id" : ObjectId("5dd7c927f1dd4583e7103fdf"),"address" : {"city" : "Los Angeles","state" : "California","pincode" : "123"},"tags" : ["music","cricket","blogs"],"name" : "Tom Benzamin"
}
>

要验证是否使用了正确的索引,请使用以下解释命令−

>db.users.find({tags:"cricket"}).explain()

这将给出以下结果−

{"queryPlanner" : {"plannerVersion" : 1,"namespace" : "mydb.users","indexFilterSet" : false,"parsedQuery" : {"tags" : {"$eq" : "cricket"}},"queryHash" : "9D3B61A7","planCacheKey" : "04C9997B","winningPlan" : {"stage" : "FETCH","inputStage" : {"stage" : "IXSCAN","keyPattern" : {"tags" : 1},"indexName" : "tags_1","isMultiKey" : false,"multiKeyPaths" : {"tags" : [ ]},"isUnique" : false,"isSparse" : false,"isPartial" : false,"indexVersion" : 2,"direction" : "forward","indexBounds" : {"tags" : ["[\"cricket\", \"cricket\"]"]}}},"rejectedPlans" : [ ]},"serverInfo" : {"host" : "Krishna","port" : 27017,"version" : "4.2.1","gitVersion" : "edf6d45851c0b9ee15548f0f847df141764a317e"},"ok" : 1
}
>

上述命令的结果是 "cursor":"BtreeCursor tags_1",这证实了正确的索引被使用。

对子文档字段进行索引
假设我们想根据城市、州和平码字段来搜索文件。由于所有这些字段都是地址子文件字段的一部分,我们将在子文件的所有字段上创建一个索引。

为了在子文档的所有三个字段上创建索引,请使用以下代码

>db.users.createIndex({"address.city":1,"address.state":1,"address.pincode":1})
{"numIndexesBefore" : 4,"numIndexesAfter" : 4,"note" : "all indexes already exist","ok" : 1
}
>

一旦建立了索引,我们就可以利用这个索引来搜索任何一个子文件字段,如下所示

> db.users.find({"address.city":"Los Angeles"}).pretty()
{"_id" : ObjectId("5dd7c927f1dd4583e7103fdf"),"address" : {"city" : "Los Angeles","state" : "California","pincode" : "123"},"tags" : ["music","cricket","blogs"],"name" : "Tom Benzamin"
}  

记住,查询表达式必须遵循指定的索引的顺序。因此,上面创建的索引将支持以下查询 -

>db.users.find({"address.city":"Los Angeles","address.state":"California"}).pretty()
{"_id" : ObjectId("5dd7c927f1dd4583e7103fdf"),"address" : {"city" : "Los Angeles","state" : "California","pincode" : "123"},"tags" : ["music","cricket","blogs"],"name" : "Tom Benzamin"
}
>

索引的局限性

额外的开销
每个索引都会占用一些空间,并在每次插入、更新和删除时造成开销。因此,如果你很少使用你的集合进行读取操作,不使用索引是有意义的。

内存的使用
由于索引被存储在RAM中,你应该确保索引的总大小不超过RAM的限制。如果总大小增加了RAM的大小,它将开始删除一些索引,导致性能损失。

查询限制
索引不能用于使用--的查询。

正则表达式或否定运算符,如$nin, $not, 等。
算术运算符,如$mod,等等。
$where条款
因此,建议你总是检查你的查询的索引使用情况。

索引键的限制
从2.6版本开始,如果现有的索引字段的值超过了索引键的限制,MongoDB将不会创建一个索引。

插入超过索引键限制的文档
如果任何文档的索引字段值超过了索引键限制,MongoDB将不会将该文档插入到一个有索引的集合。mongorestore和mongoimport工具的情况也是如此。

最大范围
一个集合不能有超过64个索引。
索引名称的长度不能超过125个字符。
一个复合索引最多可以有31个字段的索引。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/20646.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

LangChain+ChatGLM大模型应用落地实践(一)

LLMs的落地框架(LangChain),给LLMs套上一层盔甲,快速构建自己的新一代人工智能产品。 一、简介二、LangChain源码三、租用云服务器实例四、部署实例 一、简介 LangChain是一个近期非常活跃的开源代码库,目前也还在快速…

使用vscode进行远程开发服务器配置

1.下载vscode 2.给vscode 安装python 和 remote ssh插件 remote—SSH扩展允许您使用任何具有SSH服务器的远程机器作为您的开发环境。 3.安装remote-SSH插件之后,vscode左侧出现电脑图标,即为远程服务,按图依次点击,进行服务器配置…

运维:18工作中常用 Shell 脚本, 强烈推荐

1、检测两台服务器指定目录下的文件一致性 #!/bin/bash ###################################### 检测两台服务器指定目录下的文件一致性 ##################################### #通过对比两台服务器上文件的md5值,达到检测一致性的目的 dir=/data/web b_ip=192…

访问者模式——操作复杂对象结构

1、简介 1.1、概述 访问者模式是一种较为复杂的行为型设计模式,它包含访问者和被访问元素两个主要组成部分。这些被访问的元素通常具有不同的类型,且不同的访问者可以对它们进行不同的访问操作。访问者模式使得用户可以在不修改现有系统的情况下扩展系…

TensorRT学习笔记--基于TensorRT部署YoloV3, YoloV5和YoloV8

目录 1--完整项目 2--模型转换 3--编译项目 4--序列化模型 5--推理测试 1--完整项目 以下以 YoloV8 为例进行图片和视频的推理,完整项目地址如下:https://github.com/liujf69/TensorRT-Demo git clone https://github.com/liujf69/TensorRT-Demo.…

Open3D点云数据处理(十八):最小二乘直线拟合(二维)

文章目录 1 最小二乘直线拟合原理2 最小二乘直线拟合代码实现3 点云最小二乘直线拟合专栏目录:Open3D点云数据处理(Python) 1 最小二乘直线拟合原理 最小二乘直线拟合是一种常见的数据拟合方法,通常用于拟合一组二元数据点的直线方程。其原理是通过最小化数据点到拟合直线…

PostgreSql 进程及内存结构

一、进程及内存架构 PostgreSQL 数据库运行时,使用如下命令可查询数据库进程,正对应上述结构图。 [postgreslocalhost ~]$ ps -ef|grep post postgres 8649 1 0 15:05 ? 00:00:00 /app/pg13/bin/postgres -D /data/pg13/data postgres …

一篇聊聊JVM优化:堆

一、Java 堆概念 1、简介 对于Java应用程序来说,Java堆(Java Heap)是虚拟机所管理的内存中最大的一块。Java堆是被所有线程共享 的一块内存区域,在虚拟机启动时创建。此内存区域的唯一目的就是存放对象实例,Java 世界…

【算法第十八天8.2】235. 二叉搜索树的最近公共祖先 701.二叉搜索树中的插入操作 450.删除二叉搜索树中的节点

链接力扣235. 二叉搜索树的最近公共祖先 思路 // 二叉搜索树的最近公共祖先,可以根据值判断 class Solution {public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {// 如果p、q在左子树if (root.val > p.val && root.val…

MongoDB文档--架构体系

阿丹: 在开始学习先了解以及目标知识的架构体系。就能事半功倍。 架构体系 MongoDB的架构体系由以下几部分组成: 存储结构:MongoDB采用文档型存储结构,一个数据库包含多个集合,一个集合包含多个文档。存储形式&#…

js设计模式-常见的13种

在JavaScript中,有多种常见的设计模式可供使用。以下是13种常见的JavaScript设计模式: JavaScript设计模式 单例模式(Singleton Pattern)工厂模式(Factory Pattern)抽象工厂模式(Abstract Fac…

Quartz使用文档,使用Quartz实现动态任务,Spring集成Quartz,Quartz集群部署,Quartz源码分析

文章目录 一、Quartz 基本介绍二、Quartz Java 编程1、文档2、引入依赖3、入门案例4、默认配置文件 三、Quartz 重要组件1、Quartz架构体系2、JobDetail3、Trigger(1)代码实例(2)SimpleTrigger(3)CalendarI…

python-Excel数据模型文档转为MySQL数据库建表语句(需要连接数据库)-工作小记

将指定Excel文档转为create table 建表语句。该脚本适用于单一且简单的建表语句 呈现效果 代码 # -*- coding:utf-8 -*- # Time : 2023/8/2 17:50 # Author: 水兵没月 # File : excel_2_mysql建表语句.py import reimport pandas as pd import mysql.connectordb 库名mydb m…

ELK高级搜索(一)

文章目录 ELK搜索1.简介1.1 内容1.2 面向 2.Elastic Stack2.1 简介2.2 特色2.3 组件介绍 3.Elasticsearch3.1 搜索是什么3.2 数据库搜索3.3 全文检索3.4 倒排索引3.5 Lucene3.6 Elasticsearch3.6.1 Elasticsearch的功能3.6.2 Elasticsearch使…

【算法题】2790. 长度递增组的最大数目

题目: 给你一个下标从 0 开始、长度为 n 的数组 usageLimits 。 你的任务是使用从 0 到 n - 1 的数字创建若干组,并确保每个数字 i 在 所有组 中使用的次数总共不超过 usageLimits[i] 次。此外,还必须满足以下条件: 每个组必须…

Python3 网络爬虫开发实战

JavaScript逆向爬虫 JavaScript接口加密技术,JavaScript有以下两个特点: JS代码运行在客户端,所以它必须在用户浏览器加载并运行JS代码公开透明,所以浏览器可以直接获取到正在运行的JS源码。 所以JS代码不安全,任何…

电脑安装新系统不知道去哪里下载,看我就够了

大家在日常生活中肯定都会遇到电脑安装系统的需求,如果去微软官方购买正版的系统又很贵,又不太想花这个冤枉钱,这个时候我们就不得不去网上查找一些免费好用的系统,可是百度一下,或者Google一下,各种下载系…

设计模式思考,简单工厂模式和策略模式的区别?

最近学习了设计模式,学到简单工厂模式和策略模式的时候想,这两个模式不是一样嘛,仔细思考之后发现大体设计思路是一样的,但是细节却有所不一样。 简单工厂模式 简单工厂模式是一种创建型设计模式,它主要涉及对象的创建…

Android性能优化—卡顿分析与布局优化

一、什么是卡顿?或者说我们怎么感知APP卡顿? 这里面涉及到android UI渲染机制,我们先了解一下android UI是怎么渲染的,android的View到底是如何一步一步显示到屏幕上的? android系统渲染页面流程: 1&…

短视频矩阵营销系统技术开发者开发笔记分享

一、开发短视频seo抖音矩阵系统需要遵循以下步骤: 1. 确定系统需求:根据客户的需求,确定系统的功能和特点,例如用户注册登录、视频上传、视频浏览、评论点赞等。 2. 设计系统架构:根据系统需求,设计系统的…