优质博文:IT-BLOG-CN
一、存储数据格式
MongoDB
作为主流的
NoSQL
数据库之一,使用面向文档的数据存储方式,将数据以
JSON
和
BSON
的方式存储在磁盘中。
BSON Binary JSON
是一种基于
JSON
的二级制序列化格式,用于
MongoDB
存储文档并进行远程过程调用,作为网络数据交互的一种存储形式,类似于
Protocol Buffer
和
Thrift
。
BSON
是一种
schema-less
的存储形式,它的优点是灵活性高,但它的缺点是空间利用率不是很理想
一个
Collection
包含一个
JSON
和
BSON
文档:
JSON
和
BSON
之间最主要的区别如下表所示:
JSON
BSON
JSON
是
javascript
对象表示法
BSON
是二进制
JSON
是一种轻量级的、基于文本的、开放的数据交换格式是一种二进制序列化文档格式
JSON
包含一些基本数据类型,如字符串、数字、布尔值、空值除了支持
JSON
中的类型外,
BSON
还包含一些额外的数据类型,例如日期
Date
、二进制
BinData
等
AnyDB
、
Redis
等数据库将数据存储为
JSON
格式
MongoDB
中将数据存储为
BSON
格式主要用于传输数据主要用于存储数据没有响应的编码和解码技术有专用的编码和解码技术如果想从
JSON
文件中读取指定信息,需要遍历整个数据在
BSON
中,可以使用索引跳过到指定内容
JSON
格式不需要解析,因为它是人类可读的
BSON
需要解析,因为它是二进制的
JSON
是对象和数组的组合,其中对象是键值对的集合,而数组是元素的有序列表
BSON
是二进制数据,在其中可以存储一些附加信息,例如字符串长度、对象类型等
二、架构视图
MongoDB
与
MySQL
中的架构相似,底层都使用了可插拔的存储引擎以满足用户的不同需要。用户可以根据程序的数据特征选择不同的存储引擎,在最新版本的
MongoDB
中使用了
WiredTiger
作为默认的存储引擎,
WiredTiger
提供了不同粒度的并发控制和压缩机制,能够为不同种类的应用提供了最好的性能和存储率。
在存储引擎上层的就是
MongoDB
的数据模型和查询语言了,由于
MongoDB
对数据的存储与
RDBMS:Relational Database Management System
有较大的差异,所以它创建了一套不同的数据模型和查询语言。虽然
MongoDB
查询语言非常强大,支持的功能也很多,同时也是可编程的,不过其中包含的内容非常繁杂、
API
设计也不是非常优雅,所以还是需要一些学习成本的,对于长时间使用
MySQL
的开发者肯定会有些不习惯。
**数据模型
Data Model
:**
【1】内嵌: 内嵌的方式指的是把相关联的数据保存在同一个文档结构之中。
MongoDB
的文档结构允许一个字段或者一个数组内的值作为一个嵌套的文档。通常如下场景选择内嵌:
■ 数据对象之间有包含关系,一般是数据对象之间有一对多或者一对一的关系。
■ 需要经常一起读取的数据。
■ 有
map-reduce/aggregation
需求的数据放在一起,这些操作都只能操作单个
collection
。
【2】引用: 引用方式通过存储数据引用信息来实现两个不同文档之间的关联,应用程序可以通过解析这些数据引用来访问相关数据。通常如下场景选择内嵌引用:
■ 当内嵌数据会导致很多数据的重复,并且读性能的优势又不足于覆盖数据重复的弊端。
■ 需要表达比较复杂的多对多关系的时候。
■ 大型层次结果数据集嵌套不要太深。
三、存储引擎
存储引擎是
MongoDB
的核心组件,负责管理数据如何存储在硬盘和内存上。
MongoDB
支持的存储引擎有:
MMAPv1
、
WiredTiger
、
InMemory
。
InMemory
存储引擎用于将数据只存储在内存中,只将少量的元数据
meta-data
和诊断日志
Diagnostic
存储到硬盘文件中,由于不需要
Disk
的
IO
操作,就能获取所需的数据,
InMemory
存储引擎大幅度降低了数据查询的延迟
Latency
。从
mongodb3.2
开始默认的存储引擎是
WiredTiger
,
3.2
版本之前的默认存储引擎是
MMAPv1
,
mongodb4.x
版本不再支持
MMAPv1
存储引擎。
storage:journal:enabled:truedbPath: /data/mongo/
#是否一个库一个文件夹 directoryPerDB:true##数据引擎 engine: wiredTiger
##WT引擎配置 WiredTiger:engineConfig:##WT最大使用cache(根据服务器实际情况调节) cacheSizeGB:2##是否将索引也按数据库名单独存储 directoryForIndexes:true
journalCompressor:none (默认snappy)
##表压缩配置 collectionConfig:blockCompressor: zlib (默认snappy,还可选none、zlib)
##索引配置 indexConfig:prefixCompression:true
WiredTiger优势
【1】文档空间分配方式:
WiredTiger
使用的是
BTree
存储;
MMAPV1
线性存储需要
Padding
;
【2】并发级别:
WiredTiger
文档级别锁;
MMAPV1
引擎使用表级锁;
【3】数据压缩:
snappy
默认和
zlib
,相比
MMAPV1
无压缩空间节省数倍;
【4】内存使用:
WiredTiger
可以指定内存的使用大小,从
MongoDB 3.2
版本开始,
WiredTiger
内部缓存的使用量,默认值是:
1GB
或
60%of RAM - 1GB
,取两值中的较大值(不同版本会有区别,具体参考版本配置文件说明);文件系统缓存的使用量不固定,
MongoDB
自动使用系统空闲的内存;
**【5】
Cache
使用:**
WT
引擎使用了二阶缓存
WiredTiger Cache
,
File System Cache
来保证
Disk
上的数据的最终一致性。而
MMAPv1
只有
journal
日志;
【6】文档级别的锁:
MongoDB
在执行写操作时,
WiredTiger
在文档级别进行并发控制,就是说,在同一时间,多个写操作能够修改同一个集合中的不同文档;当多个写操作修改同一个文档时,必须以序列化方式执行;这意味着,如果该文档正在被修改,其他写操作必须等待,直到在该文档上的写操作完成之后,其他写操作相互竞争,获胜的写操作在该文档上执行修改操作;
【7】检查点机制: 类似关系数据库的
CheckPoint
,在
Checkpoint
操作开始时,
WiredTiger
提供指定时间点
point-in-time
的数据库快照
Snapshot
,该
Snapshot
呈现的是内存中数据的一致性视图。当向
Disk
写入数据时,
WiredTiger
将
Snapshot
中的所有数据以一致性方式写入到数据文件中。同样
MongoDB
借助
Journal
日志文件也可以还原数据;
【1】
WiredTiger.basecfg
: 存储基本配置信息,与
ConfifigServer
有关系;
【2】
WiredTiger.lock
: 定义锁操作;
【3】
table*.wt
: 存储各张表的数据;
【4】
WiredTiger.wt
: 存储
table*
的元数据;
【5】
WiredTiger.turtle
: 存储
WiredTiger.wt
的元数据;
【6】
journal
: 存储
WAL(Write Ahead Log)
;
WiredTiger存储引擎实现原理
Transport Layer
是处理请求的基本单位。
Mongo
有专门的
listener
线程,每次有连接进来,
listener
会创建一个新的线程
conn
负责与客户端交互,它把具体的查询请求交给
network
线程,真正到数据库里查询由
TaskExecutor
来进行。
写请求:
WiredTiger
的写操作会默认写入
Cache
,并持久化到
WAL (Write Ahead Log)
,每
60s
或
Log
文件达到
2G
做一次
checkpoint
(当然我们也可以通过在写入时传入
j: true
的参数强制
journal
文件的同步,
writeConcern: { w: , j: , wtimeout: })
产生快照文件。
WiredTiger
初始化时,恢复至最新的快照状态,然后再根据
WAL
恢复数据,保证数据的完整性。
Cache
是基于
BTree
的,节点是一个
page
,
rootpage
是根节点,
internal page
是中间索引节点,
leaf page
真正存储数据,数据以
page
为单位读写。
WiredTiger
采用
Copy on write
的方式管理写操作
insert、update、delete
,写操作会先缓存在
cache
里,持久化时,写操作不会在原来的
leaf page
上进行,而是写入新分配的
page
,每次
checkpoint
都会产生一个新的
rootpage
。
checkpoint
流程:
【1】对所有的
table
进行一次
checkpoint
,每个
table
的
checkpoint
的元数据更新至
WiredTiger.wt
【2】对
WiredTiger.wt
进行
checkpoint
,将该
tablecheckpoint
的元数据更新至临时文件
WiredTiger.turtle.set
【3】将
WiredTiger.turtle.set
重命名为
WiredTiger.turtle
【4】上述过程如果中间失败,
WiredTiger
在下次连接初始化时,首先将数据恢复至最新的快照状态,然后根据
WAL
恢复数据,以保证存储可靠性
Journaling:
在数据库宕机时,为保证
MongoDB
中数据的持久性,
MongoDB
使用了
Write Ahead Logging
向磁盘上的
journal
文件预先进行写入。除了
journal
日志,
MongoDB
还使用检查点
checkpoint
来保证数据的一致性,当数据库发生宕机时,我们就需要
checkpoint
和
journal
文件协作完成数据的恢复工作。
【1】在数据文件中查找上一个检查点的标识符;
【2】在
journal
文件中查找标识符对应的记录;
【3】重做对应记录之后的全部操作;
丢数据的情况: 写入数据时,引擎内部是先将数据存在内存中,每隔
60s
或内存存储容量达到
2G
后提交一次到磁盘中,因此在这
60s
期间如果机器宕机,则有极大的可能性会丢失数据
不丢数据的情况: 写入数据时,引擎内部是先将数据存在内存中,同时也会写一份操作日志到内存中,该日志会每个
100ms
持续化到磁盘文件,这种日志成为
Journaling
。
Journaling
类似于关系数据库中的事务日志。
Journaling
能够使
MongoDB
数据库由于意外故障后快速恢复。
MongoDB2.4
版本后默认开启了
Journaling
日志功能,
mongod
实例每次启动时都会检查
journal
日志文件看是否需要恢复。由于提交
journal
日志会产生写入阻塞,所以它对写入的操作有性能影响,但对于读没有影响。
四、写入策略
MongoDB
的写入策略有多种方式,写入策略是指当客户端发起写入请求后,数据库什么时候给应答,
MongoDB
有三种处理策略:客户端发出去的时候,服务器收到请求的时候,服务器写入磁盘的时候。
【1】
Unacknowledged
:客户端发出请求丢到
socket
的时候就收到响应,这个时候客户端不需要等服务器的应答,但是的本地的驱动还是尽可能的通知客户端网络的异常,这和客户端操作系统的配置有关。
【2】
Acknowledged
:这种方式客户端发送接口会等待服务器给的确认,这种方式一定能确保服务器收到了客户端的请求,并且当服务器能够异常时,响应客户端。
【3】
Journaled
:
Journaled
方式相比
Acknowledged
的方式是要保证服务器端已经写入到硬盘文件了。对于
Acknowledged
的方式有可能服务收到请求数据相应客户端后的一瞬间当机了,这个数据就丢失了,但是对于
Journaled
方式,服务器保证写入到磁盘后再相应客户端,即使当机了,也不会导致数据丢失。
【4】
Replica Acknowledged
:这个方式和
Acknowledged
是一样的意思,适用于
Replica sets
模式。
Acknowledged
模式下只有一台机器收到了请求就返回了,对于复制集模式有多台机器的情况,可以要求有多台机器收到写入请求后再相应客户端。这种更安全,但是导致了客户端耗时增加,所以要结合自己的场景设置合适的策略。
可以通过下面的方式设置默认的策略,
majority
表示多数节点写入成功后才相应客户端,也可以替换成具体的数子,比如
w:2
表示至少写入
2
个节点才返回。
wtimeout
表示超时时间,还有一个参加
j
可以设置
true
,
false
表示是否是写入日志才返回。
cfg = rs.conf()
cfg.settings.getLastErrorDefaults ={ w:"majority", wtimeout:5000}
rs.reconfig(cfg)
也可以通过客户端来指定具体的策略,如下: 至少要写入两个节点,超时时间是
5s
db.products.insert({ item:"envelopes", qty :100, type:"Clasp"},{ writeConcern:{ w:2, wtimeout:5000}})
如果复制集是
3
台机器,写入两台机器,流程如下:
版权归原作者 程序猿进阶 所有, 如有侵权,请联系我们删除。