mongoDB(二)

MongoDB的副本集

副本集简介

所谓副本集,就是指一组服务器的集群,其中有一个主服务器,用于处理用户的请求;其余为备份服务器,用于保存主服务器的数据副本。如果主服务器崩溃了,会自动将一个备份服务器升级为新的主服务器,从而保证服务的进行。

 

MongoDB提供复制的功能,用来将数据保存到多台服务器上,在实际生产环境中,强烈建议集群并使用复制的功能,以实现故障转移和健壮服务。

 

n 创建副本集 

1:先创建几个存放数据的文件夹,比如在前面的dbs下面创建db1,db2,db3; 同理在前面的logs下面创建logs1,logs2,logs3

2:在启动MongoDB服务器的时候,使用--replSet 副本集名称

选项,如:

./mongod --dbpath ../dbs/db1 --logpath ../logs/log1 --port 20001 --fork --replSet myrepl

3:然后再启动两个,端口分别为20002和20003,当然要修改相应的数据文件路径和日志路径,副本集名称跟上面一样

4:连接到副本集1,进行副本集的初始化

rs.initiate(

{_id:"myrepl",members:[

{_id:0,host:'127.0.0.1:20001'},

{_id:1,host:'127.0.0.1:20002'},

 {_id:2,host:'127.0.0.1:20003'}

] })

 

5:察看副本集状态

rs.status(); 或者 rs.config(); 或者 db.printReplicationInfo();

6:几点说明:

(1)客户端的读写请求,都是发送到主节点进行操作

(2)客户端不能在备份节点上进行写请求

(3)默认情况下,客户端不能从备份节点读取数据,可以通过显示的执行如下语句来允许读:db.getMongo().setSlaveOk();

至此就创建好了副本集,可以进行使用测试了。

 

rs辅助函数

rs是一个全局变量,其中包含了与复制相关的辅助函数,可以通过rs.help()查看。

rs.status()说明:

rs.status函数对应的命令是replSetGetStatus,返回的信息中,

主要的字段说明:

1:self:这个字段只会出现在运行rs.status函数的成员信息中

2:stateStr:服务器状态,状态选项在后面讲心跳的时候有

3:uptime:从成员可达一直到现在所经历的时间,单位是秒

4:optimeDate:每个成员的oplog中最后一个操作发生的时间。

注意:这里的状态是每个成员通过心跳报告上来的状态,所以optime跟实际时间可能会有几秒钟的偏差

5:lastHeartbeat:当前服务器最后一次收到其他成员心跳的时间,如果网络故障,或者是当 前服务器比较繁忙,这个时间可能会是2秒钟之前

6:pingMs:心跳从当前服务器到达某个成员所花费的平均时间

7:errmsg:成员在心跳请求中返回的状态信息,通常是一些状态信息,而不是错误信息。

8:state:也表示服务器的状态,state是内部表示,而stateStr是适合阅读的表示

9:optime:和optimeDate也是一样的,只是optimeDate更适合阅读

10:syncingTo:表示当前成员正在从哪个成员处进行复制

 

修改副本集配置

1:从副本集中删除成员:rs.remove(“ip:port”)

2:为副本集添加成员:rs.add(“ip:port”);

 

副本集中主节点的确定

  是通过选举机制,要大多数的节点同意的节点才能成为主节点。

成员配置选项——选举仲裁者

所谓仲裁者,就是不保存数据,专门用来投票选举主节点的副本,以解决副本个数为偶数的情况。

1:启动仲裁者和普通的副本方式一样

2:只是配置该节点的时候,设置: arbiterOnly:true ,

如果用rs来加入的话,应该是: rs.addArb(“ip:port”);

3:最多只能有一个仲裁者

4:尽量使用奇数个成员,而不是采用仲裁者

 

成员配置选项——优先级

优先级用来表示一个成员渴望成为主节点的程度,可以在0-100之间,默认是1。

1:如果优先级为0的话,表示这个成员永远不能够成为主节点。

2:拥有最高优先级的成员会优先选举为主节点,只要它能得到“大多数”的票,并且数据是最新的,就可以了 3:如果一个高优先级的成员,数据又是最新的,通常会使得当前的主节点自动退位,让这个优先级高的做主节点

 

成员配置选项——隐藏成员

对于设置为隐藏的成员,客户端不能发送请求,也不会作为复制源,通常用来做备份服务器,

方式是:

