本文曾经收录进 JavaGuide(「Java学习+面试指南」一份涵盖大部分 Java 程序员所须要把握的外围常识。)

少部分内容参考了 MongoDB 官网文档的形容,在此阐明一下。

MongoDB 根底

MongoDB 是什么?

MongoDB 是一个基于 分布式文件存储 的开源 NoSQL 数据库系统,由 C++ 编写的。MongoDB 提供了 面向文档 的存储形式,操作起来比较简单和容易,反对“无模式”的数据建模,能够存储比较复杂的数据类型,是一款十分风行的 文档类型数据库

在高负载的状况下,MongoDB 人造反对程度扩大和高可用,能够很不便地增加更多的节点/实例,以保障服务性能和可用性。在许多场景下,MongoDB 能够用于代替传统的关系型数据库或键/值存储形式,皆在为 Web 利用提供可扩大的高可用高性能数据存储解决方案。

MongoDB 的存储构造是什么?

MongoDB 的存储构造区别于传统的关系型数据库,次要由如下三个单元组成:

  • 文档(Document) :MongoDB 中最根本的单元,由 BSON 键值对(key-value)组成,相似于关系型数据库中的行(Row)。
  • 汇合(Collection) :一个汇合能够蕴含多个文档,相似于关系型数据库中的表(Table)。
  • 数据库(Database) :一个数据库中能够蕴含多个汇合,能够在 MongoDB 中创立多个数据库,相似于关系型数据库中的数据库(Database)。

也就是说,MongoDB 将数据记录存储为文档 (更具体来说是BSON 文档),这些文档在汇合中汇集在一起,数据库中存储一个或多个文档汇合。

SQL 与 MongoDB 常见术语比照

SQLMongoDB
表(Table)汇合(Collection)
行(Row)文档(Document)
列(Col)字段(Field)
主键(Primary Key)对象 ID(Objectid)
索引(Index)索引(Index)
嵌套表(Embeded Table)嵌入式文档(Embeded Document)
数组(Array)数组(Array)

文档

MongoDB 中的记录就是一个 BSON 文档,它是由键值对组成的数据结构,相似于 JSON 对象,是 MongoDB 中的根本数据单元。字段的值可能包含其余文档、数组和文档数组。

文档的键是字符串。除了多数例外情况,键能够应用任意 UTF-8 字符。

  • 键不能含有 \0(空字符)。这个字符用来示意键的结尾。
  • .$ 有特地的意义,只有在特定环境下能力应用。
  • 以下划线_结尾的键是保留的(不是严格要求的)。

BSON [bee·sahn] 是 Binary JSON的简称,是 JSON 文档的二进制示意,反对将文档和数组嵌入到其余文档和数组中,还蕴含容许示意不属于 JSON 标准的数据类型的扩大。无关 BSON 标准的内容,能够参考 bsonspec.org,另见BSON 类型。

依据维基百科对 BJSON 的介绍,BJSON 的遍历速度优于 JSON,这也是 MongoDB 抉择 BSON 的次要起因,但 BJSON 须要更多的存储空间。

与 JSON 相比,BSON 着眼于进步存储和扫描效率。BSON 文档中的大型元素以长度字段为前缀以便于扫描。在某些状况下,因为长度前缀和显式数组索引的存在,BSON 应用的空间会多于 JSON。

汇合

MongoDB 汇合存在于数据库中,没有固定的构造,也就是 无模式 的,这意味着能够往汇合插入不同格局和类型的数据。不过,通常状况相爱插入汇合中的数据都会有肯定的关联性。

汇合不须要当时创立,当第一个文档插入或者第一个索引创立时,如果该汇合不存在,则会创立一个新的汇合。

汇合名能够是满足下列条件的任意 UTF-8 字符串:

  • 汇合名不能是空字符串""
  • 汇合名不能含有 \0 (空字符),这个字符示意汇合名的结尾。
  • 汇合名不能以"system."结尾,这是为零碎汇合保留的前缀。例如 system.users 这个汇合保留着数据库的用户信息,system.namespaces 汇合保留着所有数据库汇合的信息。
  • 汇合名必须以下划线或者字母符号开始,并且不能蕴含 $

数据库

数据库用于存储所有汇合,而汇合又用于存储所有文档。一个 MongoDB 中能够创立多个数据库,每一个数据库都有本人的汇合和权限。

