MongoDB - 索引知识

索引简介

什么是索引

索引最常用的比喻就是书籍的目录,查询索引就像查询一本书的目录。

索引支持 MongoDB 查询的高效执行。如果没有索引,MongoDB 必须扫描集合中每一个文档,以选择与查询语句相匹配的文档。如果查询存在适当的索引,MongoDB 就可以使用索引来限制它扫描的文档数。

筛选字段时使用索引提速有以下几个因素:

  • 索引数据通过 B 树来存储,从而使得搜索的时间复杂度为 O(log⁡n)
  • 索引本身存储在高速缓存中,相比磁盘 IO 有大幅的性能提升(有的时候数据量非常大的时候,索引数据也会非常大,当大到超出内存容量的时候,会导致部分索引数据存储在磁盘上,这会导致磁盘 IO 的开销大幅增加,从而影响性能,所以务必要保证有足够的内存能容下所有的索引数据)

索引可以显著缩短查询时间,但是使用索引、维护索引是有代价的。在执行写入操作时,除了要更新文档之外,还必须更新索引,这必然会影响写入的性能。

因此,当有大量写操作而读操作少时,或者不考虑读操作的性能时,都不推荐建立索引。

何时不使用索引

查询结果集在原集合中占比越大,索引就会越低效。

出现这种情况的原因是:使用索引需要进行两次查找:一次是查找索引项,一次是根据索引的指针去查找其指向的文档。而全表扫描只需进行一次查找:查找文档。在最坏的情况下(返回集合内的所有文档),使用索引进行查找的次数会是全表扫描的两倍,通常会明显比全表扫描慢。

根据经验,如果查询返回集合中 30% 或更少的文档,则索引通常可以加快速度。然而,这个数字会在 2%~60% 变动。

通常,索引使用的情况有这些:比较大的集合、比较大的文档、选择性查询。

而全表扫描相对使用的情况有这些:比较小的集合、比较小的文档、非选择性查询。

总结一下就是,在以下情况不推荐使用索引:

  • 有大量写操作而读操作较少的场景,更新损耗比查询的损耗更大,不推荐建索引
  • 查询结果集在原集合中占比越大,索引就会越低效
  • 索引基数(字段去重后的数量)越低,索引的作用就越小

MongoDB 如何选择索引

MongoDB 如何选择索引具有自己的机制,通常是根据要搜索的字段和一些附加信息(比如是否有排序)有关。基于这些信息,系统会识别出可能用于满足查询的候选索引。

当候选索引被选出之后,则会进行候选索引竞赛的阶段。

在竞赛阶段,MongoDB 会分别为这些候选索引创建 1 个查询计划,并在并行线程中运行这些查询计划,每个线程使用不同的索引。

到达目标状态的第一个查询计划成为赢家。更重要的是,具有相同 形状 的其他查询都会选择这个索引。

服务端会维护这些查询计划的缓存,以备将来用于进行相同 形状 的查询。

通常,以下这些事件导致缓存被清除掉:随着时间变化、重建特定的索引、添加或删除索引、显式清除计划缓存、mongod 进程的重启等。

索引的类型

单一索引

MongoDB 提供了默认的 _id 索引,在此之外,还支持对文档的单个字段创建用户定义的升序、降序索引。但是对于单字段索引,索引键的排序顺序并不重要,因为 MongoDB 可以在任意方向上遍历索引。

复合索引

MongoDB 还支持在多个字段上定义索引,即复合索引。

在考虑复合索引的设计时,需要知道对于利用索引的通用查询模式,如何处理其等值过滤、多值过滤以及排序这些部分。大部分情况可以参考以下准则:

  • 等值过滤字段应该在最前面
  • 排序字段应在多值过滤字段之前
  • 多值过滤字段应该在最后面

多键索引

多键索引和复合索引的概念不能搞混,如果一个文档有被索引的数组字段,则该索引会立即被标记为多键索引。

对数组创建索引就是对数组中的每个元素创建索引,而不是对数组本身创建索引。

对数组创建索引有一个例外,即 MongoDB 最多支持对一个数组字段创建索引,索引项中不允许出现多个数组字段,这是为了避免多键索引中的索引项数量呈爆炸式地增长。

多键索引通常会比非多键索引慢一些,可能会有许多索引项指向同一个文档。而一旦索引被标记为多键多键,就再也无法变成非多键索引,唯一办法是将多键索引删除重建成非多键索引。