1:在配置这个节点的时候,设置hidden:true

2:只有优先级为0的成员才能被隐藏

3:可以通过rs.config()或者rs.status()查看到

 

成员配置选项——延迟备份节点

可以通过slaveDelay设置一个延迟的备份节点,以在主节点的数据不小心被破坏过后,能从备份中恢复回来。要求该备份节点的优先级为0。

 

成员配置选项——创建索引

如果不需要备份节点与主节点拥有一样的索引,可以设置:buildIndexes:false

1:这是一个永久选项,一旦指定了,就无法恢复为创建索引的正常的节点了

2:如果要恢复,只能删掉,重新再创建节点了

3:同样要求该节点的优先级为0

 

MongoDB副本集的基本原理

操作日志oplog Oplog是主节点的local数据库中的一个固定集合,按顺序记录了主节点的每一次写操作,MongoDB的复制功能是使用oplog来实现的,备份节点通过查询这个集合就可以知道需要进行哪些数据的复制了。

 

每个备份节点也都维护着自己的oplog,记录着每次从主节点复制数据的操作。这样每个节点都可以作为数据的同步源提供给其他成员使用。

注意几点:

1:由于是先复制数据,再写日志,因此可能会出现重复的复制操作,这个没有关系,MongoDB会处理这种情况,多次执行Oplog中同一个操作与执行一次是一样的。

2:oplog的大小是固定的,它只能保存特定数量的操作日志,如果多次大批量的执行操作,oplog很快就会被填满,oplog的大小,可以通过mongod的--plogSize来指定

3:oplog的内容,在local数据库的oplog.rs集合里面

 

初始化同步

当副本集中的一个成员启动的时候,它就会检查自身状态,然后到集群中检查是否需要同步,以及从哪儿同步,并进行数据的复制,这个过程就称为初始化同步。

 

大致步骤如下:

1:记录前的准备工作:选择一个成员作为同步源,在local.me中为自己创建一个标识符,删除所有已存在的数据库,以全新的状态开始同步

2:从数据源把所有数据复制到本地

3:写oplog,把所有复制中的操作写入oplog

4:把上一步oplog同步中的操作记录下来

5:此时数据应该和数据源一致了,可以开始创建索引了

6:如果当前节点的数据仍然远远落后于数据源,那么oplog会将创建索引期间的所有操作同步过来

7:完成初始化同步后,切换到普通同步状态,也就可以作为备份节点使用了

 

处理陈旧数据 如果备份节点远远落后于同步源的数据,那么这个节点上的数据就是成旧数据。

通常的处理办法是:

让主节点使用比较大的oplog,以保存足够多的操作日志,可以让备份节点慢慢来同步操作。

 

心跳

为了维护集合的最新状态,每个成员每隔2秒会向其他成员发送一个心跳请求,用于

检查每个成员的状态。常见状态如下:

1:主节点、备份节点

2:STARTUP:成员刚启动时处于这个状态,加载完副本集配置后,进入STARTUP2

3:STARTUP2:整个初始化同步过程都处于这个状态

4:RECOVERING:表明成员运转正常,但暂时还不能处理读取请求

5:ARBITER:仲裁者始终处于这个状态

 

系统出问题的话,会处于如下状态:

1:DOWN:成员不可到达,有可能成员还是在正常运行,也有可能挂了

2:UNKNOWN:标识一个成员无法到达其他所有的成员

3:REMOVED:成员被移出副本集

4:ROLLBACK:成员正在进行数据回滚

5:FATAL:成员出现了致命的错误,也不再尝试恢复正常

 

回滚

回滚的时候,会把受到影响的文档,保存到数据目录下的rollback目录中,文件名为集合名.bson,

基本步骤是:

1:判断是否需要回滚,典型就是副本记录和主记录不一致了

2:在两个oplog中寻找最后一个共同的点

3:然后副本回滚到这个共同的点

4:副本再从主节点同步数据回来

注意:

如果要回滚的数据很多,比如大于300M,或者需要操作30分钟以上,回滚就会失

  败。如果回滚失败的话,就需要重新同步了。

MongoDB副本集的管理

以单机模式启动成员

由于很多维护的工作需要写入操作,所以不合适在副本集中操作,可以以单机模式启动成员,也就是不要使用副本的选项,就跟以前启动单独的服务器一样。一般使用一个跟副本集配置中不一样的端口号,这样其他成员会认为这个服务器挂了。

 

