1. 简介
MongoDB 是一款流行的开源文档型数据库,从它的命名来看,确实是有一定野心的。
MongoDB 的原名一开始来自于 英文单词"Humongous", 中文含义是指"庞大",即命名者的意图是可以处理大规模的数据。
但笔者更喜欢称呼它为 "芒果"数据库,除了译音更加相近之外,原因还来自于这几年使用 MongoDB 的两层感觉:
- 第一层感受是"爽",使用这个文档数据库的特点是几乎不受什么限制,一方面Json文档式的结构更容易理解,而无Schema约束也让DDL管理更加简单,一切都可以很快速的进行。
- 第二层感受是"酸爽",这点相信干运维或是支撑性工作的兄弟感受会比较深刻,MongoDB 由于入门体验"太过于友好",导致一些团队认为用好这个数据库是个很简单的事情,所以开发兄弟在存量系统上埋一些坑也是正常的事情。所谓交付一时爽,维护火葬场.. 当然了,这句话可能有些过。 但这里的潜台词是:与传统的RDBMS数据库一样,MongoDB 在使用上也需要认真的考量和看护,不然的化,会遇到更多的坑。
MongoDB 数据库的一些特性:
- 面向文档存储,基于JSON/BSON 可表示灵活的数据结构
- 动态 DDL能力,没有强Schema约束,支持快速迭代
- 高性能计算,提供基于内存的快速数据查询
- 容易扩展,利用数据分片可以支持海量数据存储
- 丰富的功能集,支持二级索引、强大的聚合管道功能,为开发者量身定做的功能,如数据自动老化、固定集合等等。
- 跨平台版本、支持多语言SDK
假定你是初次了解 MongoDB,下面的内容将能帮助你对该数据库技术的全貌产生一定的了解。
2. 基本模型
数据结构对于一个软件来说是至关重要的,MongoDB 在概念模型上参考了 SQL数据库,但并非完全相同。
如下表所示:
SQL概念 | MongoDB概念 |
---|---|
database | database |
table | collection |
row | document |
column | field |
- database 数据库,与SQL的数据库(database)概念相同,一个数据库包含多个集合(表)
- collection 集合,相当于SQL中的表(table),一个集合可以存放多个文档(行)。 不同之处就在于集合的结构(schema)是动态的,不需要预先声明一个严格的表结构。更重要的是,默认情况下 MongoDB 并不会对写入的数据做任何schema的校验。
- document 文档,相当于SQL中的行(row),一个文档由多个字段(列)组成,并采用bson(json)格式表示。
- field 字段,相当于SQL中的列(column),相比普通column的差别在于field的类型可以更加灵活,比如支持嵌套的文档、数组。此外,MongoDB中字段的类型是固定的、区分大小写、并且文档中的字段也是有序的。
另外,SQL 还有一些其他的概念,对应关系如下:
SQL概念 | MongoDB概念 |
---|---|
primary key | _id |
foreign key | reference |
view | view |
index | index |
join | $lookup |
transaction | transaction |
group by | aggregation |
- _id 主键,MongoDB 默认使用一个_id 字段来保证文档的唯一性。
- reference 引用,勉强可以对应于 外键(foreign key) 的概念,之所以是勉强是因为 reference 并没有实现任何外键的约束,而只是由客户端(driver)自动进行关联查询、转换的一个特殊类型。
- view 视图,MongoDB 3.4 开始支持视图,和 SQL 的视图没有什么差异,视图是基于表/集合之上进行动态查询的一层对象,可以是虚拟的,也可以是物理的(物化视图)。
- index 索引,与SQL 的索引相同。
- $lookup,这是一个聚合操作符,可以用于实现类似 SQL-join 连接的功能
- transaction 事务,从 MongoDB 4.0 版本开始,提供了对于事务的支持
- aggregation 聚合,MongoDB 提供了强大的聚合计算框架,group by 是其中的一类聚合操作。
2.1 BSON数据类型
MongoDB 文档可以使用 Javascript 对象表示,从格式上讲,是基于 JSON 的。一个典型的文档如下:
{
"_id": 1,
"name" : { "first" : "John", "last" : "Backus" },
"contribs" : [ "Fortran", "ALGOL", "Backus-Naur Form", "FP" ],
"awards" : [
{
"award" : "W.W. McDowell Award",
"year" : 1967,
"by" : "IEEE Computer Society"
}, {
"award" : "Draper Prize",
"year" : 1993,
"by" : "National Academy of Engineering"
}
]
}
曾经,JSON 的出现及流行让 Web 2.0 的数据传输变得非常简单,所以使用 JSON 语法是非常容易让开发者接受的。
但是 JSON 也有自己的短板,比如无法支持像日期这样的特定数据类型,因此 MongoDB 实际上使用的是一种扩展式的JSON,叫 BSON(Binary JSON)。
BSON 所支持的数据类型包括:
2.2 分布式ID
在单机时代,大多数应用可以使用数据库自增式ID 来作为主键。 传统的 RDBMS 也都支持这种方式,比如 mysql 可以通过声明 auto_increment来实现自增的主键。 但一旦数据实现了分布式存储,这种方式就不再适用了,原因就在于无法保证多个节点上的主键不出现重复。
为了实现分布式数据ID的唯一性保证,应用开发者提出了自己的方案,而大多数方案中都会将ID分段生成,如著名的 snowflake 算法中就同时使用了时间戳、机器号、进程号以及随机数来保证唯一性。
MongoDB 采用 ObjectId 来表示主键的类型,数据库中每个文档都拥有一个_id 字段表示主键。_id 的生成规则如下:
其中包括:
- 4-byte Unix 时间戳
- 3-byte 机器 ID
- 2-byte 进程 ID
- 3-byte 计数器(初始化随机)
值得一提的是 _id 的生成实质上是由客户端(Driver)生成的,这样可以获得更好的随机性,同时降低服务端的负载。当然服务端也会检测写入的文档是否包含_id 字段,如果没有就生成一个。
3. 操作语法
除了文档模型本身,对于数据的操作命令也是基于JSON/BSON 格式的语法。
比如插入文档的操作:
db.book.insert(
{
title: "My first blog post",
published: new Date(),
tags: [ "NoSQL", "MongoDB" ],
type: "Work",
author : "James",
viewCount: 25,
commentCount: 2
}
)
执行文档查找:
db.book.find({author : "James"})
更新文档的命令:
db.book.update(
{"_id" : ObjectId("5c61301c15338f68639e6802")},
{"$inc": {"viewCount": 3} }
)
删除文档的命令:
db.book.remove({"_id":
ObjectId("5c612b2f15338f68639e67d5")})
在传统的SQL语法中,可以限定返回的字段,MongoDB可以使用Projection来表示:
db.book.find({"author": "James"},
{"_id": 1, "title": 1, "author": 1})
实现简单的分页查询:
db.book.find({})
.sort({"viewCount" : -1})
.skip(10).limit(5)
这种基于BSON/JSON 的语法格式并不复杂,它的表达能力或许要比SQL更加强大。与 MongoDB 做法类似的还有 ElasticSearch,后者是搜索数据库的佼佼者。