MongoDB 预留了几个非凡的数据库。

  • admin : admin 数据库次要是保留 root 用户和角色。例如,system.users 表存储用户,system.roles 表存储角色。个别不倡议用户间接操作这个数据库。将一个用户增加到这个数据库,且使它领有 admin 库上的名为 dbAdminAnyDatabase 的角色权限,这个用户主动继承所有数据库的权限。一些特定的服务器端命令也只能从这个数据库运行,比方敞开服务器。
  • local : local 数据库是不会被复制到其余分片的,因而能够用来存储本地单台服务器的任意 collection。个别不倡议用户间接应用 local 库存储任何数据,也不倡议进行 CRUD 操作,因为数据无奈被失常备份与复原。
  • config : 当 MongoDB 应用分片设置时,config 数据库可用来保留分片的相干信息。
  • test : 默认创立的测试库,连贯 mongod 服务时,如果不指定连贯的具体数据库,默认就会连贯到 test 数据库。

数据库名能够是满足以下条件的任意 UTF-8 字符串:

  • 不能是空字符串""
  • 不得含有' '(空格)、.$/\\0 (空字符)。
  • 应全副小写。
  • 最多 64 字节。

数据库名最终会变成文件系统里的文件,这也就是有如此多限度的起因。

MongoDB 有什么特点?

  • 数据记录被存储为文档 :MongoDB 中的记录就是一个 BSON 文档,它是由键值对组成的数据结构,相似于 JSON 对象,是 MongoDB 中的根本数据单元。
  • 模式自在 :汇合的概念相似 MySQL 里的表,但它不须要定义任何模式,可能用更少的数据对象体现简单的畛域模型对象。
  • 反对多种查问形式 :MongoDB 查问 API 反对读写操作 (CRUD)以及数据聚合、文本搜寻和天文空间查问。
  • 反对 ACID 事务 :NoSQL 数据库通常不反对事务,为了可扩大和高性能进行了衡量。不过,也有例外,MongoDB 就反对事务。与关系型数据库一样,MongoDB 事务同样具备 ACID 个性。MongoDB 单文档原生反对原子性,也具备事务的个性。MongoDB 4.0 退出了对多文档事务的反对,但只反对复制集部署模式下的事务,也就是说事务的作用域限度为一个正本集内。MongoDB 4.2 引入了分布式事务,减少了对分片集群上多文档事务的反对,并合并了对正本集上多文档事务的现有反对。
  • 高效的二进制存储 :存储在汇合中的文档,是以键值对的模式存在的。键用于惟一标识一个文档,个别是 ObjectId 类型,值是以 BSON 模式存在的。BSON = Binary JSON, 是在 JSON 根底上加了一些类型及元数据形容的格局。
  • 自带数据压缩性能 :存储同样的数据所需的资源更少。
  • 反对 mapreduce :通过分治的形式实现简单的聚合工作。不过,从 MongoDB 5.0 开始,map-reduce 曾经不被官网举荐应用了,代替计划是 聚合管道。聚合管道提供比 map-reduce 更好的性能和可用性。
  • 反对多种类型的索引 :MongoDB 反对多种类型的索引,包含单字段索引、复合索引、多键索引、哈希索引、文本索引、 地理位置索引等,每种类型的索引有不同的应用场合。
  • 反对 failover :提供主动故障复原的性能,主节点产生故障时,主动从从节点中选举出一个新的主节点,确保集群的失常应用,这对于客户端来说是无感知的。
  • 反对分片集群 :MongoDB 反对集群主动切分数据,让集群存储更多的数据,具备更强的性能。在数据插入和更新时,可能主动路由和存储。
  • 反对存储大文件 :MongoDB 的单文档存储空间要求不超过 16MB。对于超过 16MB 的大文件,MongoDB 提供了 GridFS 来进行存储,通过 GridFS,能够将大型数据进行分块解决,而后将这些切分后的小文档保留在数据库中。

MongoDB 适宜什么利用场景?

MongoDB 的劣势在于其数据模型和存储引擎的灵活性、架构的可扩展性以及对弱小的索引反对。

选用 MongoDB 应该充分考虑 MongoDB 的劣势,结合实际我的项目的需要来决定:

  • 随着我的项目的倒退,应用类 JSON 格局(BSON)保留数据是否满足我的项目需要?MongoDB 中的记录就是一个 BSON 文档,它是由键值对组成的数据结构,相似于 JSON 对象,是 MongoDB 中的根本数据单元。
  • 是否须要大数据量的存储?是否须要疾速程度扩大?MongoDB 反对分片集群,能够很不便地增加更多的节点(实例),让集群存储更多的数据,具备更强的性能。
  • 是否须要更多类型索引来满足更多利用场景?MongoDB 反对多种类型的索引,包含单字段索引、复合索引、多键索引、哈希索引、文本索引、 地理位置索引等,每种类型的索引有不同的应用场合。
  • ......

MongoDB 存储引擎

MongoDB 反对哪些存储引擎?

存储引擎(Storage Engine)是数据库的外围组件,负责管理数据在内存和磁盘中的存储形式。

与 MySQL 一样,MongoDB 采纳的也是 插件式的存储引擎架构 ,反对不同类型的存储引擎,不同的存储引擎解决不同场景的问题。在创立数据库或汇合时,能够指定存储引擎。