地理空间索引

为了支持对地理空间坐标数据的高效查询,MongoDB 提供了两个特殊索引:返回结果时使用平面几何的 2d 索引和使用球面几何的 2dsphere 索引。

创建索引时,通过将索引键的值设置成 2d 或者是 2dsphere 即可创建地理空间索引。

文本索引

MongoDB 提供了一种文本索引类型,支持在集合中搜索字符串内容。这些文本索引不存在特定于语言的停用词(如 theaor 等),并且集合中的词干仅存储词根。

文本索引需要一定数量的与被索引字段中单词成比例的键,创建文本索引可能会耗费大量的系统资源。同时写操作通常比对单一索引、符合索引,甚至多键索引的写操作开销更大,应在需求明确时创建文本索引。

创建索引时,通过将索引键的值设置成 text 即可创建文本索引,并且可以同时对多个键创建文本索引。在创建文本索引时,也可以用 $** 表示文档的所有字符串字段。

默认情况下,文本索引中的每个字段都会被平等对待。也可以通过 wights 属性设置本文索引中每个键的权重。但需要注意的是,文本索引一旦被创建,就不能改变索引的权重了(除非删除索引再重建)。

文本索引能解决搜索关键字的问题,但对于在中国使用汉字的应用程序来说,请谨慎使用。从 官方文档 中可以了解到支持到语言,其中并没有包含汉字。

哈希索引

为了支持基于哈希的分片,MongoDB 提供了哈希索引类型,索引字段值的哈希值。这些索引在其范围内具有更随机的值分布,但仅支持等值匹配而不支持范围查询。

对于嵌入文档,哈希索引的哈希函数会折叠其值并计算哈希值,而对于数组,哈希索引是不支持的,对其创建哈希索引时会返回错误。

MongoDB 不支持在哈希索引上指定唯一约束,可以通过对存储原始值的键构建唯一索引以指定唯一约束。

在创建索引时,通过将键的值设置为 hashed 即可将其设置成哈希索引。

索引的属性

唯一索引

索引的唯一属性会导致 MongoDB 拒绝索引字段的重复值。除了唯一约束之外,唯一索引在功能上可与其他 MongoDB 索引相同。

对于单一索引,唯一属性针对的是单个键值;对于复合索引,唯一属性针对的是所有键值的组合。

在某些情况下,索引桶(index bucket)的大小是有限制的,如果索引项超过了索引桶的大小就不会被包含在索引中。

在 MongoDB 4.2 之前,索引中包含的字段必须小于 1024 字节,也就是说大小超过 1024 字节的键不会受到唯一索引的约束;在 MongoDB 4.2 及以后版本,这个限制被去掉了。

创建索引时设置 {unique: true} 可以设置唯一索引。

部分索引

部分索引在 3.2 版本新增,其表示仅索引符合特定过滤表达式的文档。

MongoDB 的部分索引只会在数据的一个子集上创建,通过索引集合中的文档子集,部分索引具有较低的存储要求,可以减少索引创建和维护的性能成本。

创建索引时设置 partialFilterExpression 选项,可以只对符合表达式要求的值做索引。

当查询条件匹配部分索引时,不在索引内的值不在搜索结果当中,如果需要返回那些缺少字段的文档,可以使用 hint 强制执行全表扫描。

稀疏索引

稀疏索引也称为间隙索引,就是包含具有索引字段的文档的条目,跳过没有索引字段的文档。

通过上述的定义可以看出,稀疏索引是部分索引的子集,创建部分索引时设定索引键必须存在的过滤表达式即可达到稀疏索引的作用。

将稀疏索引和唯一索引组合,以拒绝具有字段重复值的文档,但忽略没有索引键的文档。

创建索引时设置 {sparse: true} 可以设置稀疏索引。

TTL 索引

TTL 索引提供了一个过期机制,允许为每一个文档设置一个过期时间,当一个文档达到预设的过期时间之后就会被删除。

TTL 索引有自己适合的场景,如机器生成的事件数据,日志和会话信息等,这些信息通常只需在数据库中保存有限的时间。

MongoDB 会每分钟扫描一次 TTL 索引,因此不应依赖于秒级的粒度。

创建索引时设置 {expireAfterSeconds: <seconds>} 可以设置 TTL 索引,通常索引键时日期类型时,TTL 索引才会起作用。