副本集的配置

副本集的配置以一个文档的形式保存在local.system.replSet集合中,副本集中所有成员的这个文档都是相同的。绝对不要使用update来更新这个文档,应该使用rs或者replSetReconfig命令来修改副本集的配置。

1:创建副本集,这个前面学过,就是启动所有的成员服务器后,使用rs.initiate命令

2:修改副本集成员,前面也学过一些,比如rs.add(),rs.remove()命令,还可以使用rs.reconfig(config)命令,比如修改第一个成员的host名称,

示例如下:

var config = rs.config();

config.members[0].host=“newHost:port”;

rs.reconfig(config);

修改其他的也一样,先rs.config()得到当前配置,然后修改数据,再rs.reconfig 就OK了,

但有如下几个限制:

(1):不能修改成员的”_id”字段

(2):不能将接收rs.reconfig命令的成员的优先级设置为0

(3):不能将仲裁者变成正常成员,反之也不行

(4):不能将buildIndexes为false的成员修改为true

 

创建比较大的副本集

副本集最多只能拥有12个成员,只有7个成员拥有投票权。因此要创建超过7个副本集的话,需要将其他成员的投票权设置为0,例如: rs.add({“_id”:8,”host”:”localhost:20008”,”votes”:0});

 

如果要配置超过12个成员的话,需要使用Master/Slave的方式,不过这个方式已经不建议使用了,如果将来副本集能支持更多成员的话,这个方式可能会立即废除。

 

强制重新配置 如果副本集无法达到“大多数”要求的话,可能会无法选出主节点,这个时候,可以shell连接任意一个成员,然后使用force选项强制重新配置,示例如下: rs.reconfig(config,{“force”:true});

 

把主节点变为备份节点可以使用stepDown函数,可以自己指定退化的持续时间,示例如下: rs.stepDown(); 或者 rs.stepDown(60); //秒为单位

 

阻止选举 如果对主节点进行维护,但不希望这段时间其他节点选举新的主节点,可以在每个备份节点上执行freeze命令,强制他们始终处于备份状态。例如: rs.freeze(100); //秒为单位,表示冻结多长时间 如果在主节点上执行rs.freeze(0);,可以将退位的主节点重新变为主节点。

 

使用维护模式 当在副本集的某个成员上执行一个非常耗时的功能的话,可以设置该成员进入维护模式,方式如下:db.adminCommand({“replSetMaintenanceMode”:true}); 要从维护模式中恢复的话,设置为false就可以了。

 

不作为复制源 如果希望都从主节点复制数据,可以把所有的备份成员的allowChaining设置为false

MongoDB主从复制

简介

主从复制是MongoDB中一种常用的复制方式。这种方式非常灵活,可用于备份、故障恢复、读扩展等。 最基本的设置方式就是建立一个主节点和一个或者多个从节点,每个从节点要知道主节点的地址。

 

配置方式

1:主节点 启动的时候加个--master

2:从节点

启动的时候加上--slave 和 --source来指定主节点的ip和端口

 

添加和删除源 除了可以在启动从节点的时候,制定source外,也可以在从节点的local数据库的sources集合里面添加主节点的信息,如: db.sources.insert({“host”:”ip:port”});

不用了,就remove就好了

 

MongoDB的分片

简介

所谓分片,指的就是把数据拆分,将其分散到不同机器上的过程。MongoDB支持自动分片,对应用而言,好像始终和一个单机的服务器交互一样。

分片和复制

复制是让多台服务器拥有相同的数据副本,而分片是每个分片都拥有整个数据集的 一个子集,且相互是不同的数据,多个分片的数据合起来构成整个数据集。

 

Mongos 用来执行客户端访问集群数据的路由,它维护着一个内容列表,记录了每个分片包含的数据,应用程序只要连接上它,就跟操作单台服务器一样。

 

配置服务器

配置服务器就是普通的mongod服务器,保存整个集群和分片的元数据:

集群中有哪些分片,分片的是哪些集合,以及数据块的分布。它极其重要,必须启用日志功能。

在大型的集群中,建议配置3台配置服务器,就足够用了。

 

启动配置服务器的方式:

1: 先创建几个存放数据的文件夹,比如在前面的dbs下面创建confdb文件夹,然后在confdb下面创建confdb1,confdb2,confdb3文件夹; 同理在前面的logs下面创建conflogs文件夹

2: 然后分别启动这三个配置服务器,使用—configsvr指明是配置服务器,

