首页 > 编程笔记 > MySQL笔记 阅读:1

MongoDB是什么(非常全面)

MongoDB(来自英文单词 Humongous,其中文含义为庞大)是可以应用于各种规模的企业、各个行业以及各类应用程序的开源数据库,是目前 NoSQL 数据库中使用非常广泛的数据库之一。

根据 DB-Engines Ranking 于 2023 年 8 月份发布的全球数据库排名(如下图所示),前 6 名依次是 Oracle、MySQL、Microsoft SQL Server、PostgreSQL、MongoDB 和 Redis。此排名已经持续很长时间。


图 1 2023年8月全球数据库排名

在这排名前 6 的数据库中,MongoDB 被专业开发人员和编码学习人员所使用的比例持续增长,这说明信息技术公司和开发人员对 MongoDB 的认可度非常高。

MongoDB 具有高性能、高可用、可伸缩、易部署、易使用,以及数据存储十分方便等特点,主要特性有:面向集合存储,易于存储对象类型的数据,模式自由,支持动态查询,支持完全索引,支持复制和故障恢复,使用高效的二进制数据存储,文件存储格式为 BSON(一种 JSON 的扩展)等。

数据库访问时延的增大可能会带来直接的经济损失,所以,MongoDB#4.4 之后的版本提供了 Hedged Reads(对冲读)的功能,即在分片集群的场景下,MongoDB 会把一个读请求同时发送给某个分片的两个副本集成员,之后选择最快的返回结果回复给客户端,以缩短业务上的时延。

在生产过程中,因机器故障导致系统死机的情况不可避免。单体数据库在计算能力和存储能力上的瓶颈也无法满足当前的数据量爆发式增长的需求,而解决这两个问题就是系统对高可用和可伸缩架构的需求。MongoDB 在原生上可满足这两方面的需求,它的高可用性体现在对副本集的支持上,可伸缩性体现在分片集群的部署方式上。

MongoDB 的副本集提供自动故障转移和数据冗余服务,副本集的结构可以保证数据库中的全部数据都会有多份备份,这与 HDFS 的备份机制比较类似。采用副本集的集群中具有主(Master)、从(Slaver)、仲裁(Arbiter)这 3 种角色:
当需要存储大量的数据时,主、从服务器都需要存储全部数据,这可能会出现写性能问题。同时,副本机制解决的主要是读数据高可用方面的问题,在对数据库查询时也只限制查询一台服务器,并不能支持一次查询多台数据库服务器,也没有满足数据库读/写操作的分布式需求。

MongoDB 中提供水平可伸缩功能的是分片(Shard)。分片的操作与 HDFS 会将文件切成 128 MB(Hadoop 3.x 默认配置)这种操作相似,也是通过将数据切成数片(Sharding)写入不同的分片节点,完成分布式写入。同时,MongoDB 在读取数据时提供了分布式读的方式,这种方式与 HDFS 的分布式读/写方式十分类似。

MongoDB 的安装非常简单,支持多种安装方式,对第三方组件的依赖程度很低,这使得用户可以使用 MongoDB  较容易地搭建起一个完整的生产集群。MongoDB  的单机部署十分简单,对于分片副本集的安装也有第三方工具可提供辅助支持。

MongoDB 对开发者十分友好,便于使用,支持丰富的查询语言、数据聚合、文本搜索、地理空间查询等功能,让用户可以创建丰富的索引来提升查询速度。读者可以通过对比 MongoDB 与关系数据库操作的这种方式来掌握 MongoDB 的操作特点。

MongoDB 允许用户在服务端执行脚本。用户可以先用 JavaScript 编写某个函数,然后直接在服务端执行;也可以把函数的定义存储在服务端,使用时直接调用即可。

MongoDB 支持多种编程语言,例如 Ruby、Python、Java、C++、PHP、C# 等。

MongoDB面向文档

1、文档数据模型

传统的关系数据库需要对表结构进行预先定义,且对表结构有着严格的要求。而这样的严格要求导致处理数据的过程更加烦琐,甚至降低了执行效率。

在数据量达到一定规模的情况下,传统关系数据库反应迟钝。要想解决这个问题,就需要反其道而行之,尽可能地去掉传统关系数据库的各种规范约束,甚至无须预览定义数据存储结构。

文档数据模型支持对结构化数据的访问。与关系数据模型不同的是,文档数据模型没有强制的架构。文档数据模型以封包键值对的方式进行数据存储,文档数据模型支持嵌套结构。

