Mongodb——数据结构
Collections
在mongodb中叫做集合,是文档的集合,内部存储一行又一行不规则的数据;相当于mysql的表
Document
存储在集合中一行又一行的数据
Document——松散结构特性
mongodb是一个模式自由的NOSQL,不像其他RDBMS一样需要预先定义Schema而且所有的数据都整齐划一,mongodb的document是BSON格式,松散的
Document——松散数据策略(背景)
对于有些update,比如对array新增元素等,
会导致document尺寸的增加
,无论任何存储系统包括MYSQL、Hbase等,对于这种情况都需要额外的考虑,这归结于磁盘空间的分配是连续的(连续意味着读取性能将更高,存储文件空间通常是预分配固定尺寸
,我们需要尽可能的利用磁盘IO的这种优势)
Document——松散数据策略(Power of 2 Allocate)
对于MMAPV1引擎, 如果文档尺寸超过了原分配的空间),mongodb将会重新分配新的空间来保存整个文档(
旧文档空间回收,可以被后续的insert重用
)
Mongodb——存储引擎
wiredTiger引擎——优缺点
文档级别的锁——高并发
所有的write请求都基于
“文档级别”的lock
,因此多个客户端可以同时更新一个colleciton中的不同文档,这种更细颗粒度的lock,可以支撑更高的读写负载和并发量
内存缓存的限制——可能产生swap
wiredTiger不像MMAPV1引擎那样尽可能的耗尽内存,它可以通过在配置文件中指定“
cacheSizeGB
”参数设定引擎使用的内存量,此内存用于缓存工作集数据(索引、namespace,未提交的write,query缓冲等)
。
MMAPv1引擎——优缺点
内存映射机制
此引擎将不会使用到swap空间,对于大量数据的插入比较友好,因为全部通过内存映射实现
collection级别的锁
不利于并发操作
内存映射——优缺点
虚拟内存优点
Mongodb利用好了虚拟内存的优势,文件映射到一个虚拟内存的区域。
有了内存映射文件,要访问的数据就好像都在内存里面,简单化了MongoDB访问和修改数据的逻辑
MongoDB读写都只是和虚拟内存打交道,剩下都交给OS打理 (mongoDB的高效数直接通过系统高效的VM系统来实现的)
虚拟内存流程
操作系统通过mmap来把进程所需的所有数据映射到这个地址空间(红线), 然后再把当前需要处理的数据映射到物理内存(灰线)
当进程访问某个数据时,如果数据不在虚拟内存里, 触发page fault,然后OS从硬盘里把数据加载进虚拟内存和物理内存
如果物理内存满了,触发swap-out操作,这时有些数据就需要写回磁盘, 如果是纯粹的内存数据,写回swap分区,如果不是就写回磁盘。
Mongodb——存储结构
Data Files
存储位置配置
我们在mongodb配置文件中指定了dbpath
存储单位
该目录下的文件是以database作为基本单位,然后达到一定容量后
序号自增的方式
实现持久存储的
存储步长
数据文件从16M开始,每次扩张一倍(16M、32M、64M、128M…),在默认情况下单个data file的最大尺寸为2G
存储文件
(1)实际数据文件
:一种是info.n(n表示数字,从0开始,由于我用的是64位机器,所以文件从16M开始,每次翻倍,直到2G后每次都生成2G),数据文件在内部分为很多块,每一块保存一个名字空间的数据,块与块之间用链表连接,每块中的每条数据间也用链表连接
。(2)命名空间文件
:是个16m的文件info.ns,info.ns,用来保存名字空间对应的元数据,info.ns是一个hashtable,保存了每个名字空间中的储存信息,包括大小,块数,第一块位置,最后一块位置,索引信息等,而块的位置又通过文件序号(后面跟的数字)与文件中的偏移量来确定。
journal文件
write操作细节
(1)首先写入journal日志
(2)然后将数据在内存中修改(mmap)
(3)此后后台线程间歇性的将内存中变更的数据flush到底层的data files中(可以通过syncPeriodSecs来配置)
注意事项——采用独立的磁盘驱动
开启journal日志功能,将会导致write性能有所降低,可能降低5~30%,因为它直接加剧了磁盘的写入负载,我们可以将journal日志单独放置在其他磁盘驱动器中来提高写入并发能力(与data files分别使用不同的磁盘驱动器)
Mongodb——主从模式
通常是三个对等的节点构成一个“复制集”集群
有“primary”和secondary等多中角色
(1)primary负责读写请求
(2)secondary可以负责读请求,这有配置决定,;
如果primay失效,则集群进行“多数派”选举,选举出新的primary,即failover机制,即HA架构。复制集解决了单点故障问题,也是mongodb垂直扩展的最小部署单位,当然sharding
cluster中每个shard节点也可以使用Replica set提高数据可用性。
Mongodb——分片模式
sharding模式将应用的数据访问操作分散到多个shard上,每个shard只承担一部分请求;比如read操作只需要访问持有数据的shard节点即可
sharding模式也减少每个shard节点的数据存储量。
分片服务结构
将整个collection的数据将根据sharding key被sharding到多个mongod节点上,即每个节点持有collection的一部分数据,
Config Server
存储集群所有节点、分片数据路由信息。默认需要配置3个Config Server节点。
所有存、取数据的方式,所有shard节点的信息,分片功能的一些配置信息。可以理解为真实数据的元数据。
Mongos(数据路由)
提供对外应用访问,所有操作均通过mongos执行。一般有多个mongos节点。数据迁移和数据自动平衡。
当数据写入时,MongoDB Cluster根据分片键设计写入数据。
当外部语句发起数据查询时,MongoDB根据数据分布自动路由至指定节点返回数据
shard
真正的数据存储位置,以chunk为单位存数据。
分片机制解析
shard key(分片键)
数据的分区根据“shard key”,对于每个需要sharding的
collection
,都需要指定“shardkey”(分片键
);
分片键必须是索引字段或者为组合索引的左前缀
;mongodb根据分片键将数据分成多个chunks,并将它们均匀分布在多个shards节点上。目前,mongodb支持两种分区算法:区间分区(Range)和哈希(Hash)分区。
Range分区
定义:
首先shard key必须是数字类型,整个区间的上下边界分别为“正无穷大”、“负无穷大”,每个chunk覆盖一段子区间,即整体而言,任何shard> key均会被某个特定的chunk所覆盖。区间均为作闭右开。每个区间均不会有重叠覆盖,且互相临近。当然chunk并不是预先创建的,而是随着chunk数据的增大而不断split。(参见下文)
优缺点
Range分区更好的支持range查询,根据指定的shard key进行range查询,router可以很简单的判断出那些chunks覆盖此range,并将请求转发给特定的几个shards。不过当shard key是单调递增时,range分区会导致数据分布不均
,因为在一定时间内,所有write请求(读取最新数据的read请求)将会映射到一个shard上,即少数shards在某段时间内承载了系统的大部分请求。
Hash分区
定义
计算shard key的hash值(64位数字),并以此作为Range来分区,基本方式同1);Hash值具有很强的散列能力,通常不同的shard
key具有不同的hash值(冲突是有限的),这种分区方式可以将document更加随机的分散在不同的chunks上。
优点
Hash分区正好相反,即使是单调递增的shardkey,它们的Hash值也有较大不同,因此这些数据将会比较随机的分散在多个chunks上,但是这引入了range查询的问题,临近的shard
缺点
key可能分布在不同的chunks上甚至是shards上,这意味着range查询需要访问所有的shards,特别是在有sort、limit等操作时
。
sharding机制
动态平衡
集群环境可以动态调整,比如数据量增大到一定程度,可以向集群中增加shard节点;如果数据量紧缩,也可以移除shard;这些过程均会触发chunks的动态平衡。
数据的增删操作以及集群中增减shards节点,都可能导致数据的分布不均,不过mongos提供了balancer机制,它可以对chunks进行split(分裂)和迁移,最终动态平衡数据分布。
Splitting
一个后台进程用于避免chunk增长的过大,当
chunk尺寸超过指定的chunk size时
(默认为64M,可以命令修改),mongodb将会把此chunk分成等同的2个
;inserts和updates操作均可以触发split,分离时mongodb不会迁移任何数据,也不会对shard产生影响
Balancing
一个后台线程用于管理chunks迁移,balancer可以运行在任何一个(多个)mongos上;当集群中collection数据分布不均时,balancer将把一部分chunks从chunks量最大的shard上
迁移到持有量最小的shards上,直到平衡为止
;在chunk迁移时,源shard将会把此chunk数据全部发送给目标shard,在此期间,源shard仍负责接收客户端的请求(read、write);最终,在config servers上变更chunks的位置信息。如果迁移过程中,发生异常,balancer将会终止此chunk的迁移,chunk也将继续保留在原来的shard上;当迁移成功后,mongodb将会删除原来shard上的chunk文件。
threshold
最小化balancing对集群的影响,只有当shards上“最多”与“最少”chunks个数差值达到阀值时,才会重新平衡chunks分布。threshold的值目前没有办法修改,当chunks总数< 20时,此值为2,总数 >= 80时,此值为8,其他为4。一旦balancing工作启动,只有当chunks分布均衡后才会停止,即“最多”与“最少”的差值不大于2。
Mongodb——常见优化
设计优化
No padding Allocation策略
即按照实际数据尺寸分配空间;删除或者update变大而产生的磁盘碎片空间(尺寸变大,意味着开辟新空间存储此document,旧的空间被mark为deleted)可以被其他insert重用
Capped Collection
一种特殊的collection,其尺寸大小是固定值,类似于一个可循环使用的buffer,如果空间被填满之后,新的插入将会覆盖最旧的文档,我们通常不会对Capped进行删除或者update操作,所以这种类型的collection能够支撑较高的write和read
内嵌document
因为card数量无法预估,这就会导致document的尺寸可能不断增加以至于超过“Power of 2 Allocate”,从而触发空间重新分配
带来性能开销,这种情况下,我们需要将内嵌文档单独保存到一个额外的collection中,作为一个或者多个document存储,比如把card列表保存在card collection中。“one-to-one”的情况也需要个别考虑,如果reference文档尺寸较小,可以内嵌,如果尺寸较大,建议单独存储。此外内嵌文档还有个优点就是write的原子性,如果使用reference的话,就无法保证了。
整体优化