MongoDB基础知识
MongoDB的目标是做通用型的NewSQL数据库,兼有关系型数据库的强大功能以及NoSQL易扩展的特性,做数据库领域的瑞士军刀
特性:
架构的设计最重要的不是当前设计的可扩展性,对设计的可读性,还是说原来的数据库三范式。最重要的在于,你的app一般呈现出来的是什么数据结构,就设计成什么样,取出即用。
一对少,内嵌较好
一对多,引用较好
MongoDB可能更适合初始数据形式很难确定的项目中。但这并不意味着可以因此而偷懒:在项目开始的时候忽略设计正确的数据表,可能会在之后引入问题
- 文件存储格式为BSON,使用易于掌握和理解的JSON风格语法。相对于JSON来说,BSON拥有更好的性能,主要表现为更快的遍历速度、操作更加简易、增加了额外的数据类型
- 模式自由,支持嵌入子文档和数组,无需事先创建数据结构,属于逆规范化的数据模型,有利于提高查询速度
- 动态查询,支持丰富的查询表达式,使用JSON形式的标记,可轻易查询文档中内嵌的对象、数组以及子文档
- 完整的索引支持,包括文档内嵌对象和数据,同时还提供了全文检索方式,MongoDB的查询优化器会分析查询表达式,并生成一个高效的查询计划
使用BSON格式出于以下三个目的:
- 效率。BSON是为效率而设计的,它只需要使用很少的空间。即使在最坏的情况下,BSON都比JSON在最好情况下的存储效率高
- 传输性。在某些情况下,BSON会牺牲额外的空间让数据的传输更加方便。比如,字符串传输的前缀会标识字符串的长度,而不是在字符串的末尾打上结束的标记。这样的传输形式有利于MongoDB修改传输的数据
- 性能。BSON格式的编码和解码都是非常迅速的。它使用C风格的数据表现形式,这样在各种语言中都可以高效使用
说明:
- 数据库名为文件系统的文件名
- 文档以行的形式存储在集合中
- 单个文档最大支持16M,超过16M的文件使用GridFS存储
- MongoDB默认会为插入的文档生成_id字段(如果应用本身没有指定该字段)
GridFS:
- GridFS是MongoDB的一种存储机制,用于存储和恢复那些超过16M的BSON文件
- GridFS会将大文件对象分割成多个小的chunk,默认为256k/个,每个chunk将作为MongoDB的一个文档被存储在chunks集合中
- GridFS用两个集合来存储一个文件:fs.files与fs.chunks。fs.files存储metadata,fs.chunks存储二进制数据。也可以自定义为其它集合
存储引擎:
- 默认为Wired Tiger
- In-memory和Encrypted只在企业版提供
- 为第三方插件式存储引擎提供了API
WiredTiger:
支持行存储、列存储以及LSM等三种存储形式,MongoDB使用时,只是将其作为普通的KV存储引擎来使用,MongoDB的每个集合对应一个WT的table,table里包含多个Key-Value pairs,以B树形式存储。用户可以自己选择存储数据的压缩比例,MongoDB提供最高达80%的压缩率,不过压缩率越高数据处理的时间成本也越多。
MongoDB持久化的步骤:
- 客户端的数据进来
- 数据操作写入到日志缓存中
- 数据写入到数据缓冲
- 返回操作结果到客户端(异步)
- 后台线程进行日志缓冲中的数据刷盘,非常频繁(默认100ms),也可自行设置
- 后台线程进行数据缓冲中的数据刷盘,默认是60s
副本集持久化步骤:
- 客户端的数据进来
- 数据写入到日志缓冲中
- 数据写入到数据缓冲
- 把日志缓冲中的操作日志放到OPLOG中来
- 返回操作结果到客户端(异步)
- 后台线程进行OPLOG复制到从节点,这个频率是非常高的,比日志刷盘频率还要高,从节点会一直监听主节点,OPLOG一有变化就会进行复制操作
- 后台线程进行数据缓冲中的数据刷盘,默认是60s
副本集成员:
- Primary:主节点
- Secondary:副节点统称为Secondary。里面存放着实时同步的主节点数据。副节点默认是不能读和写的,可以通过设置使得副节点可读,从而实现主节点写,副节点读。做到读写分离。主副节点可能随时切换,取决于主节点是不是发生故障。若主节点故障,会自动选举副节点成为主节点
- Hidden:将一个副节点优先级设为0,然后再设为Hidden,便得到一个Hidden节点。客户端不会向Hidden节点发送请求,隐藏节点也不会成为复制源。对隐藏节点设置备份延迟,Hidden节点就不会再实时同步,而是一段时间后才开始。这样可以避免备份到恶性数据
选举制度:
- 默认为1,0永远不会被选为主节点。选举中会先比较数据新旧程度(通过oplog),然后才会比较优先级。高优先级优先成为主节点
- 主节点不能强制设置,必须间接选出来
- 一票否决,一张否决票可以抵一万张赞成票
-
-
选举机制
MongoDB节点之间维护心跳检查,主节点选举由心跳触发。
心跳检查
MongoDB复制集成员会向自己之外的所有成员发送心跳并处理响应信息,因此每个节点都维护着从该节点POV看到的其他所有节点的状态信息。节点根据自己的集群状态信息判断是否需要更换新的Primary。
在实现的时候主要由两个异步的过程分别处理心跳响应和超时,抛开复杂的条件检查,核心逻辑主要包括:
- Secondary节点权重比Primary节点高时,发起替换选举;
- Secondary节点发现集群中没有Primary时,发起选举;
- Primary节点不能访问到大部分(Majority)成员时主动降级;
降级操作会断开链接,终止用户请求等。
MongoDB节点之间维护心跳检查,主节点选举由心跳触发。
心跳检查
MongoDB复制集成员会向自己之外的所有成员发送心跳并处理响应信息,因此每个节点都维护着从该节点POV看到的其他所有节点的状态信息。节点根据自己的集群状态信息判断是否需要更换新的Primary。
在实现的时候主要由两个异步的过程分别处理心跳响应和超时,抛开复杂的条件检查,核心逻辑主要包括:
- Secondary节点权重比Primary节点高时,发起替换选举;
- Secondary节点发现集群中没有Primary时,发起选举;
- Primary节点不能访问到大部分(Majority)成员时主动降级;
降级操作会断开链接,终止用户请求等。
选举发起
发起选举的节点需要首先做一些条件判断,比如节点位于备选节点列表中、POV包含复制集Majority等,真实情况的条件判断更加复杂。然后将自己标记为选举过程中,并发起投票请求。
投票
投票发起者向集群成员发起Elect请求,成员在收到请求后经过会一系列检查,如果通过检查则为发起者投一票。一轮选举中每个成员最多投一票,在PV0中用30秒“选举锁”避免为其他发起者重复投票,这导致了如果新选举的Primary挂掉,可能30秒内不会有新的Primary选举产生;在PV1中通过为投票引入单调递增的Term解决重复投票的问题。
如果投票发起者获得超过半数的投票,则选举通过成为Primary节点,否则重新发起投票。
注意
发起选举的节点需要首先做一些条件判断,比如节点位于备选节点列表中、POV包含复制集Majority等,真实情况的条件判断更加复杂。然后将自己标记为选举过程中,并发起投票请求。
投票
投票发起者向集群成员发起Elect请求,成员在收到请求后经过会一系列检查,如果通过检查则为发起者投一票。一轮选举中每个成员最多投一票,在PV0中用30秒“选举锁”避免为其他发起者重复投票,这导致了如果新选举的Primary挂掉,可能30秒内不会有新的Primary选举产生;在PV1中通过为投票引入单调递增的Term解决重复投票的问题。
如果投票发起者获得超过半数的投票,则选举通过成为Primary节点,否则重新发起投票。
注意
- MongoDB选举需要获得大多数投票才能通过,在一轮选举中两个节点得票相同则重新选举,为避免陷入无限重复选举,MongoDB建议复制集的成员个数为奇数个,当Secondary节点个数为偶数时,可以增加一个Arbiter节点,。
- PV0版本中,所有成员都可以投否决票,一个否决票会将得票数减少10000,所以一般可以认为只要有成员反对,则该节点不能成为Primary。PV1版本取消了否决票。
- 选举过程中,复制集没有主节点,所有成员都是只读状态。