如下: ./mongod --configsvr --dbpath ../dbs/confdb/confdb1 --

logpath ../logs/conflog/conflog1 --fork --port 30001

3:--configsvr默认的端口为27019,默认的数据目录为/data/configdb,可以使用--dbpath 和--port自己定义。

4:注意不要使用--replSet选项,配置服务器不是副本集成员。Mongos会向所有的3台配置服务器发送写请求,并确保3台服务器拥有相同的数据。

 

启动mongos进程

1:./mongos --configdb localhost:30001,localhost:30002,localhost:30003 --

logpath ../logs/conflog/mongoslog --fork

2:可以启动任意多个mongos,通常是一个应用服务器使用一个mongos,也就是说mongos通常 与应用服务器运行在一个机器上

3:mongos的默认端口是27017,可以用chunkSize来指定块的大小,默认是200M

 

将副本集转换成为分片

1:如果没有副本集,按照前面讲的创建并初始化一个;如果有一个副本集,就打开相应的服务器,把副本集运行起来

2:use admin 也就是切换到使用admin的数据库

3:然后连接到mongos,把副本集转换成为分片,示例如下:

sh.addShard("myrepl/127.0.0.1:20001,127.0.0.1:20002"); 不用把所有副本集的成员都写出来,mongos会自动检查整个副本集。副本集的名称 myrepl就用作了分片的名称。

4:使用sh.status();察看状态,会发现整个副本集里面的服务都加入进来了。

5:注意:添加分片过后,客户端应该连接mongos进行操作,而不是连接副本集了。

6:也可以创建单mongod服务器的分片,但不建议在生产环境中使用

7:至此一个分片就创建好了,然后可以重复步骤,创建一个新的副本集,加入到分片中来

 

数据分片

需要明确指定分片的数据库和集合,MongoDB才会对数据进行自动分片。

1:对数据库启用分片 sh.enableSharding(“数据库名”);

2:然后指定分片的集合,还有分片的键,如果对已经存在的集合进行分片,那么指定的这个分片键上必须有索引;如果集合不存在,mongos会自动在分片键上创建索引。

例如: sh.shardCollection("rep1.users",{"userId":1});

 

块(chunk)

MongoDB将文档分组成为块,每个块由给定片键特定范围内的文档组成,一个块只存在于一个分片上,所以MongoDB用一个较小的表就能维护块和分片的映射关系。 块与块间的数据不能重复,因此不能使用数组来作为片键,因为MongoDB会为数组创建多个索引条目,从而导致同一数据在多个块中出现。
注意:块是个逻辑概念,而非物理存储实现

查看块信息 块信息保存在config.chunks集合中,sh.status()里面也带有分块的信息。 如果单个分片键可能重复的话,可以创建复合分片键,方式跟创建复合索引一样。

 

拆分块 mongos会记录每个块中插入了多少数据,如果一个块的数据大小超出了块的大小,

或者达到某个阀值(拆分点), MongoDB会自动的拆分这个块。

拆分的时候,配置服务器会创建新的块文档,同时修改旧的块范围。进行拆分的时候,所有的配置服务器都必须可以到达,否则不会进行拆分,此时就会造成“拆分风暴”,也就是mongos不断发起拆分请求,却无法拆分的情况。唯一的解决办法就是保证配 置服务器的健康和稳定。

 

均衡器(balancer) 均衡器会周期性的检查分片间是否存在不均衡,如果存在,则开始块的迁移。不均衡的表现是:一个分片明显比其他分片拥有更多的块。

1:均衡器可以是任意一个mongos

2:每隔几秒钟,mongos就会尝试成为均衡器,如果没有其他可用的均衡器,mongos就会对整个集群加锁,以防止配置服务器对集群进行修改,然后做一次均衡操作

3:均衡不会影响mongos的正常路由操作,因此对客户端没有任何影响

4:可以查看config.locks集合,看看哪一个mongos是均衡器,

语句如下; db.locks.findOne({"_id":"balancer"});

(1)_id为balancer的文档就是均衡器

(2)字段who表示当前或者曾经作为均衡器的mongos

(3)字段state表示均衡器是否在运行,0非活动,2正在均衡,1正在尝试得到锁,一般不会看到状态1

 

限制分片大小 默认情况下,MongoDB会在分片间均匀的分配数据,但是,如果服务器配置严重失衡,比如某些机器配置非常高,而其他机器只是普通机器,那么就应该使用maxSize选项, 来指定分片能增长到的最大存储容量,单位是MB。