插件式的存储引擎架构能够实现 Server 层和存储引擎层的解耦,能够反对多种存储引擎,如MySQL既能够反对B-Tree构造的InnoDB存储引擎,还能够反对LSM构造的RocksDB存储引擎。

在存储引擎刚进去的时候,默认是应用 MMAPV1 存储引擎,MongoDB4.x 版本不再反对 MMAPv1 存储引擎。

当初次要有上面这两种存储引擎:

  • WiredTiger 存储引擎 :自 MongoDB 3.2 当前,默认的存储引擎为 WiredTiger 存储引擎 。非常适合大多数工作负载,倡议用于新部署。WiredTiger 提供文档级并发模型、检查点和数据压缩(后文会介绍到)等性能。
  • In-Memory 存储引擎 :In-Memory 存储引擎在 MongoDB Enterprise 中可用。它不是将文档存储在磁盘上,而是将它们保留在内存中以取得更可预测的数据提早。

此外,MongoDB 3.0 提供了 可插拔的存储引擎 API ,容许第三方为 MongoDB 开发存储引擎,这点和 MySQL 也比拟相似。

WiredTiger 基于 LSM Tree 还是 B+ Tree?

目前绝大部分风行的数据库存储引擎都是基于 B/B+ Tree 或者 LSM(Log Structured Merge) Tree 来实现的。对于 NoSQL 数据库来说,绝大部分(比方 HBase、Cassandra、RocksDB)都是基于 LSM 树,MongoDB 不太一样。

下面也说了,自 MongoDB 3.2 当前,默认的存储引擎为WiredTiger 存储引擎。在 WiredTiger 引擎官网上,咱们发现 WiredTiger 应用的是 B+ 树作为其存储构造:

WiredTiger maintains a table's data in memory using a data structure called a B-Tree ( B+ Tree to be specific), referring to the nodes of a B-Tree as pages. Internal pages carry only keys. The leaf pages store both keys and values.

此外,WiredTiger 还反对 LSM(Log Structured Merge) 树作为存储构造,MongoDB 在应用WiredTiger 作为存储引擎时,默认应用的是 B+ 树。

如果想要理解 MongoDB 应用 B 树的起因,能够看看这篇文章:为什么 MongoDB 应用 B 树?。

应用 B+ 树时,WiredTiger 以 page 为根本单位往磁盘读写数据。B+ 树的每个节点为一个 page,共有三种类型的 page:

  • root page(根节点) : B+ 树的根节点。
  • internal page(外部节点) :不理论存储数据的两头索引节点。
  • leaf page(叶子节点):真正存储数据的叶子节点,蕴含一个页头(page header)、块头(block header)和真正的数据(key/value),其中页头定义了页的类型、页中理论载荷数据的大小、页中记录条数等信息;块头定义了此页的checksum、块在磁盘上的寻址地位等信息。

其整体构造如下图所示:

如果想要深入研究学习 WiredTiger 存储引擎,举荐浏览 MongoDB 中文社区的 WiredTiger存储引擎系列。

MongoDB 聚合

MongoDB 聚合有什么用?

理论我的项目中,咱们常常须要将多个文档甚至是多个汇合汇总到一起计算剖析(比方求和、取最大值)并返回计算后的后果,这个过程被称为 聚合操作

依据官网文档介绍,咱们能够应用聚合操作来:

  • 将来自多个文档的值组合在一起。
  • 对汇合中的数据进行的一系列运算。
  • 剖析数据随工夫的变动。

MongoDB 提供了哪几种执行聚合的办法?

MongoDB 提供了两种执行聚合的办法:

  • 聚合管道(Aggregation Pipeline) :执行聚合操作的首选办法。
  • 繁多目标聚合办法(Single purpose aggregation methods) :也就是繁多作用的聚合函数比方 count()distinct()estimatedDocumentCount()

绝大部分文章中还提到了 map-reduce 这种聚合办法。不过,从 MongoDB 5.0 开始,map-reduce 曾经不被官网举荐应用了,代替计划是 聚合管道。聚合管道提供比 map-reduce 更好的性能和可用性。

MongoDB 聚合管道由多个阶段组成,每个阶段在文档通过管道时转换文档。每个阶段接管前一个阶段的输入,进一步解决数据,并将其作为输出数据发送到下一个阶段。

每个管道的工作流程是:

  1. 承受一系列原始数据文档
  2. 对这些文档进行一系列运算
  3. 后果文档输入给下一个阶段

罕用阶段操作符