例如,文档数据模型支持 XML 和 JSON 文档,字段的值可以嵌套存储其他文档,也可存储数组等数据类型。MongoDB 存储数据的文档类型为 BSON,BSON 是一种类 JSON 的二进制存储格式,是 Binary JSON 的简称。

MongoDB的文档数据模型如下图所示:


图 2 MongoDB 的文档数据模型

它的存储逻辑结构为文档。文档中采用键值对结构,文档中的 _id 为主键,默认创建主键索引。从 MongoDB 的存储逻辑结构中可以看出,MongoDB 的相关操作大多是通过指定键完成对值的操作。

文档数据库无须预先定义数据存储结构,这与键值数据库和列族数据库类似,只需在存储时采用指定的文档结构。从图 2 中可以看出,一个大括号({})中包含了若干个键值对,大括号中的内容称为一条文档。

2、文档存储结构

文档数据库的存储结构分为 4 层,从小到大依次是:键值对、文档、集合、数据库。下表描述了 MongoDB 存储与 MySQL 存储的对应关系:

表 3 MongoDB 存储与 MySQL 存储的对应关系
MySQL术语(概念) MongoDB术语(概念) 解释(说明)
database database 数据库
table collection 数据表/集合
row(一条记录,实体) document 行/文档
column field 数据字段/域
table join 表连接,MongoDB 不支持
primary key primary key 主键,MongoDB 自动将 _id 字段设置为主键

从上表可以看出,MongoDB 中的文档、集合、数据库对应于关系数据库中的行数据、表、数据库。

1) 键值对

文档数据库存储结构的基本单位是键值对,包含数据和类型。

键值对的数据包含键和值,其中,键的格式一般为字符串,值的格式可以包含字符串、数值、数组、文档等类型。

键值对可以按照复杂程度分为基本键值对和嵌套键值对。例如,表 3 中键值对的键为字符串,值为基本类型,这种键值对称为基本键值对。

嵌套键值对如下图所示:


图 4 嵌套键值对

从图 4 可以看出,contact 键对应的值为一个文档,该文档中又包含了相关的键值对,这种类型的键值对称为嵌套键值对。

键起着唯一索引的作用,确保一个键值结构中数据记录的唯一性,同时也起着信息记录的作用。例如,“country∶ China”用“∶”实现了对一条地址的分割记录,“country”起到了“China”唯一地址的作用。另外,“country”作为键的内容,说明了所对应内容的一些信息。

值是键所对应的数据,其内容通过键来获取,可存储任何类型的数据,甚至可以为空。

键和值构成了键值对,它们之间的关系是一一对应的,例如定义了“country∶ China”键值对,那么“country”只能对应“China”,而不能对应“USA”。

文档中键的命名规则如下:

2) 文档

文档是 MongoDB的核心概念,是数据的基本单元,与关系数据库中的行十分类似,但比行复杂。

文档是一组有序的键值对集合。文档的数据结构与 JSON 文档的数据结构基本相同,所有存储在集合中的数据格式都是 BSON。

一个简单的文档例子如下:
{"country": "China", "city": "Beijing"}

MongoDB 中的数据具有灵活的架构,且集合不强制要求文档结构,但数据建模的不同可能会影响程序性能和数据库容量。文档和文档之间的关系是数据建模需要考虑的重要因素,包括嵌入和引用两种。

下面通过一个顾客 patron 文档和地址 address 文档之间关系的例子来说明在某些情况下,嵌入关系优于引用关系。patron 文档和 address 文档的内容如下:
{
    _id: "joe",
    name: "Joe Bookreader",
    patron_id: "joe",
    street: "123 Fake Street",
    city: "Faketon",
    state: "MA",
    zip: "12345"
}
采用引用关系时,patron 文档和 address 文档是两个相互独立的文档,只有在查询时进行关联,这就是引用的使用方式。在实际查询中,如果需要频繁地通过 _id 获得 address 信息,那么就需要频繁地通过关联引用来返回查询结果。在这种情况下,嵌入关系表示更为合适。

将 address 信息嵌入 patron 信息中,这样通过一次查询就可获得完整的 patron 和 address 信息。具体代码如下:
{
    _id: "joe",
    name: "Joe Bookreader",
    address: {
              street: "123 Fake Street",
              city: "Faketon",
              state: "MA",
              zip: "12345"
        }
}

