My Avatar

Shadow

I love bleak day, like something will happen

MongoDB

2018年04月17日 星期二, 发表于 北京

如果你对本文有任何的建议或者疑问, 可以在 这里给我提 Issues, 谢谢! :)

  我在这几年的开发过程中都是用的Mongo,可以说对传统的关系型数据库反倒是没有怎么接触,但是对于Mongo的使用也挺皮毛的,这里就花点时间好好学习下Mongo吧


NoSql数据库

  所谓NoSql并不是no sql,而是not only sql,即不仅仅是SQL,是对不同于传统的关系型数据库的数据库系统的统称,它具有非关系型分布式不提供ACID的数据库设计模式等特征。      NoSQL数据库和关系型数据库存在许多显著的不同,其中以最重要的不同点就是,NoSQL数据库不使用SQL作为自己的查询语言,而且数据的存储模式也不再是表格模型,NoSQL常常采用key-value格式或Document格式,来存储数据。      近年来,NoSQL类型数据库系统发展及其迅速,其原因在于,NoSQL数据库一般都具有水平高扩展性支持高并发的用户访问量,常见的NoSQL数据库有key-value存储系统(比如,Big、MecacheDB、Dynamo),和文档型数据库(MongoDB、SimpleDB、CouchDB)。

  在传统的关系型数据库中,需要遵循ACID原则,即Atomicity原子性、Consistency一致性、Isolation独立性、Durability持久性,而Nosql型数据库并不需要遵守这样的规则,但它也有一套自己遵循的对可用性及一致性的弱要求原则:BASE

  BASE:即Basically Available、Soft-state、Eventually Consistent。他是牺牲了高一致性,来获取可用性和分区容忍性的模型(涉及到CAP原理:在一个分布式系统中,一致性、可用性、分区容忍性三者最多只能满足两个)

  Basically Available:基本可用

  Soft-state:软状态/柔性事务。

  Eventual Consistency:最终一致性

  Nosql数据库并不像关系型数据库是按表格模型来存储数据,而是支持key-value、文档、图存储、列存储等等存储方式

  那么关系型数据库和NoSql数据库到底各自有何优缺点,又如何选择呢?

  首先来看Nosql数据库的优势:

  1. 性能:nosql数据结构简单,而且无需保证ACID,所以具有较高的性能   2. 扩展性:nosql数据库存储的数据没有规定是什么样子的,关系型数据库对表中的数据都要定义好字段,如果要增加字段则需要修改schema,在数据量大的时候,是很麻烦的。而nosql数据库想加什么字段就加什么字段   3. 高可用:通过分布式能较容易地实现高可用。

  但是nosql也有一些缺点:      1. 不支持事务:对于安全性能要求很高的业务,像银行的交易等,还是需要传统的关系型数据库来支持   2. 缺少统一标准:SQL数据库已经高度标准化,有完整的生态链,而NoSQL家族庞大,思路各不相同,没有统一标准   3. 有限的查询功能:关系型数据库可以通过sql支持在一个表以及多个表之间做非常复杂的数据查询,而nosql支持的查询功能就很有限了,不支持join操作。   4. 运维复杂:NoSQL存储结构很丰富,非常强大,但同时也意味着繁杂,使其学习、应用、运维的成本增高,而关系数据库有丰富的运维工具和大量经验丰富的运维人员

  所以在下面这些情况下,我们是推荐使用nosql数据库的

  1. 数据库事务一致性需求不大:很多web实时系统并不要求严格的数据库事务,对读一致性的要求很低,有些场合对写一致性要求也不高。因此数据库事务管理成了数据库高负载下一个沉重的负担。   2. 数据库的写实时性和读实时性需求不大:对关系数据库来说,插入一条数据之后立刻查询,是肯定可以读出来这条数据的,但是对于很多web应用来说,并不要求这么高的实时性,比方说我(JavaEye的robbin)发一条消息之后,过几秒乃至十几秒之后,我的订阅者才看到这条动态是完全可以接受的。   3. 对复杂的SQL查询,特别是多表关联查询的需求不大:任何大数据量的web系统,都非常忌讳多个大表的关联查询,以及复杂的数据分析类型的复杂SQL报表查询,特别是SNS类型的网站,从需求以及产 品设计角度,就避免了这种情况的产生。往往更多的只是单表的主键查询,以及单表的简单条件分页查询,SQL的功能被极大的弱化了。

MongoDB

  MongoDB就是Nosql的一种,但其实,MongoDB是一个介于关系数据库和非关系数据库之间的产品,是非关系数据库当中功能最丰富,最像关系数据库的。他支持的数据结构非常松散,是类似json的bjson格式,因此可以存储比较复杂的数据类型。Mongo最大的特点是他支持的查询语言非常强大,其语法有点类似于面向对象的查询语言,几乎可以实现类似关系数据库单表查询的绝大部分功能,而且还支持对数据建立索引。

  MongoDB是一个开源的文档数据库,高性能、高可用、自动扩展。

  Mongo中的一条记录就是一个文档,他是一个类似json的数据接口,每个字段都有一个key,value可以是某种类型的值、其他的文档、数组或者文档数组

  那么使用这种文档存储格式的好处在于:

  1. 文档跟很多编程语言中的原生数据类型一致   2. 因为各种内嵌的文档和数组,就减少了对表之间join的需求   3. 数据schema随时动态可变,想存什么就存什么