操作符简述
$match匹配操作符,用于对文档汇合进行筛选
$project投射操作符,用于重构每一个文档的字段,能够提取字段,重命名字段,甚至能够对原有字段进行操作后新增字段
$sort排序操作符,用于依据一个或多个字段对文档进行排序
$limit限度操作符,用于限度返回文档的数量
$skip跳过操作符,用于跳过指定数量的文档
$count统计操作符,用于统计文档的数量
$group分组操作符,用于对文档汇合进行分组
$unwind拆分操作符,用于将数组中的每一个值拆分为独自的文档
$lookup连贯操作符,用于连贯同一个数据库中另一个汇合,并获取指定的文档,相似于 populate

更多操作符介绍详见官网文档:https://docs.mongodb.com/manu...

阶段操作符用于 db.collection.aggregate 办法外面,数组参数中的第一层。

db.collection.aggregate( [ { 阶段操作符:表述 }, { 阶段操作符:表述 }, ... ] )

上面是 MongoDB 官网文档中的一个例子:

db.orders.aggregate([   # 第一阶段:$match阶段按status字段过滤文档,并将status等于"A"的文档传递到下一阶段。    { $match: { status: "A" } },  # 第二阶段:$group阶段按cust_id字段将文档分组,以计算每个cust_id惟一值的金额总和。    { $group: { _id: "$cust_id", total: { $sum: "$amount" } } }])

MongoDB 事务

MongoDB 事务想要搞懂原理还是比拟破费工夫的,我本人也没有搞太明确。因而,我这里只是简略介绍一下 MongoDB 事务,想要理解原理的小伙伴,能够自行搜寻查阅相干材料。

这里举荐几篇文章,供大家参考:

  • 技术干货| MongoDB 事务原理
  • MongoDB 一致性模型设计与实现
  • MongoDB 官网文档对事务的介绍

咱们在介绍 NoSQL 数据的时候也说过,NoSQL 数据库通常不反对事务,为了可扩大和高性能进行了衡量。不过,也有例外,MongoDB 就反对事务。

与关系型数据库一样,MongoDB 事务同样具备 ACID 个性:

  • 原子性Atomicity) : 事务是最小的执行单位,不容许宰割。事务的原子性确保动作要么全副实现,要么齐全不起作用;
  • 一致性Consistency): 执行事务前后,数据保持一致,例如转账业务中,无论事务是否胜利,转账者和收款人的总额应该是不变的;
  • 隔离性Isolation): 并发拜访数据库时,一个用户的事务不被其余事务所烦扰,各并发事务之间数据库是独立的。WiredTiger 存储引擎反对读未提交( read-uncommitted )、读已提交( read-committed )和快照( snapshot )隔离,MongoDB 启动时默认选快照隔离。在不同隔离级别下,一个事务的生命周期内,可能呈现脏读、不可反复读、幻读等景象。
  • 持久性Durability): 一个事务被提交之后。它对数据库中数据的扭转是长久的,即便数据库产生故障也不应该对其有任何影响。

对于事务的具体介绍这篇文章就不多说了,感兴趣的能够看看我写的MySQL常见面试题总结这篇文章,外面有具体介绍到。

MongoDB 单文档原生反对原子性,也具备事务的个性。当议论 MongoDB 事务的时候,通常指的是 多文档 。MongoDB 4.0 退出了对多文档 ACID 事务的反对,但只反对复制集部署模式下的 ACID 事务,也就是说事务的作用域限度为一个正本集内。MongoDB 4.2 引入了 分布式事务 ,减少了对分片集群上多文档事务的反对,并合并了对正本集上多文档事务的现有反对。

依据官网文档介绍:

从 MongoDB 4.2 开始,分布式事务和多文档事务在 MongoDB 中是一个意思。分布式事务是指分片集群和正本集上的多文档事务。从 MongoDB 4.2 开始,多文档事务(无论是在分片集群还是正本集上)也称为分布式事务。

在大多数状况下,多文档事务比单文档写入会产生更大的性能老本。对于大部分场景来说, 非规范化数据模型(嵌入式文档和数组) 仍然是最佳抉择。也就是说,适当地对数据进行建模能够最大限度地缩小对多文档事务的需要。

留神

  • 从MongoDB 4.2开始,多文档事务反对正本集和分片集群,其中:主节点应用WiredTiger存储引擎,同时从节点应用WiredTiger存储引擎或In-Memory存储引擎。在MongoDB 4.0中,只有应用WiredTiger存储引擎的正本集反对事务。
  • 在MongoDB 4.2及更早版本中,你无奈在事务中创立汇合。从 MongoDB 4.4 开始,您能够在事务中创立汇合和索引。无关详细信息,请参阅 在事务中创立汇合和索引。

MongoDB 数据压缩

借助 WiredTiger 存储引擎( MongoDB 3.2 后的默认存储引擎),MongoDB 反对对所有汇合和索引进行压缩。压缩以额定的 CPU 为代价最大限度地缩小存储应用。