例如:db.runCommand({“addShard”:”myrepl/ip:port”,“maxSize”:1000});

注意maxSize更像是一个建议而非规定,MongoDB不会从maxSize处截断一个分片,也不会阻止分片增加数据,而是会停止将数据移动到该分片,当然也可能会移走一部分数据。因此可以理解这个maxSize是一个建议或者提示。

 

注意集群对数据的影响

1:如果在从机上查询数据,就必须要可接受过时数据

2:使用集群时,不能把整个集合看成一个“即时快照”了,这个问题很严重,

比如:

(1)在一个分片集合上,对数据进行计数,很有可能得到的是比实际文档多的数据,因为有可能数据正在移动复制

(2)唯一索引也无法强制保证了

(3)更新操作也面临问题,无法确保在多个分片间只发生一次,因此要更新单个文档,一定要在条件中使用片键,否则会出现问题

 

理解MongoDB分片数据的分配方式 —— 一分片多区间

当需要进行迁移的时候,将持有过多数据的分片上的块分割,使得分割出来的区间,刚好满足迁移需要,然后再进行迁移。

比如:

如果shard 1中存有[a, f]区间的数据,数据量为500G,此时需要从shard 1上面迁移100G到shard 4,以保证数据的均匀分布。经统计,shard 1中的[a, d]段的数据为400G,[d, f]段数据为100G,因此将shard 1中的[d, f] 段的数据直接迁移到shard 4上面。同理,需要从shard 2中迁移100G的数据到shard 3中。 这种迁移方式的数据迁移量是理论上的最小值。

 

 

MongoDB分片的片键选择

片键选择的重要性

所谓片键,就是用来拆分数据的字段,通常为1-2个字段,由于片键一旦确定,并已经分片过后,基本上就不可能再修改片键了,因此初期设计和选择就非常重要了  

片键规则

1:不可以是数组

2:一旦插入了文档,片键不可修改,要修改就必须先删除文档,然后才能修改片键

3:大多数特殊类型的索引都不能作为片键

4:片键的数据取值应该是多样的,这样才利于分片

 

片键的几种类型

1:小基数片键 就是片键可取的值非常少,所以叫小基数。通常这不是个好方式,因为:片键有N个 值,也就最多只能有N个块,也就是最多只能N个分片。

这也意味着当某个块越来越大的时候,MongoDB无法拆分块,因此你什么也干不了, 除了购买更大的硬盘。

2:升序片键 就是片键值是不断增加的,类似于自增字段。通常这不是个好方式,因为:

(1)新加入的数据始终会加入最后一个块,即所有数据都被添加到一个块上,从而导致了热点必然存在,且是单一,不可分割的热点。

(2)由于数据始终会先加入到最大块,会导致最大块需要不断的拆分出新的小块

(3)会导致数据均衡处理很困难,因为所有的新快都是由同一个分片创建的

3:随机分发的片键

就是片键值是随机散列的数据,这种方式对数据的均衡是有好处的,数据加载速度 也很快,缺点是:如果需要按照片键值进行范围查找的话,就必须到所有分片上执行了。

    创建一个散列片键,需要先创建散列索引,示例如下:
db.users.ensureIndex({“userId”:”hashed”}); 然后对集合分片,示例如下: 

sh.shardCollection(“mydb.users”,{“userId”:”hashed”});

4:基于某个业务的片键

这个就要具体问题具体分析和选择了,比如:选择用户IP,电话号码段,或者是自定义的编码段等。如果要指定特定范围的块出现在特定的分片中,可以为分片添加tag,然后为块指定tag,例如:

sh.addShardTag(“myrep2”,“gtu”) 然后: sh.addTagRange("mydb.users",{"userId":"v0"},{"userId":"v9"},"gtu"); 就可以设定v0-v9的数据存放到myrep2这个分片里面去

(1)在设置范围的时候,可以使用:ObjectId()、MinKey、MaxKey 等来作为值

(2)不用某个tag了可以删除:sh.removeShardTag(“myrep2”,“gtu”);

 

好片键的建议

实际项目中,建议尽量采用 准升序键加查询键 构成组合片键。 其中升序键的每个值最好能对应几十到几百个数据块,而查询键则是应用程序通常都回依据其进行查询的字段。