如果具有多个 address 文档,那么可以将它们嵌入 patron 文档中,这样通过一次查询就可获得完整的 patron 文档和多个 address 文档信息。具体代码如下:
{
    _id: "joe",
    name: "Joe Bookreader",
    addresses: [
                {
                  street: "123 Fake Street",
                  city: "Faketon",
                  state: "MA",
                  zip: "12345"
                },
                {
                  street: "1 Some Other Street",
                  city: "Boston",
                  state: "MA",
                  zip: "12345"
                }
              ]
}

但是,在一些情况下,引用关系比嵌入关系更有优势。下面通过一个图书出版商与图书信息的例子来进行说明,具体代码如下:
{
    title: "MongoDB: The Definitive Guide",
    author: [ "Kristina Chodorow", "Mike Dirolf" ],
    published_date: ISODate("2010-09-24"),
    pages: 216,
    language: "English",
    publisher: {
                name: "O'Reilly Media",
                founded: 1980,
                location: "CA"
              }
}
{
    title: "50 Tips and Tricks for MongoDB Developer",
    author: "Kristina Chodorow",
    published_date: ISODate("2011-05-06"),
    pages: 68,
    language: "English",
    publisher: {
                name: "O'Reilly Media",
                founded: 1980,
                location: "CA"
              }
}
从上述代码中可以看出,嵌入关系导致出版商的信息被重复发布,这时可采用引用关系来描述集合之间的关系。

在使用引用关系时,关系的增长速度决定了引用的存储位置。如果每个出版商的图书数量很少且新书增长量有限,那么将图书信息存储在出版商文档中是可行的。通过 books 数组来存储每本图书的 id 信息,这样就可以查询到指定出版商的指定图书信息。

但如果图书出版商的图书数量很多,则此数据模型将导致 books 数组是可变的、不断增长的。具体代码如下:
{
    name: "O'Reilly Media",
    founded: 1980,
    location: "CA",
    books: [123456789, 234567890, …]
}
{
    _id: 123456789,
    title: "MongoDB: The Definitive Guide",
    author: [ "Kristina Chodorow", "Mike Dirolf" ],
    published_date: ISODate("2010-09-24"),
    pages: 216,
    language: "English"
}
{
    _id: 234567890,
    title: "50 Tips and Tricks for MongoDB Developer",
    author: "Kristina Chodorow",
    published_date: ISODate("2011-05-06"),
    pages: 68,
    language: "English"
}

为了避免出现可变的、不断增长的数组,可以将出版商引用关系存储到图书文档中,具体代码如下:
{
    _id: "oreilly",
    name: "O'Reilly Media",
    founded: 1980,
    location: "CA"
}
{
    _id: 123456789,
    title: "MongoDB: The Definitive Guide",
    author: [ "Kristina Chodorow", "Mike Dirolf" ],
    published_date: ISODate("2010-09-24"),
    pages: 216,
    language: "English",
    publisher_id: "oreilly"
}
{
    _id: 234567890,
    title: "50 Tips and Tricks for MongoDB Developer",
    author: "Kristina Chodorow",
    published_date: ISODate("2011-05-06"),
    pages: 68,
    language: "English",
    publisher_id: "oreilly"
}

3) 集合

MongoDB 将文档存储在集合中,一个集合是一些文档所构成的对象。如果说 MongoDB 中的文档类似于关系数据库中的行,那么集合就如同关系数据库中的表。

集合存在于数据库中,没有固定的结构,这意味着用户对集合可以插入不同格式和类型的数据。但是,在通常情况下,插入集合的数据都会有一定的关联性,即一个集合中的文档应该具有相关性。

集合的结构如下图所示:


图 5 集合的结构

4) 数据库

在 MongoDB 中,数据库由集合组成。一个 MongoDB 实例可承载多个数据库,这些数据库之间彼此独立。在开发过程中,一个应用的所有数据通常被存储到同一个数据库中。MongoDB 将不同的数据库存储在不同文件中。

数据库的结构如下图所示:


图 6 数据库的结构

MongoDB数据类型

JSON 是一种网络常用的数据格式,具有自描述性。JSON的数据表示方式易于解析,但支持的数据类型有限。BSON 目前主要用于 MongoDB 中,选择 JSON 进行改造的主要原因是 JSON 具有通用性及无模式写入(Schemaless)特性。

BSON 对 JSON 的改进主要有下面 3 点:

1) 更快的遍历速度