默认状况下,WiredTiger 应用 Snappy 压缩算法(谷歌开源,旨在实现十分高的速度和正当的压缩,压缩比 3 ~ 5 倍)对所有汇合应用块压缩,对所有索引应用前缀压缩。

除了 Snappy 之外,对于汇合还有上面这些压缩算法:

  • zlib:高度压缩算法,压缩比 5 ~ 7 倍
  • Zstandard(简称 zstd):Facebook 开源的一种疾速无损压缩算法,针对 zlib 级别的实时压缩场景和更好的压缩比,提供更高的压缩率和更低的 CPU 使用率,MongoDB 4.2 开始可用。

WiredTiger 日志也会被压缩,默认应用的也是 Snappy 压缩算法。如果日志记录小于或等于 128 字节,WiredTiger 不会压缩该记录。


title: MongoDB常见面试题总结(下)
category: 数据库
tag:

  • NoSQL
  • MongoDB

MongoDB 索引

MongoDB 索引有什么用?

和关系型数据库相似,MongoDB 中也有索引。索引的目标次要是用来进步查问效率,如果没有索引的话,MongoDB 必须执行 汇合扫描 ,即扫描汇合中的每个文档,以抉择与查问语句匹配的文档。如果查问存在适合的索引,MongoDB 能够应用该索引来限度它必须查看的文档数量。并且,MongoDB 能够应用索引中的排序返回排序后的后果。

尽管索引能够显著缩短查问工夫,然而应用索引、保护索引是有代价的。在执行写入操作时,除了要更新文档之外,还必须更新索引,这必然会影响写入的性能。因而,当有大量写操作而读操作少时,或者不思考读操作的性能时,都不举荐建设索引。

MongoDB 反对哪些类型的索引?

MongoDB 反对多种类型的索引,包含单字段索引、复合索引、多键索引、哈希索引、文本索引、 地理位置索引等,每种类型的索引有不同的应用场合。

  • 单字段索引: 建设在单个字段上的索引,索引创立的排序程序无所谓,MongoDB 能够头/尾开始遍历。
  • 复合索引: 建设在多个字段上的索引,也能够称之为组合索引、联结索引。
  • 多键索引 :MongoDB 的一个字段可能是数组,在对这种字段创立索引时,就是多键索引。MongoDB 会为数组的每个值创立索引。就是说你能够依照数组外面的值做条件来查问,这个时候仍然会走索引。
  • 哈希索引 :按数据的哈希值索引,用在哈希分片集群上。
  • 文本索引: 反对对字符串内容的文本搜寻查问。文本索引能够蕴含任何值为字符串或字符串元素数组的字段。一个汇合只能有一个文本搜寻索引,但该索引能够笼罩多个字段。MongoDB 尽管反对全文索引,然而性能低下,临时不倡议应用。
  • 地理位置索引: 基于经纬度的索引,适宜 2D 和 3D 的地位查问。
  • 惟一索引 :确保索引字段不会存储反复值。如果汇合曾经存在了违反索引的惟一束缚的文档,则后盾创立惟一索引会失败。
  • TTL 索引 :TTL 索引提供了一个过期机制,容许为每一个文档设置一个过期工夫,当一个文档达到预设的过期工夫之后就会被删除。
  • ......

复合索引中字段的程序有影响吗?

复合索引中字段的程序十分重要,例如下图中的复合索引由{userid:1, score:-1}组成,则该复合索引首先依照userid升序排序;而后再每个userid的值内,再依照score降序排序。

在复合索引中,依照何种形式排序,决定了该索引在查问中是否能被利用到。

走复合索引的排序:

db.s2.find().sort({"userid": 1, "score": -1})db.s2.find().sort({"userid": -1, "score": 1})

不走复合索引的排序:

db.s2.find().sort({"userid": 1, "score": 1})db.s2.find().sort({"userid": -1, "score": -1})db.s2.find().sort({"score": 1, "userid": -1})db.s2.find().sort({"score": 1, "userid": 1})db.s2.find().sort({"score": -1, "userid": -1})db.s2.find().sort({"score": -1, "userid": 1})

咱们能够通过 explain 进行剖析:

db.s2.find().sort({"score": -1, "userid": 1}).explain()

复合索引遵循左前缀准则吗?

MongoDB 的复合索引遵循左前缀准则 :领有多个键的索引,能够同时失去所有这些键的前缀组成的索引,但不包含除左前缀之外的其余子集。比如说,有一个相似 {a: 1, b: 1, c: 1, ..., z: 1} 这样的索引,那么实际上也等于有了 {a: 1}{a: 1, b: 1}{a: 1, b: 1, c: 1} 等一系列索引,然而不会有 {b: 1} 这样的非左前缀的索引。

什么是 TTL 索引?

