1、什么是 MongoDB
MongoDB 是一个基于分布式文件存储的数据库,由 C++ 语言编写,旨在为 WEB 应用提供可扩展的高性能数据存储解决方案。MongoDB 是一个介于关系数据库和非关系数据库之间的产品,是非关系数据库当中功能最丰富,最像关系数据库的。
MongoDB 将数据存储为一个文档,数据结构由键值(key=>value)对组成。MongoDB 文档类似于 JSON 对象。字段值可以包含其他文档,数组及文档数组。
2、MongoDB 概念
2.1、数据库
MongoDB 的单个实例可以容纳多个独立的数据库,每个数据库都有自己的集合和权限。
数据库通过名字来标识;数据库名为 UTF-8 字符串,需满足以下条件:
- 不能是空字符串("")。
- 不得含有 ' '(空格)、.、$、/、\ 和 \0 (空字符)。
- 应全部小写。
- 最多 64 字节。
内置数据库:
- admin:管理数据库。将一个用户添加到这个数据库,则这个用户自动继承所有数据库的权限。一些特定的服务器端命令也只能从这个数据库运行,比如关闭服务器。
- local:这个数据永远不会被复制,可以用来存储限于本地单台服务器的集合。
- config:当 MongoDB 使用分片时,config 数据库用于保存分片的相关信息。
2.2、文档(Document)
文档是一组键值(key-value)对(即 BSON)。MongoDB 的文档不需要设置相同的字段,并且相同的字段不需要相同的数据类型,这与关系型数据库有很大的区别,也是 MongoDB 非常突出的特点。一个简单的文档例子如下:
{"name":"李白", "age":30}
文档有如下特性:
- 文档中的键/值对是有序的。
- 文档中的值不仅可以是在双引号里面的字符串,还可以是其他几种数据类型(甚至可以是整个嵌入的文档)。
- MongoDB 区分类型和大小写。
- MongoDB 的文档不能有重复的键。
- 文档的键是字符串。除了少数例外情况,键可以使用任意 UTF-8 字符。
文档键命名规范:
- 键不能含有\0 (空字符)。这个字符用来表示键的结尾。
- .和$有特别的意义,只有在特定环境下才能使用。
- 以下划线"_"开头的键是保留的(不是严格要求的)。
2.3、集合
集合就是文档组,类似于关系数据库中的表。集合没有固定的结构,可以插入不同格式和类型的数据,但通常情况下我们插入集合的数据都会有一定的关联性。
比如,我们可以将以下不同数据结构的文档插入到同一集合中:
{"name":"杜甫", "age":29} {"name":"李白", "age":30} {"name":"白居易", "age":31, "height":175}
集合名的命名规范:
- 集合名不能是空字符串""。
- 集合名不能含有\0字符(空字符),这个字符表示集合名的结尾。
- 集合名字不能含有保留字符;集合名中不要有$。
Capped collections 是固定大小的集合,当达到最大值时,它会自动覆盖最早的文档。和标准的集合不同,你必须要显式的创建一个 capped collection,指定一个集合的大小,单位是字节。固定集合的数据存储空间值提前分配的。固定集合不能删除一个文档,可以使用 drop() 方法删除整个集合。
2.4、MondoDB 和 RDBMS 的对比
RDBMS 术语/概念 | MongoDB 术语/概念 | 解释/说明 |
---|---|---|
database | database | 数据库 |
table | collection | 数据库表/集合 |
row | document | 数据记录行/文档 |
column | field | 数据字段/域 |
index | index | 索引 |
table joins | $lookup | |
primary key | primary key | 主键,MongoDB 自动将 _id 字段设置为主键 |
transaction | transaction | |
group by | aggregation |
2.5、MondoDB 数据类型
数据类型 | 对应数字标识 | 别名 | 描述 | |
---|---|---|---|---|
Double | 1 | double | 双精度浮点值。 | |
String | 2 | string | 字符串。在 MongoDB 中,UTF-8 编码的字符串才是合法的。 | |
Object | 3 | object | 用于内嵌文档 | |
Array | 4 | array | 数组 | |
Binary data | 5 | binData | 二进制数据 | |
Undefined | 6 | undefined | 已过时 | |
ObjectId | 7 | objectId | 对象 ID | |
Boolean | 8 | bool | 布尔值 | |
Date | 9 | date | 日期 | |
Null | 10 | null | 空值 | |
Regular Expression | 11 | regex | 正则表达式 | |
DBPointer | 12 | dbPointer | 已过时 | |
JavaScript | 13 | javascript | JavaScript 代码 | |
Symbol | 14 | symbol | 符号,已过时 | |
JavaScript code with scope | 15 | javascriptWithScope | 已过时 | |
32-bit integer | 16 | int | 整形 | |
Timestamp | 17 | timestamp | 时间戳 | |
64-bit integer | 18 | long | 长整型 | |
Decimal128 | 19 | decimal | 浮点型,3.4 中新增 | |
Min key | -1 | minKey | 最小 key,主要是内部使用 | |
Max key | 127 | maxKey | 最大 key,主要是内部使用 |
下面介绍几个重要的数据类型。
2.5.1、ObjectId
ObjectId 类似唯一主键,可以很快的生成和排序,包含 12 bytes:
- 前 4 个字节表示 unix 时间戳,格林尼治时间 UTC 时间,比北京时间晚 8 小时
- 接下来的 3 个字节是机器标识码
- 紧接的两个字节由进程 id 组成 PID
- 最后三个字节是随机数
MongoDB 中的文档必须有一个 _id 键,这个键的值可以是任何类型,默认是 ObjectId 对象。由于 ObjectId 中保存了创建的时间戳,所以不需要为文档保存时间戳字段,可以通过 getTimestamp 函数来获取文档的创建时间:
> var newObject = ObjectId() > newObject.getTimestamp() ISODate("2022-01-07T01:45:50Z")
2.5.2、字符串
字符串都是 UTF-8 编码。
2.5.3、时间戳
时间戳类型用于 MongoDB 内部使用,与普通的日期类型不相关;时间戳值是一个 64 位的值:
- 前32位是一个 time_t 值(与 Unix 新纪元相差的秒数)
- 后32位是在某秒中操作的一个递增的序数
在单个 mongod 实例中,时间戳值通常是唯一的。
2.5.4、日期
表示当前距离 Unix 新纪元(1970年1月1日)的毫秒数。日期类型是有符号的,负数表示 1970 年之前的日期。
> var mydate1 = new Date() > mydate1 ISODate("2022-01-07T02:26:33.011Z") > > var mydate2 = ISODate() > mydate2 ISODate("2022-01-07T02:26:47.088Z")
3、MongoDB 架构模式
3.1、单机模式
该模式只安装一个节点,一般用于测试学习。
3.2、Master-Slave 模式
主从架构一般用于备份或者做读写分离。一般有一主一从设计和一主多从设计。由两种角色构成:
主(Master):可读可写,当数据有修改的时候,会将 oplog 同步到所有的 Salve 上去。
从(Slave):只读,从 Master 同步数据。
注:新版的 MongoDB 已经不支持这种模式了。
3.3、Replica Set 模式
Replica Set 提供了数据的冗余备份,在多个服务器上存储数据副本,提高了数据的可用性, 保证了数据的安全性。Replica Set 包含三类角色:
(1)主节点(Primary)
接收所有的写请求,然后把修改同步到所有 Secondary。一个 Replica Set 只能有一个 Primary 节点,当 Primary 挂掉后,其他 Secondary 或者 Arbiter 节点会重新选举一个主节点。默认读请求也发到Primary 节点处理,如果需要发到 Secondary 可修改下客户端的连接配置。
(2)副本节点(Secondary)
与主节点保持同样的数据集。当主节点挂掉的时候,参与选主。
(3)仲裁者(Arbiter)
不保存数据,不参与选主,只进行选主投票。使用 Arbiter 可以减轻数据存储的硬件需求。
Replica Set 中 Secondary 宕机,不受影响,若 Primary 宕机,会重新选主:
3.3、Sharding 模式
3.3.1、分片架构
在 MongoDB 中存在另一种集群,就是分片技术,通过在多台机器上分片存储数据,可以满足 MongoDB 数据量大量增长的需求。分片技术中包含的组件:
A.数据分片(Shards):用来存数据,可以是一个单独的mongod实例,也可以是一个副本集。生产环境下 Shard 一般是一个Replica Set,防止该数据片的单点故障。
B.路由(Routers):前端路由,客户端由此接入,让整个集群看上去像单一数据库,应用可以透明使用。路由为 mongos实例,一个 Sharding 集群,可以有一个 mongos,也可以有多 mongos 以减轻客户端请求的压力。
C.配置服务器(Config Servers):保存集群的元数据信息(路由、分片),它也是一个副本集。
Sharding 分片技术高可用的架构图如下:
3.3.2、分片算法
基于分片切分后的数据块称为 chunk,一个分片后的集合会包含多个 chunk,每个 chunk 位于哪个分片(Shard) 记录在 Config Server(配置服务器)上。Mongos 在操作分片集合时,会自动根据分片键找到对应的 chunk,并向该 chunk 所在的分片发起操作请求。
数据是根据分片策略来进行切分的,分片策略由分片键(ShardKey)+分片算法(ShardStrategy)组成。MongoDB 支持两种分片策略:
- 范围分片
如上图所示,假设集合根据 x 字段来分片,x 的取值范围为[minKey, maxKey],将整个取值范围划分为多个 chunk,每个 chunk(默认配置为64MB)包含其中一小段的数据:如 Chunk1 包含 x 的取值在[minKey, -75)的所有文档,而 Chunk2 包含x取值在 [-75, 25) 之间的所有文档,Chunk3、Chunk4 依次类推。
范围分片能很好的满足范围查询的需求,比如想查询 x 的值在[-30, 10]之间的所有文档,这时 Mongos 直接能将请求路由到 Chunk2,就能查询出所有符合条件的文档。 范围分片的缺点在于,如果 ShardKey 有明显递增(或者递减)趋势,则新插入的文档多会分布到同一个 chunk,无法扩展写的能力,比如使用 _id 作为 ShardKey,而 MongoDB 自动生成的 id 高位是时间戳,是持续递增的。
- 哈希分片
Hash 分片是根据用户的 ShardKey 先计算出 hash 值(64bit整型),再根据 hash 值按照范围分片的策略将文档分布到不同的 chunk。由于 hash 值的计算是随机的,因此 Hash 分片具有很好的离散性,可以将数据随机分发到不同的 chunk 上。 Hash 分片可以充分的扩展写能力,弥补了范围分片的不足,但不能高效的服务范围查询,所有的范围查询要查询多个 chunk 才能找出满足条件的文档。
3.3.3、分片均衡
数据是分布在不同的 chunk上的,而 chunk 则会分配到不同的分片上,那么如何保证分片上的数据(chunk) 是均衡的?有如下两种情况:
A. 全预分配,chunk 的数量和 shard 都是预先定义好的,比如 10 个shard,存储 1000 个 chunk,那么每个 shard 分别拥有100个 chunk。此时集群已经是均衡的状态(这里假定)。
B. 非预分配,这种情况则比较复杂,一般当一个 chunk 太大时会产生分裂(split),不断分裂的结果会导致不均衡;或者动态扩容增加分片时,也会出现不均衡的状态。 这种不均衡的状态由集群均衡器进行检测,一旦发现了不均衡则执行 chunk 数据的搬迁达到均衡。
MongoDB 的数据均衡器运行于 Primary Config Server(配置服务器的主节点)上,而该节点也同时会控制 Chunk 数据的搬迁。
对于数据的不均衡是根据两个分片上的 Chunk 个数差异来判定的,阈值对应表如下:
Number of Chunks | Migration Threshold |
---|---|
Fewer than 20 | 2 |
20-79 | 4 |
80 and greater | 8 |
MongoDB 的数据迁移对集群性能存在一定影响,这点无法避免,目前的规避手段只能是将均衡时间放到业务闲时段。
4、MongoDB 用户管理
在 MongoDB 里面用户是属于数据库的,不同的数据库可以拥有不同的用户。用户通过角色来控制权限,角色也是与数据库关联的;设置角色时需要同时设置对应的数据库。MongoDB 中内置了一些角色:
1.Database User Roles(数据库用户角色)
Read:允许从指定数据库读数据
readWrite:允许从指定数据库读写数据
2.Database Administration Roles(数据库管理角色)
dbAdmin:数据库管理功能
dbOwner: 该角色是 readWrite, dbAdmin 和 userAdmin 三个角色的集合
userAdmin:在当前数据库上创建、修改角色和用户
3.Cluster Administration Roles(集群管理角色)
clusterAdmin:该角色是 clusterManager, clusterMonitor 和 hostManager 三个角色的集合
clusterManager:提供管理和监视的权限
clusterMonitor:提供只读的监视的权限
hostManager:提供监视和管理服务器的权限
4.Backup and Restoration Roles(备份恢复角色)
backup:备份
restore:还原数据
5.All-Database Roles(针对所有数据库的角色,除了 local 和 config 数据库)
readAnyDatabase:从所有数据(除了 local 和 config)读取数据
readWriteAnyDatabase:从所有数据(除了 local 和 config)读写取数据
userAdminAnyDatabase:对所有数据(除了 local 和 config)提供与 userAdmin 一样的权限
dbAdminAnyDatabase:对所有数据(除了 local 和 config)提供与 dbAdmin 一样的权限
6.Superuser Roles(超级角色)
root:拥有任何数据库的任何权限
7.Internal Role
__system:对数据库中的任何对象具有任何操作的权限
创建用户方法如下:
db.createUser({ user: 'test', pwd: '123456', roles:[{ role: 'readWrite', db: 'test' }] })
如需启用权限认证,可在 MongoDB 启动时增加 --auth 参数。
参考:
https://www.runoob.com/mongodb/mongodb-tutorial.html
https://www.cnblogs.com/littleatp/p/11675233.html