例如:某应用,用户会定期访问过去一个月的数据,就可以在{month:1,user:1}上

进行分片,month是一个粗粒度的升序字段,每个月都会增大;而user是经常会查询某特定用户数据的查询字段。 1:注意一点:查询键不可以是升序字段,否则该片键会退化成为一个升序片键,照样会面临热点问题

2:通常通过 准升序键来控制数据局部化,而查询键则是应用上常用的查询字段

 

MongoDB分片的管理

列出所有的Shard 可以用:db.runCommand({“listshards”:1});

查看分片信息 使用函数:printShardingStatus()

判断是否分片 使用:db.runCommand({isdbgrid:1});返回ok为1的就是分片

查看集群信息摘要 sh.status()命令可以查看分片、数据库和分片集合的摘要信息,如果块的数量少的话,还会给出块的信息,否则它只给出集合的片键,以及每个分片的块数。

 

查看连接统计

可使用connPoolStats命令来查看mongos和mongod之间的连接信息,如: db.adminCommand({"connPoolStats":1});

 

检查配置信息

1:config.shards :记录着所有分片的信息

2:config.databases :记录集群中所有数据库的信息

3:config.collections :记录所有分片集合的信息

4:config.chunks :记录集合中所有块的信息

5:config.changelog :记录集群的操作

6:config.tags :记录分片的标签

 

限制连接数量 一个mongos或者mongod最多允许20000个连接,可在mongos的命令行配置中使用maxConns选项来控制mongos能创建的连接数量。

 

添加分片服务器,这个前面学过了,就是addShard命令

 

删除分片

通常来说,不应从集群中删除分片,即使加多了,也可以留在那儿,以后会用得上,如果非要删除分片的话,可以按照如下操作:

1:首先保证均衡器是打开的,因为删除分片的时候,均衡器会负责将待删除分片的数据迁移至其它分片

2:执行removeShard命令,示例如下:

use admin 最好切换到mongos的admin数据库再操作,然后: db.runCommand({"removeShard":"myrep2"}); 如需要查看删除情况,再次执行上一条命令,直到remaining的chunks为0。

3:所有块完成转移过后,如果仍有数据库将该分片作为主分片,需要在删除分片前将这 些数据库移除掉,通常会提示:“note” : “you need to drop or movePrimary these databases”,

示例如下: db.adminCommand({"movePrimary":"mydb2","to":"myrep1"});

4:然后再次执行removeShard命令,直到状态显示completed

监控应用状态

查看正在进行的操作

使用db.currentOp()命令,该函数会列出数据库正在进行的所有操作,其中一些字段:

1:opid:操作的唯一标识符

2:active:该操作是否正在运行

3:secs_running:该操作已经执行的时间

4:op:操作的类型

5:desc:日志中与此连接相关的每一条记录都会以[conn3]为前缀,因此可以此来筛选日志

6:locs:描述该操作使用的锁的类型,其中“^”表示全局锁

7:waitingForLock:表示该操作是否因正在等待锁而处于阻塞状态

8:numYields:表示该操作交出锁,而使其他操作得以运行的次数

9:lockstats.timeAcquiringMicros:表示该操作需要多长时间才能取得所需的锁

 

另外,在执行db.currentOp的时候,还可以加入过滤条件,从而只显示符合条件的结果, 比如:db.currentOp({“ns”:”mydb.users”});只查询该命名空间的操作

通常用这个命令来查找耗时的操作,但是,所有跟复制、分片等相关的操作,都应该忽略 掉,即使慢也不要去动,更不要去终止他们。

 

终止操作的执行可以用db.killOp(opid);,其中的opid可以通过上面的命令来查看。

 

系统分析器system.profile 系统分析器可记录特殊集合system.profile中的操作,并提供大量有关耗时过长的操作信息,但相应的,系统的整体性能会有所下降,因此默认是关闭的

1:开启系统分析器:db.setProfilingLevel(级别,自定义耗时长);

设置为1级,默认会记录耗时大于100ms的操作,也可以自定义时长

设置为2级,分析器就会记录所有内容 ;

设置为0级,将会关闭系统分析器

2:通过查看system.profile集合的内容,在那个数据库上开启,就在那个数据库里面查看

获取服务器统计信息:db.serverStatus();

 

计算空间消耗

1:使用Object.bsonsize()来计算文档的大小,比如: Object.bsonsize(db.users.findOne(可以加条件));

2:使用stats函数来显示一个集合的信息,