TTL 索引提供了一个过期机制,容许为每一个文档设置一个过期工夫 expireAfterSeconds ,当一个文档达到预设的过期工夫之后就会被删除。TTL 索引除了有 expireAfterSeconds 属性外,和一般索引一样。

数据过期对于某些类型的信息很有用,比方机器生成的事件数据、日志和会话信息,这些信息只须要在数据库中保留无限的工夫。

TTL 索引运行原理

  • MongoDB 会开启一个后盾线程读取该 TTL 索引的值来判断文档是否过期,但不会保障已过期的数据会立马被删除,因后盾线程每 60 秒触发一次删除工作,且如果删除的数据量较大,会存在上一次的删除未实现,而下一次的工作曾经开启的状况,导致过期的数据也会呈现超过了数据保留工夫 60 秒以上的景象。
  • 对于正本集而言,TTL 索引的后盾过程只会在 Primary 节点开启,在从节点会始终处于闲暇状态,从节点的数据删除是由主库删除后产生的 oplog 来做同步。

TTL 索引限度

  • TTL 索引是单字段索引。复合索引不反对 TTL
  • _id字段不反对 TTL 索引。
  • 无奈在下限汇合(Capped Collection)上创立 TTL 索引,因为 MongoDB 无奈从下限汇合中删除文档。
  • 如果某个字段曾经存在非 TTL 索引,那么在该字段上无奈再创立 TTL 索引。

什么是笼罩索引查问?

依据官网文档介绍,笼罩查问是以下的查问:

  • 所有的查问字段是索引的一部分。
  • 后果中返回的所有字段都在同一索引中。
  • 查问中没有字段等于null

因为所有呈现在查问中的字段是索引的一部分, MongoDB 无需在整个数据文档中检索匹配查问条件和返回应用雷同索引的查问后果。因为索引存在于内存中,从索引中获取数据比通过扫描文档读取数据要快得多。

举个例子:咱们有如下 users 汇合:

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

咱们在 users 汇合中创立联结索引,字段为 genderuser_name :

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

当初,该索引会笼罩以下查问:

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

为了让指定的索引笼罩查问,必须显式地指定 _id: 0 来从后果中排除 _id 字段,因为索引不包含 _id 字段。

MongoDB 高可用

复制集群

什么是复制集群?

MongoDB 的复制集群又称为正本集群,是一组保护雷同数据汇合的 mongod 过程。

客户端连贯到整个 Mongodb 复制集群,主节点机负责整个复制集群的写,从节点能够进行读操作,但默认还是主节点负责整个复制集群的读。主节点产生故障时,主动从从节点中选举出一个新的主节点,确保集群的失常应用,这对于客户端来说是无感知的。

通常来说,一个复制集群蕴含 1 个主节点(Primary),多个从节点(Secondary)以及零个或 1 个仲裁节点(Arbiter)。

  • 主节点 :整个集群的写操作入口,接管所有的写操作,并将汇合所有的变动记录到操作日志中,即 oplog。主节点挂掉之后会主动选出新的主节点。
  • 从节点 :从主节点同步数据,在主节点挂掉之后选举新节点。不过,从节点能够配置成 0 优先级,阻止它在选举中成为主节点。
  • 仲裁节点 :这个是为了节约资源或者多机房容灾用,只负责主节点选举时投票不存数据,保障能有节点取得少数赞成票。

下图是一个典型的三成员正本集群:

主节点与备节点之间是通过 oplog(操作日志) 来同步数据的。oplog 是 local 库下的一个非凡的 下限汇合(Capped Collection) ,用来保留写操作所产生的增量日志,相似于 MySQL 中 的 Binlog。

下限汇合相似于定长的循环队列,数据程序追加到汇合的尾部,当汇合空间达到下限时,它会笼罩汇合中最旧的文档。下限汇合的数据将会被程序写入到磁盘的固定空间内,所以,I/O 速度十分快,如果不建设索引,性能更好。

当主节点上的一个写操作实现后,会向 oplog 汇合写入一条对应的日志,而从节点则通过这个 oplog 一直拉取到新的日志,在本地进行回放以达到数据同步的目标。

正本集最多有一个主节点。 如果以后主节点不可用,一个选举会抉择出新的主节点。MongoDB 的节点选举规定可能保障在 Primary 挂掉之后选取的新节点肯定是集群中数据最全的一个。

为什么要用复制集群?

  • 实现 failover :提供主动故障复原的性能,主节点产生故障时,主动从从节点中选举出一个新的主节点,确保集群的失常应用,这对于客户端来说是无感知的。
  • 实现读写拆散 :咱们能够设置从节点上能够读取数据,主节点负责写入数据,这样的话就实现了读写拆散,加重了主节点读写压力过大的问题。MongoDB 4.0 之前版本如果主库压力不大,不倡议读写拆散,因为写会阻塞读,除非业务对响应工夫不是十分关注以及读取历史数据承受肯定时间延迟。