BSON 对 JSON 的一个主要改进是 BSON 元素头部设置了一个区域,用于存储元素的长度。

当遍历时,如果想跳过某个文档进行读取,就可以先读取存储在 BSON 元素头部的元素长度,直接查找到指定的点上。而在 JSON 中,要想跳过一个文档进行数据读取,需要先在对此文档进行扫描的同时匹配数据结构,这样才可以完成跳过文档操作。

2) 操作更简易

如果要修改 JSON 数据中的一个值,如将 9 修改为 10,这实际上是将一个字符变成了两个字符,那么会导致其后面的所有内容都向后移一位。而在 BSON 中,可以指定值所在列为整型,那么当将 9 修改为 10 时,只是在整型范围内对数字进行修改,数据总长并不会变化。

需要注意的是,如果数字从整型变为长整型,那么值的修改还是会导致数据总长增加。

3) 支持更多的数据类型

BSON 在 JSON 的基础上增加了很多额外的类型,例如字节数组(Byte Array),这使得二进制数据的存储不再需要先进行 base64 转换、再存储为 JSON 格式,减少了计算开销。

BSON 支持的数据类型如下表所示:

表:BSON 支持的数据类型
类型 描述和示例
Null 表示空值或者不存在的字段,如 {"x": null}
Boolean 布尔型有 true 和 false,如 {"x": true}
Number 数值:客户端默认使用 64 bit 浮点型数值,如 {"x": 3.14} 或 {"x": 3}。对于整型值,包括 NumberInt(长度为 4B 的符号整数)或 NumberLong(长度为 8B 的符号整数)。用户也可以指定数值类型,如 {"x": NumberInt("3")}
String 字符串:BSON 字符串格式是 UTF-8,如 {"x": "中文"}
Regular Expression 正则表达式:语法与 JavaScript 的正则表达式相同,如 {"x": /cba/}
Array 数组:使用 “[]” 表示,如 {"x": ["a","b","c"]}
Object 内嵌文档:文档的值是嵌套文档,如 {"a": {"b": 3 }}
ObjectId 对象 id:对象 id 是一个长度为 12B 的字符串,是文档的唯一标识,如 {"x": ObjectId() }
Binary Data 二进制数据:是一个任意长度(单位为 B)的字符串。它不能直接在 Shell 中使用。如果要将非 UTF-8 标准的字符保存到数据库中,那么二进制数据是唯一的方式
JavaScript 代码:查询和文档中可以包括任何 JavaScript 代码,如 {"x": function(){/*...*/}}
Data 日期:{"x": new Date()}
Timestamp 时间戳:var a = new Timestamp()

MongoDB特性

MongoDB 的特点是高性能、易部署、易使用,存储数据非常方便,具体如下:

MongoDB优势

与关系数据库相比,MongoDB 具有以下优点。

1) 弱一致性(最终一致),更能保证用户的访问速度

举例来说,在传统的关系数据库中,一个 Count 类型的操作会锁定数据集,这样可以保证得到当前情况下较精确的值。这在一些情况下,例如通过自动取款机查看账户信息的时候很重要。

但是,对于英文词库 Wordnik 来说,数据是不断更新的,数据量是不断增长的,这种“较精确”的保证几乎没有意义,反而会产生很大的操作时延,这时需要的是一个大约的数字以及更快的处理速度。

2) 内置GridFS,支持大容量存储

GridFS 是一个出色的分布式文件系统,可以支持海量的数据存储。内置了 GridFS 的 MongoDB 能够满足对大数据集的快速查询。

3) 内置自动分片机制

MongoDB 提供自动分片(Auto Sharding)机制。一个集合可按照记录的范围分成若干个段,切分为不同的分片(Shard)。分片可以和复制集结合,配合复制集能够实现分片+故障转移,使分片之间可以做到负载均衡。

4) 第三方支持丰富

很多 NoSQL 数据库完全属于社区型的,没有官方支持,这给使用者带来了很大风险。而 MongoDB 有商业公司 10gen 为其提供商业培训和支持。

目前,MongoDB 社区非常活跃,很多开发框架迅速提供了对 MongDB 的支持。不少知名大公司和网站也在生产环境中使用 MongoDB。

5) 性能优越

在使用场合下,对于千万级别的文档对象,MongoDB 对有索引字段的查询速度并不会比 MySQL 慢;对非索引字段的查询,和 MySQL 比更是全面胜出。MongoDB 的写入性能同样比较优秀。

相关文章