MongoDB 是一个基于分布式文件存储的数据库,由 C++ 语言编写,旨在为 WEB 应用提供可扩展的高性能数据存储解决方案。MongoDB 是一个介于关系数据库和非关系数据库之间的产品,是非关系数据库当中功能最丰富,最像关系数据库的。
MongoDB 将数据存储为一个文档,数据结构由键值(key=>value)对组成。MongoDB 文档类似于 JSON 对象。字段值可以包含其他文档,数组及文档数组。
MongoDB 的单个实例可以容纳多个独立的数据库,每个数据库都有自己的集合和权限。
数据库通过名字来标识;数据库名为 UTF-8 字符串,需满足以下条件:
内置数据库:
文档是一组键值(key-value)对(即 BSON)。MongoDB 的文档不需要设置相同的字段,并且相同的字段不需要相同的数据类型,这与关系型数据库有很大的区别,也是 MongoDB 非常突出的特点。一个简单的文档例子如下:
{"name":"李白", "age":30}
文档有如下特性:
文档键命名规范:
集合就是文档组,类似于关系数据库中的表。集合没有固定的结构,可以插入不同格式和类型的数据,但通常情况下我们插入集合的数据都会有一定的关联性。
比如,我们可以将以下不同数据结构的文档插入到同一集合中:
{"name":"杜甫", "age":29} {"name":"李白", "age":30} {"name":"白居易", "age":31, "height":175}
集合名的命名规范:
Capped collections 是固定大小的集合,当达到最大值时,它会自动覆盖最早的文档。和标准的集合不同,你必须要显式的创建一个 capped collection,指定一个集合的大小,单位是字节。固定集合的数据存储空间值提前分配的。固定集合不能删除一个文档,可以使用 drop() 方法删除整个集合。
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 |
数据类型 | 对应数字标识 | 别名 | 描述 | |
---|---|---|---|---|
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,主要是内部使用 |
下面介绍几个重要的数据类型。
ObjectId 类似唯一主键,可以很快的生成和排序,包含 12 bytes:
MongoDB 中的文档必须有一个 _id 键,这个键的值可以是任何类型,默认是 ObjectId 对象。由于 ObjectId 中保存了创建的时间戳,所以不需要为文档保存时间戳字段,可以通过 getTimestamp 函数来获取文档的创建时间:
> var newObject = ObjectId() > newObject.getTimestamp() ISODate("2022-01-07T01:45:50Z")
字符串都是 UTF-8 编码。
时间戳类型用于 MongoDB 内部使用,与普通的日期类型不相关;时间戳值是一个 64 位的值:
在单个 mongod 实例中,时间戳值通常是唯一的。
表示当前距离 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")
该模式只安装一个节点,一般用于测试学习。
主从架构一般用于备份或者做读写分离。一般有一主一从设计和一主多从设计。由两种角色构成:
主(Master):可读可写,当数据有修改的时候,会将 oplog 同步到所有的 Salve 上去。
从(Slave):只读,从 Master 同步数据。
注:新版的 MongoDB 已经不支持这种模式了。
Replica Set 提供了数据的冗余备份,在多个服务器上存储数据副本,提高了数据的可用性, 保证了数据的安全性。Replica Set 包含三类角色:
(1)主节点(Primary)
接收所有的写请求,然后把修改同步到所有 Secondary。一个 Replica Set 只能有一个 Primary 节点,当 Primary 挂掉后,其他 Secondary 或者 Arbiter 节点会重新选举一个主节点。默认读请求也发到Primary 节点处理,如果需要发到 Secondary 可修改下客户端的连接配置。
(2)副本节点(Secondary)
与主节点保持同样的数据集。当主节点挂掉的时候,参与选主。
(3)仲裁者(Arbiter)
不保存数据,不参与选主,只进行选主投票。使用 Arbiter 可以减轻数据存储的硬件需求。
Replica Set 中 Secondary 宕机,不受影响,若 Primary 宕机,会重新选主:
在 MongoDB 中存在另一种集群,就是分片技术,通过在多台机器上分片存储数据,可以满足 MongoDB 数据量大量增长的需求。分片技术中包含的组件:
A.数据分片(Shards):用来存数据,可以是一个单独的mongod实例,也可以是一个副本集。生产环境下 Shard 一般是一个Replica Set,防止该数据片的单点故障。
B.路由(Routers):前端路由,客户端由此接入,让整个集群看上去像单一数据库,应用可以透明使用。路由为 mongos实例,一个 Sharding 集群,可以有一个 mongos,也可以有多 mongos 以减轻客户端请求的压力。
C.配置服务器(Config Servers):保存集群的元数据信息(路由、分片),它也是一个副本集。
Sharding 分片技术高可用的架构图如下:
基于分片切分后的数据块称为 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 才能找出满足条件的文档。
数据是分布在不同的 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 的数据迁移对集群性能存在一定影响,这点无法避免,目前的规避手段只能是将均衡时间放到业务闲时段。
在 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