MongoDB 还提供了一种类似于固定长度队列的集合,称作为“固定集合”。其长度是固定的,当集合已满足设定大小时,旧的文档会被删除,新的文档将取而代之。

通常来说,相对于固定集合,MongoDB 优先推荐使用 TTL 索引,因为其在 WiredTiger 存储引擎(在 3.2 版本开始作为默认存储引擎)中性能更好,可操作性也更强。

不区分大小写的索引

在 3.4 版本,MongoDB 提供了不区分大小写索引属性,支持在不考虑大小写的情况下执行字符串比较的查询。

创建索引时通过设置 {collation: {locale : <locale>, strength : <strength>}} 可以创建不区分大小写的索引。

其中,locale 指定语言规则,可以通过 官方文档 查看更多,使用 strength 可以指定比较级别,可以通过 官方文档 了解更详细内容。

索引的使用

管理索引

索引的所有信息都存储在 system.indexes 集合中,这是一个保留集合,不支持修改或删除,只能通过相关命令对其进行操作。

MongoDB 提供了一些相关命令管理索引,以下是常用的方法:

  • db.collection.createIndex(keys, options, commitQuorum): 创建单个索引
  • db.collection.createIndexes([keyPatterns], options, commitQuorum): 创建多个索引
  • db.collection.dropIndex(index): 删除集合中除 _id 的指定索引
  • db.collection.dropIndexes(): 不传参时可以删除集合中除 _id 的全部索引,也可以指定索引名实现删除指定索引
  • db.collection.getIndexes(): 查询集合的索引信息
  • db.collection.hideIndex(<index>): 在 4.4 版本新增,隐藏索引对查询计划器不可见,不能用于查询,可以通过隐藏索引发现在不删除索引的情况下评估删除所有的潜在影响
  • db.collection.unhideIndex(<index>): 取消隐藏索引

MongoDB 的索引名称可标识索引,大部分的索引管理命令都支持使用名称指定索引。

索引名称的默认形式是 keyname1dir1_keyname2_dir2..._keynameN_dirN,其中 keynameX 是索引的键,dirX是索引的方向(1 或 -1)。

索引名称是有字符数限制的,并且比较多的键时也会难以辨识,因此创建复杂的索引时可以自定义名称。

修改索引

当需要修改索引时,通常的做法是先使用 dropIndex(index) 删除指定索引,再使用 createIndex 重建索引。

修改索引的操作一般发生在应用程序已经上线之后,这时就需要考虑到创建索引既耗时又耗资源,考虑使用 background 选项在后台创建索引,尽可能减少对读写操作的影响。

在 MongoDB 4.2 之后,引入了混合索引创建的机制,即在索引创建的开始和结束时持有排他锁,创建过程中其余部分会交错地让步于读写操作。

索引方向

使用单一索引时,索引键的方向并不重要,MongoDB 会根据排序的方向,选择扫描索引的方向。只有基于多个查询条件进行排序时,索引方向才是重要的。

对于复合索引,有可能对不同的键设置不同的方向,这与实际的业务有关系。

通常是创建与排序方向相同的索引方向,且相互反转(在每个方向上都乘以 -1)的索引是等价的:{age: 1, username: -1} 适用的查询与 {age: -1, username: 1} 完全一样。

索引基数

索引的基数是指集合中某个字段有多少个不同的值,即值去重后的数量。

通常来说,一个字段的基数越高,这个字段上的索引就越有用。对于基数比较低的字段,索引通常无法排除大量可能的匹配项。

一个例子就是,如果对“性别”字段创建索引,而查找“男性”时仅能将搜索空间缩小大约 50%,其索引作用相对是较低的。

根据经验来说,应该在基数比较高的键上创建索引,或者至少应该把基数比较高的键放在复合索引的前面(在低基数的键之前)。

左前缀原则

MongoDB 的复合索引遵循左前缀原则:拥有多个键的索引,可以同时得到所有这些键的前缀组成的索引,但不包括除左前缀之外的其他子集。

比如说,有一个类似 {a: 1, b: 1, c: 1, ..., z: 1} 这样的索引,那么实际上也等于有了 {a: 1}{a: 1, b: 1}{a: 1, b: 1, c: 1} 等一系列索引,但是不会有 {b: 1} 这样的非左前缀的索引。