比如: db.users.stats(比例因子); 可以传入比例因子,如1024表示k,1024*1024表示M

3:使用stats函数显示数据库的信息,跟上面一个类似

 

使用mongotop和mongostat

这都是MongoDB自带的命令行工具,通过每隔几秒输出当前状态,以监控数据库

 

mongotop类似于Unix的top命令。

mongostat默认每秒输出一次包含当前状态的列表,大致输出如下信息: 1:insert/query/update/delete/getmore/command :每种操作发生的次数

2:flushes:mongod将数据flush到磁盘的次数

3:mapped:mongod所映射的内存数量,通常约等于数据目录的大小

4:vsize:mongod正在使用的虚拟内存大小,通常为数据目录的2倍,一次用于映射的文件,一次用于日志记录

5:res:mongod正在使用的内存大小,通常该值应尽量接近机器所有内存的大小

6:locked db:在上一个时间片里,锁定时间最长的数据库,该百分比是根据数据库被锁定的时间和全局锁的锁定时间来计算的,因此有可能超过100%

7:idx miss %:有所少索引在访问中发生了缺页中断,即索引入口不在内存中,使得mongod必须到磁盘进行读取 8:qr|qw:读写操作的队列大小,即有多少读写操作在阻塞队列中,等待被处理

9:ar|aw:活动客户端的数量

10:netIn:通过网络输入的字节数

11:netOut:通过网络输出的字节数

12:conn:此服务器打开的连接数

13:time:指以上统计信息的时间

 

备份和恢复

文件系统快照(snapshot) 生成文件系统快照这个方法需要满足两个条件:

1:文件系统本身支持快照技术

2:运行mongod时必须开启日记系统

其恢复就是快照的恢复,当然,恢复的时候,确保mongod没有开启复制文件系统

 

冷备份:就是把mongod停了,然后拷贝相应的数据文件,最好是把一个数据库相应 的数据文件夹完整的拷出,然后恢复的时候完整的拷入。

热备份:就是mongod在运行中,可以使用db.fsyncLock();来锁定数据库,然后进行数据文件的拷贝,拷贝完成后,使用db.fsyncUnLock();解除锁定。恢复的时候,需要把 mongod停了,然后把文件拷入。

 

使用mongodump来备份 这个方式有些缺点,比如速度慢,处理副本集的时候也很容易出问题。但对于单独

的数据库和集合还是一个好选择的。

1:如果mongod在运行,只要指定mongod的端口即可:mongodump --port 20001,会在当前路径下创建dump文件夹,里面存放dump的数据库,真正的数据在.bson文件里。

2:也可以-h指定主机和端口,-d指定数据库名字,-o指定输出的路经

3:如果mongod没有运行,使用--dbpath指定数据目录也可以 n 使用mongorestore来恢复

./mongorestore --port 20000 --drop dump/ 其中的--drop指定要删除服务器上的数据, dump/是存放备份数据的文件夹 当然,也可以-h指定主机和端口,-d指定数据库名字,-directoryperdb指 定备份文件所在的位置,也可不配置这个,直接指定就好了

 

对副本集进行备份 建议对备份节点进行备份,最好是采用复制文件系统的方式。

对分片集群进行备份 由于不可能对集群在某一时间点的完整状态快照,因此不太可能对正在运行的分片集群进行“完美的”备份。因此对于分片集群进行备份恢复,更关注单个分片或者副本集的备份。
    注意:要进行分片集备份,需要先关闭均衡器。

数据导入导出

使用mongoexport来导出数据,示例

./mongoexport -d mydb -c users -o ../databak/mydb.bak

1:-d :指定要导出的数据库

2:-c :指定要导出的集合

3:-o :指定输出的数据文件 还可以指定导出成csv格式的,比如:

./mongoexport -d mydb -c users -csv -f userId,name -o ../databak/mydb.csv 1:-csv :指定要导出成csv格式

2:-f :指定要导出的列

使用mongoimport来导入数据,示例

./mongoimport -d mydb -c users --file ../databak/mydb.bak 1:--file :用来指定导入的备份文件 如果要导入csv格式的话,如下:

./mongoimport -d mydb -c users --type csv --headerline -- file ../databak/mydb.csv

 

MongoDB应用建议及最佳实践

尽量在64位机器上使用MongoDB,当然32位机器也能用,但由于MongoDB使用内存映射文件,32位版本只支持2G数据的存储

 