分片集群

什么是分片集群?

分片集群是 MongoDB 的分布式版本,相较正本集,分片集群数据被平衡的散布在不同分片中, 不仅大幅晋升了整个集群的数据容量下限,也将读写的压力扩散到不同分片,以解决正本集性能瓶颈的难题。

MongoDB 的分片集群由如下三个局部组成(下图来源于官网文档对分片集群的介绍):

  • Config Servers:配置服务器,实质上是一个 MongoDB 的正本集,负责存储集群的各种元数据和配置,如分片地址、Chunks 等
  • Mongos:路由服务,不存具体数据,从 Config 获取集群配置讲申请转发到特定的分片,并且整合分片后果返回给客户端。
  • Shard:每个分片是整体数据的一部分子集,从MongoDB3.6版本开始,每个Shard必须部署为正本集(replica set)架构

为什么要用分片集群?

随着零碎数据量以及吞吐量的增长,常见的解决办法有两种:垂直扩大和程度扩大。

垂直扩大通过减少单个服务器的能力来实现,比方磁盘空间、内存容量、CPU 数量等;程度扩大则通过将数据存储到多个服务器上来实现,依据须要增加额定的服务器以减少容量。

相似于 Redis Cluster,MongoDB 也能够通过分片实现 程度扩大 。程度扩大这种形式更灵便,能够满足更大数据量的存储需要,反对更高吞吐量。并且,程度扩大所需的整体老本更低,仅仅须要绝对较低配置的单机服务器即可,代价是减少了部署的基础设施和保护的复杂性。

也就是说当你遇到如下问题时,能够应用分片集群解决:

  • 存储容量受单机限度,即磁盘资源遭逢瓶颈。
  • 读写能力受单机限度,可能是 CPU、内存或者网卡等资源遭逢瓶颈,导致读写能力无奈扩大。

什么是分片键?

分片键(Shard Key) 是数据分区的前提, 从而实现数据散发到不同服务器上,加重服务器的累赘。也就是说,分片键决定了汇合内的文档如何在集群的多个分片间的散布情况。

分片键就是文档外面的一个字段,然而这个字段不是一般的字段,有肯定的要求:

  • 它必须在所有文档中都呈现。
  • 它必须是汇合的一个索引,能够是单索引或复合索引的前缀索引,不能是多索引、文本索引或天文空间地位索引。
  • MongoDB 4.2 之前的版本,文档的分片键字段值不可变。MongoDB 4.2 版本开始,除非分片键字段是不可变的 _id 字段,否则您能够更新文档的分片键值。MongoDB 5.0 版本开始,实现了实时从新分片(live resharding),能够实现分片键的齐全从新抉择。
  • 它的大小不能超过 512 字节。

如何抉择分片键?

抉择适合的片键对 sharding 效率影响很大,次要基于如下四个因素(摘自分片集群应用注意事项 - - 腾讯云文档):

  • 取值基数 取值基数倡议尽可能大,如果用小基数的片键,因为备选值无限,那么块的总数量就无限,随着数据增多,块的大小会越来越大,导致程度扩大时挪动块会十分艰难。 例如:抉择年龄做一个基数,范畴最多只有100个,随着数据量增多,同一个值散布过多时,导致 chunck 的增长超出 chuncksize 的范畴,引起 jumbo chunk,从而无奈迁徙,导致数据分布不平均,性能瓶颈。
  • 取值散布 取值散布倡议尽量平均,散布不平均的片键会造成某些块的数据量十分大,同样有下面数据分布不平均,性能瓶颈的问题。
  • 查问带分片 查问时倡议带上分片,应用分片键进行条件查问时,mongos 能够间接定位到具体分片,否则 mongos 须要将查问散发到所有分片,再期待响应返回。
  • 防止枯燥递增或递加 枯燥递增的 sharding key,数据文件移动小,但写入会集中,导致最初一篇的数据量继续增大,一直产生迁徙,递加同理。

综上,在抉择片键时要思考以上4个条件,尽可能满足更多的条件,能力升高 MoveChuncks 对性能的影响,从而取得最优的性能体验。

分片策略有哪些?

MongoDB 反对两种分片算法来满足不同的查问需要(摘自MongoDB 分片集群介绍 - 阿里云文档):

1、基于范畴的分片

MongoDB 依照分片键(Shard Key)的值的范畴将数据拆分为不同的块(Chunk),每个块蕴含了一段范畴内的数据。当分片键的基数大、频率低且值非枯燥变更时,范畴分片更高效。

  • 长处: Mongos 能够疾速定位申请须要的数据,并将申请转发到相应的 Shard 节点中。
  • 毛病: 可能导致数据在 Shard 节点上散布不平衡,容易造成读写热点,且不具备写分散性。
  • 实用场景:分片键的值不是枯燥递增或枯燥递加、分片键的值基数大且反复的频率低、须要范畴查问等业务场景。