关键特性

  High Performance:MongoDB提供高性能的数据存储,尤其表现在:

  1. 对于内嵌数据模型的支持,大大减少了IO活动   2. 索引的支持,使得查询更快,而且对于value是内嵌文档或数组的key也支持索引

  Rich Query Language:MongoDB提供了丰富的查询语言来支持读写操作,包括数据的聚合、文本的搜索以及地理位置的查询

  High Availability:MongoDB的副本集特性,被称为replica set,提供了:自动容错(失效备援)和数据冗余,一个replica set就是一组的MongoDB服务器,他们维护着相同的数据集,提供数据冗余并且增加了数据的可用性

  Horizontal Scalability:MongoDB提供了水平扩展能力作为它的核心功能:

  1. Sharding将数据分布在集群中的各台机器上   2. 从3.4开始,MongoDB支持基于shard key创建数据分区(zones)。在一个数据平衡的集群中,MongoDB会将覆盖某一个分区的读写操作导向到在该分区的shards中

  Support for Multiple Storage Engines:MongoDB支持多个存储引擎,所谓存储引擎即决定MongoDB如何存储数据的组件,默认是存到disk上,当然它也提供基于内存的存储引擎

view

  mongodb从3.4版本开始新增了视图,视图是建立在集合之上的,并且是只读的,他并不会创建数据,他使用的都是底层集合的数据,因此它也支持索引和排序,只要低层集合上有索引。

  那么究竟为啥要推出视图呢?

  在有视图之前,如果我们要得到一个集合的子集只能有两种方法:1.自己手动创建一个子集合,将原集合中相关的数据复制到这个集合中,但是这样如果数据动态变化了,那么同步过程会有点烦,而且数据是冗余的

  2是通过Mongo的聚合框架来完成,也就是得写一堆的类似:

1
2
3
4
db.articles.aggregate( [
                    { $match : { score : { $gt : 70, $lte : 90 } } },
                    { $group: { _id: null, count: { $sum: 1 } } }
                   ] );

  的操作,这样会很复杂

  而现在有了视图,我们可以通过在集合上创建一个视图,将这部分子集中的数据通过视图提供出来,例如

1
db.createView("viewName","colName",{$match:{state:"AL"}})

  这样我们就对colName集合上的state为AL的数据,建立了一个视图,如果集合里的数据动态变化了,视图里的数据也会相应的变化,因为视图本身并不创建数据,他只会引用底层集合的数据,因此,数据不会冗余。

aggregation

  mongo的聚合操作是很有用的管道式操作,能利用聚合对集合进行复杂的查询、统计、分析

  还支持了Map-Reduce

  具体的先不说了,后面有时间再记录

data models

  https://www.cnblogs.com/Wddpct/p/6031195.html

  数据模型分内嵌和引用两类,具体情况具体方案

index

  索引是比较重要的一部分

  Mongodb中的索引能帮助高效查询的实现,如果没有索引,Mongodb会进行collection扫描,以获得满足查询条件的文档。

  通过下面这个图,我们可以简单了解索引的工作方式:

  默认_id索引:防止用户插入两条相同_id的数据,该索引自动添加,无法被删除

  Single Field索引:对于一个字段建立升序或降序的索引,不管是升序还是降序的索引,在排序时都没有影响,因为mongo能从两个方向遍历

  Compound索引:mongo支持在多个字段上建立的索引,复合索引建立时,字段的顺序是很重要的,例如{userid:1,score:-1}就是一个两个字段的索引,这个索引会首先对文档按照userid排序,然后在每一个userid值下,对文档按照score排序。对于复合索引,排序的时候指定key的顺序是能影响是否能使用索引的。

  Multikey索引:该索引用来对数组字段进行索引,如果你对一个数组类型的字段建立索引,mongo会对数组中的每一个元素创建单独的索引,而且是自动判断是否建立multikey索引的,不需要用户指定。

  Geospatial索引:地理位置索引

  Text索引:为了支持字符串内容的搜索而做的索引,

  Hashed索引:对字段内容的hash值进行索引,只支持精准匹配,不支持范围查询

  索引有如下四种不同的属性:

  1. Unique索引:这种类型的索引,保证被索引的字段的值必须是唯一的,例如_id就是这样的   2. Partial索引:只对集合中的一部分数据进行索引,也就是要满足特定的过滤条件的文档才能被索引。   3. Sparse索引:只对集合中有该索引字段的文档进行索引   4. TTL索引:用来在一定的时间之后删除掉这些文档

security

  https://docs.mongodb.com/manual/security/