避免存储过大的文件,MongoDB限制单个文档是16M,弱化数据结构模型并不等于没有数据结构模型,同样要合理设计

MongoDB没有Join语句,因此要合理的设计数据结构

复制集成员尽量使用奇数个,如果是偶数个,可以用仲裁者凑数

最好启动Journal

MongoDB使用内存映射文件并且每60秒向磁盘输出一次通知,这就意味着最大程度上你可能丢失60秒加上向硬盘输出通知这段时间内所有的数据。

为了避免数据丢失,MongoDB从2.0版本起就添加了Journaling(默认情况下开启)。

Journaling把时间从60秒更改为100ms。如果数据库意外的停机,在 启动之前它将会被重启用以确保数据库处于一致状态。当然Journaling会轻微的 影响到性能,大约5%。

 

单机使用MongoDB,应启用日志,就是journal;而多机使用就做副本集

 

关于范式化设计和反范式化设计

1:范式化设计通常是通过设计多个集合,然后用字段的值来关联数据的方式;

而反范式化设 计通常是把相关数据做成内嵌文档,嵌入到文档数据中

2:通常范式化设计能提高数据的写入速度,而反范式化设计能提高查询速度

3:通常认为适合使用内嵌数据的地方:

(1)子文档较小

(2)数据不会经常变化

(3)最终数据一致即可,对中间数据是否一致不要求

(4)需要快速读取的地方

4:通常认为适合使用引用数据的地方:

(1)子文档较大

(2)数据经常改变

(3)数据一致性要求高,包括中间数据都要求一致

(4)需要快速写入的地方

(5)适应未来的数据

5:可以混合使用引用数据和内嵌数据的方式

 

关于数据集合之间的关系

集合之间的常见关系包括:一对一、一对多和多对多。

在MongoDB中,又把多 按照数据量的大小,分成了“多”和“少”,通常少的关系使用内嵌的方式较好,多的关系使用引用的方式较好。 对于多对多的处理,通常还是会引入一个关系集合,从而拆分成两个一对多。

 

优化文档增长

如果文档中有需要不断增长的字段,那么建议把这个字段放在最后。 如果能够预估这个字段最大的长度,还可以采用手动分配的方式,在创建文档的时 候就预留足够的空间,从而避免后面移动文档,

方式如下:

1:创建文档的时候,在这个字段后面,任意添加一个字段,比如:needDelete,然后给 needDelete字段赋值,值任意,主要是长度为要增长的字段的最大长度

2:在后面更新文档的时候,就可以使用$unset来移出needDelete字段,如果字段存在就移出,不存在什么都不做

 

用数组存放要匿名访问的内嵌数据 如果确切的知道存放数据的含义,并要根据含义进行查找,用文档,否则用数组

 

MongoDB的索引代价很大

1:索引非常消耗内存,大约100万条索引要占到50M以上

2:索引对写入性能有较大的影响,索引多了,写入会非常慢 因此只有当第二个索引的查询不可避免,才值得增加额外索引。

查询的时候,应该尽量只返回需要的数据和字段,避免无谓的流量损耗

如果需要查询的数据超过数据的一半,就不适合使用索引了,可以用 {$natural:1}来禁用

更新数据的时候,应该尽量精细化更新,尽量准确的条件,并使用修改器直接修

改要改变的字段,不要整条替换

不要使用大量的、或是复杂的js计算,对cpu性能损耗较大

尽早分片,一般要在MongoDB占用服务器整体性能80%前分片

谨慎选择分片键,一旦正式使用,几乎不能再更改了

不可以对256G以上的集合进行分片

 

文档对象数据应该能直接满足应用的需要 MongoDB对数据基本是不做任何处理的,仅仅存取数据,因此,要获取的信息如果无法从文档中直接获取的话,

就只有:

1:在MongoDB中使用js计算,会付出高昂的性能代价

2:把大量数据取到客户端,然后在客户端进行计算

    两种方式都不会太好,因此要合理的设计文档对象,最好能直接从文档中获取应用需要的数据

不要用GridFS处理小的二进制数据 由于GridFS需要二次查询,一次获取文件元数据,一次获取其内容,因

此,GridFS更适合存放大数据的,至少是一个文档存放不下的数据,通常客户端也不用一次性加载这些数据。

 

And型查询,应该把条件最严格的条件放在前面

OR型查询,应该把最有可能为true的条件放在前面

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值