2、基于 Hash 值的分片

MongoDB 计算单个字段的哈希值作为索引值,并以哈希值的范畴将数据拆分为不同的块(Chunk)。

  • 长处:能够将数据更加平衡地散布在各 Shard 节点中,具备写分散性。
  • 毛病:不适宜进行范畴查问,进行范畴查问时,须要将读申请散发到所有的 Shard 节点。
  • 实用场景:分片键的值存在枯燥递增或递加、片键的值基数大且反复的频率低、须要写入的数据随机散发、数据读取随机性较大等业务场景。

除了上述两种分片策略,您还能够配置 复合片键 ,例如由一个低基数的键和一个枯燥递增的键组成。

分片数据如何存储?

Chunk(块) 是 MongoDB 分片集群的一个外围概念,其本质上就是由一组 Document 组成的逻辑数据单元。每个 Chunk 蕴含肯定范畴片键的数据,互不相交且并集为全副数据,即离散数学中划分的概念。

分片集群不会记录每条数据在哪个分片上,而是记录 Chunk 在哪个分片上一级这个 Chunk 蕴含哪些数据。

默认状况下,一个 Chunk 的最大值默认为 64MB(可调整,取值范畴为 1~1024 MB。如无非凡需要,倡议放弃默认值),进行数据插入、更新、删除时,如果此时 Mongos 感知到了指标 Chunk 的大小或者其中的数据量超过下限,则会触发 Chunk **。

数据的增长会让 Chunk 得越来越多。这个时候,各个分片上的 Chunk 数量可能会不均衡。Mongos 中的 均衡器(Balancer) 组件就会执行主动均衡,尝试使各个 Shard 上 Chunk 的数量放弃平衡,这个过程就是 再均衡(Rebalance)**。默认状况下,数据库和汇合的 Rebalance 是开启的。

如下图所示,随着数据插入,导致 Chunk **,让 AB 两个分片有 3 个 Chunk,C 分片只有一个,这个时候就会把 B 调配的迁徙一个到 C 分片实现集群数据平衡。

Balancer 是 MongoDB 的一个运行在 Config Server 的 Primary 节点上(自 MongoDB 3.4 版本起)的后盾过程,它监控每个分片上 Chunk 数量,并在某个分片上 Chunk 数量达到阈值进行迁徙。

Chunk 只会决裂,不会合并,即便 chunkSize 的值变大。

Rebalance 操作是比拟消耗系统资源的,咱们能够通过在业务低峰期执行、预分片或者设置 Rebalance 工夫窗等形式来缩小其对 MongoDB 失常应用所带来的影响。

Chunk 迁徙原理是什么?

对于 Chunk 迁徙原理的具体介绍,举荐浏览 MongoDB 中文社区的一文读懂 MongoDB chunk 迁徙这篇文章。

学习材料举荐

  • MongoDB 中文手册|官网文档中文版(举荐):基于 4.2 版本,一直与官网最新版放弃同步。
  • MongoDB 初学者教程——7 天学习 MongoDB:疾速入门。
  • SpringBoot 整合 MongoDB 实战 - 2022 :很不错的一篇 MongoDB 入门文章,次要围绕 MongoDB 的 Java 客户端应用进行根本的增删改查操作介绍。

参考

  • MongoDB 官网文档(次要参考资料,以官网文档为准):https://www.mongodb.com/docs/...
  • 《MongoDB 权威指南》
  • 技术干货| MongoDB 事务原理 - MongoDB 中文社区:https://mongoing.com/archives...
  • Transactions - MongoDB 官网文档:https://www.mongodb.com/docs/...
  • WiredTiger Storage Engine - MongoDB 官网文档:https://www.mongodb.com/docs/...
  • WiredTiger存储引擎之一:根底数据结构剖析:https://mongoing.com/topic/ar...
  • MongoDB 官网文档(次要参考资料,以官网文档为准):https://www.mongodb.com/docs/...
  • 《MongoDB 权威指南》
  • Indexes - MongoDB 官网文档:https://www.mongodb.com/docs/...
  • MongoDB - 索引常识 - 程序员翔仔 - 2022:https://fatedeity.cn/posts/da...
  • MongoDB - 索引: https://www.cnblogs.com/Neeo/...
  • Sharding - MongoDB 官网文档:https://www.mongodb.com/docs/...
  • MongoDB 分片集群介绍 - 阿里云文档:https://help.aliyun.com/docum...
  • 分片集群应用注意事项 - - 腾讯云文档:https://cloud.tencent.com/doc...