交叉索引

在 2.6 版本新增,MongoDB 可以使用交叉索引来完成查询。

对于指定复合条件的查询,如果一个索引可以满足查询条件的一部分,而另一个索引可以满足查询条件的另一部分,则 MongoDB 可以使用两个索引的交集来完成查询。

使用复合索引还是使用交叉索引更有效取决于具体的查询和系统。

覆盖查询

当查询子句和查询投影仅包含索引字段时,MongoDB 可以直接从索引返回结果,而无需扫描任何文档或加载文档到内存。

这样的覆盖查询非常有效,效率非常高。必要时还需要对不做查询的字段进行索引,以满足覆盖索引的要求。

如果对一个被覆盖的查询运行 explain,那么结果中会有一个并不处于 FETCH 阶段下的 IXSCAN 阶段,并且在 executionStats 中,totalDocsExamined 的值是 0

查询计划

使用 explain 可以为查询提供大量的信息,它是慢查询的重要诊断工具之一。下述是执行结果示例:

 
{
"queryPlanner": {
"plannerVersion": 1,
"namespace": "test.users",
"indexFilterSet": false,
"parsedQuery": {
"age": {
"$eq": 42
}
},
"winningPlan": {
"stage": "FETCH",
"inputStage": {
"stage": "IXSCAN",
"keyPattern": {
"age": 1,
"username": 1
},
"indexName": "age_1_username_1",
"isMultiKey": false,
"multiKeyPaths": {
"age": [],
"username": []
},
"isUnique": false,
"isSparse": false,
"isPartial": false,
"indexVersion": 2,
"direction": "forward",
"indexBounds": {
"age": [
"[42.0, 42.0]"
],
"username": [
"[MinKey, MaxKey]"
]
}
}
},
"rejectedPlans": []
},
"executionStats": {
"executionSuccess": true,
"nReturned": 8449,
"executionTimeMillis": 15,
"totalKeysExamined": 8449,
"totalDocsExamined": 8449,
"executionStages": {
"stage": "FETCH",
"nReturned": 8449,
"executionTimeMillisEstimate": 10,
"works": 8450,
"advanced": 8449,
"needTime": 0,
"needYield": 0,
"saveState": 66,
"restoreState": 66,
"isEOF": 1,
"invalidates": 0,
"docsExamined": 8449,
"alreadyHasObj": 0,
"inputStage": {
"stage": "IXSCAN",
"nReturned": 8449,
"executionTimeMillisEstimate": 0,
"works": 8450,
"advanced": 8449,
"needTime": 0,
"needYield": 0,
"saveState": 66,
"restoreState": 66,
"isEOF": 1,
"invalidates": 0,
"keyPattern": {
"age": 1,
"username": 1
},
"indexName": "age_1_username_1",
"isMultiKey": false,
"multiKeyPaths": {
"age": [],
"username": []
},
"isUnique": false,
"isSparse": false,
"isPartial": false,
"indexVersion": 2,
"direction": "forward",
"indexBounds": {
"age": [
"[42.0, 42.0]"
],
"username": [
"[MinKey, MaxKey]"
]
},
"keysExamined": 8449,
"seeks": 1,
"dupsTested": 0,
"dupsDropped": 0,
"seenInvalidated": 0
}
}
},
"serverInfo": {
"host": "eoinbrazil-laptop-osx",
"port": 27017,
"version": "4.0.12",
"gitVersion": "5776e3cbf9e7afe86e6b29e22520ffb6766e95d4"
},
"ok": 1
}

在 explain 的结果当中,queryPlanner 描述了所有的查询计划,其中包括一个获胜的查询计划 winningPlan 字段,和一组失败的查询计划 rejectedPlans 字段。

executionStats 字段包含了描述获胜查询计划所执行的统计信息。

  • isMultiKey: 是否使用了多键索引
  • nReturned: 返回的文档数量
  • totalDocsExamined: 按照索引指针在磁盘上查找实际文档的次数
  • totalKeysExamined: 使用了索引时是查找过的索引条目数量,全表扫描时是检查过的文档数量
  • stage: 查询阶段,COLLSCAN 表示集合扫描,IXSCAN 表示索引扫描
  • needYield: 为了让写请求顺利进行,本次查询暂停的次数
  • executionTimeMillis: 所有查询计划花费的总毫秒数,不是所选的最优查询计划所耗费的时间
  • indexBounds: 描述了索引是如何被使用的,并给出了索引的遍历范围