Replication

  replica set是MongoDB里的一组mongod进程,用来维护相同的数据集合。它能提供数据冗余和高可用,是所有生成环境部署的基础。

  replication还能提升读性能,因为客户端能将读操作发送到不同的服务器去。

  一个replica set通常包含几个数据承载节点和一个arbiter节点(仲裁者)。在数据节点中,有且只有一个主节点,其他都是secondary节点。

  主节点负责接收所有的写操作,并且以majority的write concern进行写操作确认。主节点会将所有对数据集合的改动记录到operation log中,即oplog。

  从节点会复制主节点的oplog,并实施oplog上的操作来与主节点数据进行同步。如果主节点不可用了,一个合格的从节点会发起选举来推选自己为新的主节点。

  在投票选举主节点时,是需要超过半数节点同意才可以的,而如果你的数据节点数是偶数个,那么添加一个仲裁者节点来帮助投票是最好的方式。仲裁者本身并不存储数据,所以需要的资源不多,它只负责投票以及和各个节点之间维持心跳。

  异步复制:从节点是异步地从主节点同步数据的。

  自动的Failover(失效备援):当一个主节点不与其他节点通讯超过一定时间(electionTimeoutMillis配置默认10秒),一个从节点会发动选举来推选自己为新的主节点。

  在选举期间,是没有主节点的,因此写操作在此时是无法进行的,但是如果查询是配置的可以在从节点上进行,那么读操作还是可以的。

  如果我们降低electionTimeoutMillis配置,那么是能够更快第发现主节点的failure,但是整个集群会更频繁第进行选举(有时候主节点甚至是健康的,只是由于网络延迟导致的选举),这会增加rollbacks

  所以我们的应用应该设计成能容忍这样的自动失效备援和接下来的选举过程的。

  默认情况下,我们的客户端是从主节点读取数据,但是客户端也可以指定read preference来将读操作发送到从节点进行。但是异步的复制操作以为着从从节点读到的数据有可能和主节点不一致。

  另外,如果客户端指定readConcern为local或者available,那么就可以在一个写操作被确认前,就读到操作后的结果。这个写操作甚至有可能被rollback

  一个从节点还能被配置为具备一些额外的功能:

  1. Priority 0:防止该从节点在选举中成为主节点   2. Hidden:防止应用从该节点读数据。   3. Delayed:运行一个历史版本的快照。

  rollbacks:当主节点down掉后,重新选举出新的primary后,老的主节点恢复后又要重新加入replica set时,会先rollback,即将他down掉之前已经写入的操作(但是这些操作还没有被复制到其他还存活的节点)进行回滚。另外超过300M的数据回滚是不支持的,如果需要回滚的数据太大,那只能是删掉节点上的数据,然后进行initial sync

  writeConcern:默认是主节点写完就返回给客户端,也可以指定其他方式,例如需要多少个节点写入了才能返回给客户端

  readPreference:决定了将读操作路由到哪个节点,如果选择了从节点可读,那么是怎么选择哪个从节点的呢?事实上,是选择那些没有过于延迟的节点,并且计算网络延时等,找到离客户端最近的节点列表,然后进行随机选择,达到负载均衡的目的

Sharding

     Sharding能增加系统的吞吐量,通过将数据分布在多台机器上。

  shard集群包含三个组件:mongos、config server、shard

  mongos为客户端和shard服务器之间的接口,客户端通过mongos与shard交互,config server存储数据库元信息以及路由数据,config server必须是replica set,不然config server坏了,那么整个shard集群就都不能用了,shard则是一个分片节点,每个分片都存储着一个collection的一部分数据。

  分片是根据片键(shard key)来决定数据被分到哪个shard的,shard key可以是单个字段也可以是复合字段,不论是哪个,他上面必须有唯一性索引,而且片键字段不能被修改

  如果某个collection没有被分片,那么这些collection都有一个对应的primaryShard,他们不会被分片,而是整体存在primaryshard上

  每个shard上都有多个chunk,每个chunk都有一个特定范围的片键,chunk大小是配置好的,当chunk里的数据多了超过了chunk的size后,会split成两个小的chunk,而balancer的作用是保证每个分片下的chunk数平均,因此,当某个shard里的chunk数远大于某个shard里的chunk数时,balancer就会将chunk移动到chunk数少的分片里。

  复合shard key是先根据第一个字段进行shard chunk的分配,然后根据第二个字段来排列

  例如,shard key 为{state:1,id:-1}

  那么假设state为A的记录数在一个chunk里,id 有1,2,3,三种,那么A1、A2、A3被分配在一个chunk里,如果id有成千上万种,一个chunk放不下,那么可能这些以A开头、id各不相同的数据被分配到多个chunk里,但是我们能保证,在这些chunk里,这些数据是相邻的。

  参考http://www.mongoing.com/blog/post/on-selecting-a-shard-key-for-mongodb

  http://ju.outofmemory.cn/entry/141960

  https://yq.aliyun.com/articles/60096

  https://blog.csdn.net/u010900754/article/details/78167966