1.MongoDB是什么?
MongoDB 是一个基于 分布式文件存储 的开源 NoSQL 数据库系统,由 C++ 编写的。
MongoDB 提供了面向文档 的存储方式,操作起来比较简单和容易,支持“无模式”的数据建模,可以存储比较复杂的数据类型,是一款非常流行的文档类型数据库 。
在高负载的情况下,MongoDB 天然支持水平扩展和高可用,可以很方便地添加更多的节点/实例,以保证服务性能和可用性。
2.MongoDB 的存储结构
- 文档(Document) :MongoDB 中最基本的单元,由 BSON键值对(key-value)组成,类似于关系型数据库中的行(Row)
BSON [bee·sahn] 是 Binary JSON的简称,是 JSON 文档的二进制表示,支持将文档和数组嵌入到其他文档和数组中,还包含允许表示不属于 JSON 规范的数据类型的扩展。 - 集合(Collection) :一个集合可以包含多个文档,类似于关系型数据库中的表(Table)。
- 数据库(Database) :一个数据库中可以包含多个集合,可以在 MongoDB 中创建多个数据库,类似于关系型数据库中的数据库(Database)。
mysql | MongoDB |
---|---|
表(Table) | 集合(Collection) |
行(Row) | 文档(Document) |
列(Col) | 字段(Field) |
主键(Primary Key) | 对象 ID(Objectid) |
索引(Index) | 索引(Index) |
嵌套表(Embeded Table) | 嵌入式文档(Embeded Document)) |
数组(Array) | 数组(Array) |
3.MongoDB 存储引擎
与 MySQL 一样,MongoDB 采用的也是 插件式的存储引擎架构 ,支持不同类型的存储引擎,不同的存储引擎解决不同场景的问题
- WiredTiger 存储引擎 :自 MongoDB 3.2 以后,默认的存储引擎为 WiredTiger 存储引擎 。非常适合大多数工作负载,建议用于新部署。WiredTiger 提供文档级并发模型、检查点和数据压缩(后文会介绍到)等功能。
- In-Memory 存储引擎 :In-Memory 存储引擎在 MongoDB Enterprise 中可用。它不是将文档存储在磁盘上,而是将它们保留在内存中以获得更可预测的数据延迟。
3.1 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 还支持 LSM(Log Structured Merge) 树作为存储结构,MongoDB 在使用WiredTiger 作为存储引擎时,默认使用的是 B+ 树。
使用 B+ 树时,WiredTiger 以 page 为基本单位往磁盘读写数据。B+ 树的每个节点为一个 page,共有三种类型的 page:
- root page(根节点) :B+ 树的根节点。
- internal page(内部节点) :不实际存储数据的中间索引节点。
- leaf page(叶子节点):真正存储数据的叶子节点,包含一个页头(page header)、块头(block header)和真正的数据(key/value),其中页头定义了页的类型、页中实际载荷数据的大小、页中记录条数等信息;块头定义了此页的checksum、块在磁盘上的寻址位置等信息。
4.MongoDB 聚合
4.1 MongoDB 聚合有什么用?
实际项目中,我们经常需要将多个文档甚至是多个集合汇总到一起计算分析(比如求和、取最大值)并返回计算后的结果,这个过程被称为 聚合操作 。
- 将来自多个文档的值组合在一起。
- 对集合中的数据进行的一系列运算。
- 分析数据随时间的变化。
4.2 MongoDB 提供了哪几种执行聚合的方法?
- 聚合管道(Aggregation Pipeline) :执行聚合操作的首选方法。
- 单一目的聚合方法(Single purpose aggregation methods) :也就是单一作用的聚合函数比如 count()、distinct()、estimatedDocumentCount()。
每个管道的工作流程是:
- 接受一系列原始数据文档
- 对这些文档进行一系列运算
- 结果文档输出给下一个阶段
5.MongoDB 事务
MongoDB 单文档原生支持原子性,也具备事务的特性。
MongoDB 事务的时候,通常指的是 多文档 。MongoDB 4.0 加入了对多文档 ACID 事务的支持,但只支持复制集部署模式下的 ACID 事务,也就是说事务的作用域限制为一个副本集内。MongoDB 4.2 引入了 分布式事务 ,增加了对分片集群上多文档事务的支持,并合并了对副本集上多文档事务的现有支持。
在大多数情况下,多文档事务比单文档写入会产生更大的性能成本。对于大部分场景来说, 非规范化数据模型(嵌入式文档和数组) 依然是最佳选择。也就是说,适当地对数据进行建模可以最大限度地减少对多文档事务的需求。
6.MongoDB 数据压缩
借助 WiredTiger 存储引擎( MongoDB 3.2 后的默认存储引擎),MongoDB 支持对所有集合和索引进行压缩。压缩以额外的 CPU 为代价最大限度地减少存储使用。
- 默认情况下,WiredTiger 使用 Snappy 压缩算法(谷歌开源,旨在实现非常高的速度和合理的压缩,压缩比 3 ~ 5 倍)对所有集合使用块压缩,对所有索引使用前缀压缩。
- zlib:高度压缩算法,压缩比 5 ~ 7 倍
- Zstandard(简称 zstd):Facebook 开源的一种快速无损压缩算法,针对 zlib 级别的实时压缩场景和更好的压缩比,提供更高的压缩率和更低的 CPU 使用率,MongoDB 4.2 开始可用。
WiredTiger 日志也会被压缩,默认使用的也是 Snappy 压缩算法。如果日志记录小于或等于 128 字节,WiredTiger 不会压缩该记录。
7.MongoDB 索引
7.1MongoDB 支持哪些类型的索引?
MongoDB 支持多种类型的索引,包括单字段索引、复合索引、多键索引、哈希索引、文本索引、 地理位置索引等,每种类型的索引有不同的使用场合。
- 单字段索引: 建立在单个字段上的索引,索引创建的排序顺序无所谓,MongoDB 可以头/尾开始遍历。
- 复合索引: 建立在多个字段上的索引,也可以称之为组合索引、联合索引。
多键索引 :MongoDB 的一个字段可能是数组,在对这种字段创建索引时,就是多键索引。MongoDB 会为数组的每个值创建索引。就是说你可以按照数组里面的值做条件来查询,这个时候依然会走索引。 - 哈希索引 :按数据的哈希值索引,用在哈希分片集群上。
- 文本索引: 支持对字符串内容的文本搜索查询。文本索引可以包含任何值为字符串或字符串元素数组的字段。一个集合只能有一个文本搜索索引,但该索引可以覆盖多个字段。MongoDB 虽然支持全文索引,但是性能低下,暂时不建议使用。
- 地理位置索引: 基于经纬度的索引,适合 2D 和 3D 的位置查询。
- 唯一索引 :确保索引字段不会存储重复值。如果集合已经存在了违反索引的唯一约束的文档,则后台创建唯一索引会失败。
- TTL 索引 :TTL 索引提供了一个过期机制,允许为每一个文档设置一个过期时间,当一个文档达到预设的过期时间之后就会被删除。
7.2 TTL 索引运行原理
- MongoDB 会开启一个后台线程读取该 TTL 索引的值来判断文档是否过期,但不会保证已过期的数据会立马被删除,因后台线程每 60 秒触发一次删除任务,且如果删除的数据量较大,会存在上一次的删除未完成,而下一次的任务已经开启的情况,导致过期的数据也会出现超过了数据保留时间 60 秒以上的现象。
- 对于副本集而言,TTL 索引的后台进程只会在 Primary 节点开启,在从节点会始终处于空闲状态,从节点的数据删除是由主库删除后产生的 oplog 来做同步。、
TTL 索引限制 :
- TTL 索引是单字段索引。复合索引不支持 TTL
- _id字段不支持 TTL 索引。
- 无法在上限集合(Capped Collection)上创建 TTL 索引,因为 MongoDB 无法从上限集合中删除文档。
- 如果某个字段已经存在非 TTL 索引,那么在该字段上无法再创建 TTL 索引。
8.MongoDB 高可用
MongoDB 的复制集群又称为副本集群,是一组维护相同数据集合的 mongod 进程。
- 主节点 :整个集群的写操作入口,接收所有的写操作,并将集合所有的变化记录到操作日志中,即 oplog。主节点挂掉之后会自动选出新的主节点。
- 从节点 :从主节点同步数据,在主节点挂掉之后选举新节点。不过,从节点可以配置成 0 优先级,阻止它在选举中成为主节点。
- 仲裁节点 :这个是为了节约资源或者多机房容灾用,只负责主节点选举时投票不存数据,保证能有节点获得多数赞成票。
主节点与备节点之间是通过 oplog(操作日志) 来同步数据的。oplog 是 local 库下的一个特殊的 上限集合(Capped Collection) ,用来保存写操作所产生的增量日志,类似于 MySQL 中 的 Binlog。
当主节点上的一个写操作完成后,会向 oplog 集合写入一条对应的日志,而从节点则通过这个 oplog 不断拉取到新的日志,在本地进行回放以达到数据同步的目的。
副本集最多有一个主节点。如果当前主节点不可用,一个选举会抉择出新的主节点。MongoDB 的节点选举规则能够保证在 Primary 挂掉之后选取的新节点一定是集群中数据最全的一个。
8.1 为什么要用复制集群?
- 实现 failover :提供自动故障恢复的功能,主节点发生故障时,自动从从节点中选举出一个新的主节点,确保集群的正常使用,这对于客户端来说是无感知的。
- 实现读写分离 :我们可以设置从节点上可以读取数据,主节点负责写入数据,这样的话就实现了读写分离,减轻了主节点读写压力过大的问题。MongoDB 4.0 之前版本如果主库压力不大,不建议读写分离,因为写会阻塞读,除非业务对响应时间不是非常关注以及读取历史数据接受一定时间延迟。
8.2 分片集群
分片集群是 MongoDB 的分布式版本,相较副本集,分片集群数据被均衡的分布在不同分片中, 不仅大幅提升了整个集群的数据容量上限,也将读写的压力分散到不同分片,以解决副本集性能瓶颈的难题。
- Config Servers:配置服务器,本质上是一个 MongoDB 的副本集,负责存储集群的各种元数据和配置,如分片地址、Chunks 等
- Mongos:路由服务,不存具体数据,从 Config 获取集群配置讲请求转发到特定的分片,并且整合分片结果返回给客户端。
- Shard:每个分片是整体数据的一部分子集,从MongoDB3.6版本开始,每个Shard必须部署为副本集(replica set)架构
8.3什么是分片键?
分片键(Shard Key) 是数据分区的前提, 从而实现数据分发到不同服务器上,减轻服务器的负担。也就是说,分片键决定了集合内的文档如何在集群的多个分片间的分布状况。
分片键就是文档里面的一个字段,但是这个字段不是普通的字段,有一定的要求:
- 它必须在所有文档中都出现。
- 它必须是集合的一个索引,可以是单索引或复合索引的前缀索引,不能是多索引、文本索引或地理空间位置索引。
- MongoDB 4.2 之前的版本,文档的分片键字段值不可变。MongoDB 4.2 版本开始,除非分片键字段是不可变的 _id 字段,否则您可以更新文档的分片键值。MongoDB 5.0 版本开始,实现了实时重新分片(live resharding),可以实现分片键的完全重新选择。
- 它的大小不能超过 512 字节。
参考地址