优化的一个方向是,通过将 nReturned 和 totalKeysExamined 作比较,两个数值越是接近,表示索引的选择性越高。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

深入学习JavaScript系列(七)——Promise async/await generator

本篇属于本系列第七篇 第一篇&#xff1a;#深入学习JavaScript系列&#xff08;一&#xff09;—— ES6中的JS执行上下文 第二篇&#xff1a;# 深入学习JavaScript系列&#xff08;二&#xff09;——作用域和作用域链 第三篇&#xff1a;# 深入学习JavaScript系列&#xff…

ChatGPT探索系列之二:学习GPT模型系列的发展历程和原理

文章目录前言一、GPT的起源GPT系列二、GPT的原理1. GPT原理&#xff1a;自注意2. GPT原理&#xff1a;位置编码3. GPT原理&#xff1a;Masked Language Modeling4. GPT原理&#xff1a;预训练5. GPT原理&#xff1a;微调6. GPT原理&#xff1a;多任务学习三、GPT模型的风险与挑…

二叉搜索树BST的学习

文章目录二叉搜索树BST什么是BST&#xff1f;用BST做什么&#xff1f;一、BST的特性BST的特性是什么&#xff1f;1.[230. 二叉搜索树中第K小的元素](https://leetcode.cn/problems/kth-smallest-element-in-a-bst/)2.[538. 把二叉搜索树转换为累加树](https://leetcode.cn/prob…

Git Commit Message 应该怎么写?

原文链接&#xff1a; Git Commit Message 应该怎么写&#xff1f; 最近被同事吐槽了&#xff0c;说我代码提交说明写的太差。其实都不用他吐槽&#xff0c;我自己心里也非常清楚。毕竟很多时候犯懒&#xff0c;都是直接一个 -m "fix" 就提交上去了。 这样做是非常…

C语言变量

C 变量 变量其实只不过是程序可操作的存储区的名称。C 中每个变量都有特定的类型&#xff0c;类型决定了变量存储的大小和布局&#xff0c;该范围内的值都可以存储在内存中&#xff0c;运算符可应用于变量上。 变量的名称可以由字母、数字和下划线字符组成。它必须以字母或下…

机器学习-卷积神经网络CNN中的单通道和多通道图片差异

背景 最近在使用CNN的场景中&#xff0c;既有单通道的图片输入需求&#xff0c;也有多通道的图片输入需求&#xff0c;因此又整理回顾了一下单通道或者多通道卷积的差别&#xff0c;这里记录一下探索过程。 结论 直接给出结论&#xff0c;单通道图片和多通道图片在经历了第一…

【hello C语言】结构体(下)

目录 1.结构体的声明 1.1 结构的声明 1.2 特殊声明&#xff1a;匿名结构体 1.3 结构的自引用 2. 结构体的定义和初始化 3. 结构体的内存对齐 3.1 内存对齐规则 3.2 内存对齐存在的原因 3.3 修改默认对其数 4. 结构体传参 C语言&#x1f6f4; 1.结构体的声明 结构体便是描述复杂…

一种适合容器化部署的雪花算法ID生成器

雪花算法简介 SnowFlake 中文意思为雪花&#xff0c;故称为雪花算法。最早是 Twitter 公司在其内部用于分布式环境下生成唯一 ID。 雪花算法有以下几个优点&#xff1a; 高并发分布式环境下生成不重复 id&#xff0c;每秒可生成百万个不重复 id。基于时间戳&#xff0c;以及同…

零编程经验,通过 GPT-4 十分钟开发了一个浏览器插件,并成功运行,实现了需求目标!

大佬蓝鸟ID: sundyme零编程经验&#xff0c;通过 GPT-4 十分钟开发了一个浏览器插件&#xff0c;并成功运行&#xff0c;实现了需求目标&#xff01;太不可思意了&#xff0c;真正体会到了自然语言编程的魅力&#xff01; 下一步是利用Pinterest 的 API 接口实现自动发图&#…

No.026<软考>《(高项)备考大全》【第10章】项目沟通和干系人管理(第2部分-干系人管理)

