MongoDB 是目前主流的 NoSQL (Not Only SQL)数据库之一,与关系型数据库和其它的 NoSQL 不同,MongoDB 使用了面向文档的数据存储方式,将数据以类似 JSON 的方式(BSON)存储在磁盘上。
MongoDB 其实就与 MySQL 中的架构相差不多,底层都使用了『可插拔』的存储引擎以满足用户的不同需要。
用户可以根据表中的数据特征选择不同的存储引擎,它们可以在同一个 MongoDB 的实例中使用;在最新版本的 MongoDB 中使用了 WiredTiger 作为默认的存储引擎,WiredTiger 提供了不同粒度的并发控制和压缩机制,能够为不同种类的应用提供了最好的性能和存储效率。
在不同的存储引擎上层的就是 MongoDB 的数据模型和查询语言了,与关系型数据库不同,由于 MongoDB 对数据的存储与 RDBMS 有较大的差异,所以它创建了一套不同的查询语言;虽然 MongoDB 查询语言非常强大,支持的功能也很多,同时也是可编程的,不过其中包含的内容非常繁杂、API 设计也不是非常优雅,所以还是需要一些学习成本的,对于长时间使用 MySQL 的开发者肯定会有些不习惯。
RDBMS 与 MongoDB的区别
传统的 RDBMS 其实使用 Table 的格式将数据逻辑地存储在一张二维的表中,其中不包括任何复杂的数据结构,但是由于 MongoDB 支持嵌入文档、数组和哈希等多种复杂数据结构的使用,所以它最终将所有的数据以 BSON的数据格式存储起来。
RDBMS 和 MongoDB 中的概念都有着相互对应的关系,数据库、表、行和索引的概念在两中数据库中都非常相似,唯独最后的 JOIN 和 Embedded Document 或者 Reference 有着巨大的差别。这一点差别其实也影响了在使用 MongoDB 时对集合(Collection)Schema 的设计,如果我们在 MongoDB 中遵循了与 RDBMS 中相同的思想对 Collection 进行设计,那么就不可避免的使用很多的 “JOIN” 语句,而 MongoDB 是不支持 “JOIN” 的,在应用内做这种查询的性能非常非常差,在这时使用嵌入式的文档其实就可以解决这种问题了,嵌入式的文档虽然可能会造成很多的数据冗余导致我们在更新时会很痛苦,但是查询时确实非常迅速。
举例说明:
MySQL:
TPeople
| 姓名 | 性别 | 年龄 | 住址 |
|---|---|---|---|
| 兔哥 | 男 | 25 | 110108 |
TAddress
| 编号 | 国家 | 城市 | 街道 |
|---|---|---|---|
| 110108 | 中国 | 北京 | 海淀区上地三街 |
MongoDB
{
姓名:“兔哥”,
性别:“男”,
年龄:25,
住址:{
编号:110108,
国家:“中国”,
城市:"北京",
街道:“海淀区上地三街”
}
}
每行最大存储量为16M。如果超过16M,则可以存储成GridFS形式。
MongoDB Wiredtiger存储引擎实现原理
MongoDB2.3后默认采用WiredTiger存储引擎。(之前为MMAPV1引擎)
-
Transport Layer业务层
Transport Layer是处理请求的基本单位。Mongo有专门的listener线程,每次有连接进来,listener会创建一个新的线程conn负责与客户端交互,它把具体的查询请求交给network线程,真正到数据库里查询由TaskExecutor来进行。
写请求
WiredTiger的写操作会默认写入Cache,并持久化到WAL(Write Ahead Log),每60s或Log文件达到2G做一次checkpoint,产生快照文件。WiredTiger初始化时,恢复至最新的快照状态,然后根据WAL恢复数据,保证数据的完整性。 Cache是基于BTree的,节点是一个page,root page是根节点,internal page是中间索引节点,leaf page真正存储数据,数据以page为单位与磁道读写。Wiredtiger采用Copy on write的方式管理修改操作(insert、update、delete),修改操作会先缓存在cache里,持久化时,修改操作不会在原来的leaf page上进行,而是写入新分配的page,每次checkpoint都会产生一个新的root page。
-
Journaling
为了在数据库宕机保证 MongoDB 中数据的持久性,MongoDB 使用了 Write Ahead Logging 向磁盘上的 journal 文件预先进行写入;除了 journal 日志,MongoDB 还使用检查点(Checkpoint)来保证数据的一致性,当数据库发生宕机时,我们就需要 Checkpoint 和 journal 文件协作完成数据的恢复工作:
-
在数据文件中查找上一个检查点的标识符;
-
在 journal 文件中查找标识符对应的记录;
-
重做对应记录之后的全部操作;
MongoDB 会每隔 60s 或者在 journal 数据的写入达到 2GB 时设置一次检查点,当然我们也可以通过在写入时传入 j: true 的参数强制 journal 文件的同步。
-
一致性
- WiredTiger使用
Copy on Write管理修改操作。修改先放在cache中,并持久化,不直接作用在原leaf page,而是写入新分配的page,每次checkpoint产生新page。 相关文件:
-
WiredTiger.basecfg: 存储基本配置信息,与ConfigServer有关系
-
WiredTiger.lock: 定义锁操作
-
table*.wt: 存储各张表的数据
-
WiredTiger.wt: 存储table* 的元数据
-
Wile: 存储WiredTiger.wt的元数据
-
journal: 存储WAL
2. 一次Checkpoint的大致流程如下:
对所有的table进行一次Checkpoint,每个table的Checkpoint的元数据更新至WiredTiger.wt 对WiredTiger.wt进行Checkpoint,将该table Checkpoint的元数据更新至临时文件WiredTiger.turtle.set 将WiredTiger.turtle.set重命名为WiredTiger.turtle。 上述过程如中间失败,Wiredtiger在下次连接初始化时,首先将数据恢复至最新的快照状态,然后根据WAL恢复数据,以保证存储可靠性。