1 干系人管理部分相关 1.1 干系人ITO 1.2 干系人管理 过程过程的定义过程的作用识别干系人识别能影响项目决策、活动或结果的个人、群体或组织&#xff0c;以及被项目决策、活动或者结果影响的个人、群体或者组织&#xff0c;并分析和记录他们的相关信息的过程帮助项目经理建…

此战成硕,我成功上岸西南交通大学了~~~

友友们&#xff0c;好久不见&#xff0c;很长时间没有更一个正式点的文章了&#xff01; 是因为我在去年年底忙着准备初试&#xff0c;今年年初在准备复试&#xff0c;直到3月底拟录取后&#xff0c;终于可以写下这篇上岸贴&#xff0c;和大家分享一下考研至上岸的一个过程 文章…

游戏算法-游戏AI行为树,python实现

参考文章&#xff1a;Behavior trees for AI: How they work (gamedeveloper.com) 本文主要参考上述weizProject Zomboid 的开发者 Chris Simpson文章的概念&#xff0c;用伪代码实现代码例子 AI概述 游戏AI是对游戏内所有非玩家控制角色的行为进行研究和设计&#xff0c;使得游…

电子拣货标签9代系统简介

CK_Label_v9一、产品参数 产品型号 CK_Label_v9 LED 3&#xff08;红&黄&绿&#xff09;独立可控 供电方式 DC 24V 0.2A 通信方式 无线通信 蜂鸣器 支持 尺寸 D60 H307mm 二、革新点 配合标签拣货使用三个灯&#xff08;红黄绿&#xff09;都可以被独立控…

基于MATALB编程的BP神经网络手臂血管分类识别,基于BP神经网络的图像分类

目标 背影 BP神经网络的原理 BP神经网络的定义 BP神经网络的基本结构 BP神经网络的神经元 BP神经网络的激活函数&#xff0c; BP神经网络的传递函数 数据 神经网络参数 基于BP神经网络手臂血管识别的MATLAB代码 效果图 结果分析 展望 背影 随着人工智能的发展&#xff0c;智…

贪心算法-删数问题C++

目录 一、题目 二&#xff1a;思路 代码 运行结果 一、题目 有一个长度为n&#xff08;n < 240&#xff09;的正整数&#xff0c;从中取出k&#xff08;k < n&#xff09;个数&#xff0c;使剩余的数保持原来的次序不变&#xff0c;求这个正整数经过删数之后最小是多…

用Python绘制六种可视化图表,简直太好用了

前言 嗨喽~大家好呀&#xff0c;这里是魔王呐 ❤ ~! python资料、源码、教程: 点击此处跳转文末名片获取 可视化图表&#xff0c;有相当多种&#xff0c;但常见的也就下面几种&#xff0c;其他比较复杂一点&#xff0c;大都也是基于如下几种进行组合&#xff0c;变换出来的。 …

记录--CSS 如何实现羽化效果?

这里给大家分享我在网上总结出来的一些知识&#xff0c;希望对大家有所帮助 最近碰到这样一个问题&#xff0c;在一张封面上直接显示书名&#xff0c;可能会存在书名看不太清楚的情况(容易受到背景干扰)&#xff0c;如下 为了解决这个问题&#xff0c;设计师提了一个“究极”方…

算法 - 希尔排序

原理 首先将数组两两分组&#xff0c;分成n组数组&#xff0c;每组数组内部进行排序。再分成n/2组数组&#xff0c;每组数组内部进行排序。直至分成只剩一组&#xff0c;最后进行排序得到最后的数组。 代码 public static int[] shell(int[] arr) {int temp;for (int gra …

计算机图形学13:三维图形的几何变换

作者&#xff1a;非妃是公主 专栏&#xff1a;《计算机图形学》 博客地址&#xff1a;https://blog.csdn.net/myf_666 个性签&#xff1a;顺境不惰&#xff0c;逆境不馁&#xff0c;以心制境&#xff0c;万事可成。——曾国藩 文章目录专栏推荐专栏系列文章序一、三维图形的几…

MongoDB综述【入门指南】

写这篇博客,正好是2023年4月5日15:29:31,是清明节,放假一天,我坐在我的小小租房室内,思考着没思考到啥,哈哈哈,感觉好着急啊!看完了一本《城南旧事》,但是就是不踏实,好吧~我来写一篇最近在学的一个技术 为了更优秀的自己~奥利给!! 首先,我们从最初级小白开始(因为自己也是小白…