MongoDB基本介绍及一些用法
一、概念:
分片(sharding)是指将数据库拆分,将其分散在不同的机器上的过程。将数据分散到不同的机器上,不需要功能强大的服务器就可以存储更多的数据和处理更大的负载。基本思想就是将集合切成小块,这些块分散到若干片里,每个片只负责总数据的一部分,最后通过一个均衡器来对各个分片进行均衡(数据迁移)。通过一个名为mongos的路由进程进行操作,mongos知道数据和片的对应关系(通过配置服务器)。大部分使用场景都是解决磁盘空间的问题,对于写入有可能会变差(+++里面的说明+++),查询则尽量避免跨分片查询。使用分片的时机:
1,机器的磁盘不够用了。使用分片解决磁盘空间的问题。 2,单个mongod已经不能满足写数据的性能要求。通过分片让写压力分散到各个分片上面,使用分片服务器自身的资源。 3,想把大量数据放到内存里提高性能。和上面一样,通过分片使用分片服务器自身的资源。
二、部署安装: 前提是安装了mongodb(本文用3.0测试)
在搭建分片之前,先了解下分片中各个角色的作用。
① 配置服务器。是一个独立的mongod进程,保存集群和分片的元数据,即各分片包含了哪些数据的信息。最先开始建立,启用日志功能。像启动普通的mongod一样启动配置服务器,指定configsvr选项。不需要太多的空间和资源,配置服务器的1KB空间相当于真是数据的200MB。保存的只是数据的分布表。当服务不可用,则变成只读,无法分块、迁移数据。 ② 路由服务器。即mongos,起到一个路由的功能,供程序连接。本身不保存数据,在启动时从配置服务器加载集群信息,开启mongos进程需要知道配置服务器的地址,指定configdb选项。 ③ 分片服务器。是一个独立普通的mongod进程,保存数据信息。可以是一个副本集也可以是单独的一台服务器。
部署环境:3台机子
A:配置(3)、路由1、分片1;
B:分片2,路由2;
C:分片3
在部署之前先明白片键的意义,一个好的片键对分片至关重要。片键必须是一个索引,数据根据这个片键进行拆分分散。通过sh.shardCollection加会自动创建索引。一个自增的片键对写入和数据均匀分布就不是很好,因为自增的片键总会在一个分片上写入,后续达到某个阀值可能会写到别的分片。但是按照片键查询会非常高效。随机片键对数据的均匀分布效果很好。注意尽量避免在多个分片上进行查询。在所有分片上查询,mongos会对结果进行归并排序。
启动上面这些服务,因为在后台运行,所以用配置文件启动,配置文件说明。
1)配置服务器的启动。(A上开启3个,Port:20000、21000、22000)
配置服务器是一个普通的mongod进程,所以只需要新开一个实例即可。配置服务器必须开启1个或则3个,开启2个则会报错:
BadValue need either 1 or 3 configdbs
因为要放到后台用用配置文件启动,需要修改配置文件:
/etc/mongod_20000.conf
#数据目录 dbpath=/usr/local/config/
#日志文件 logpath=/var/log/mongodb/ mongodb_config.log #日志追加 logappend= true#端口 port = 20000#最大连接数 maxConns = 50pidfilepath = /var/run/ mongo_20000.pid #日志,redo log journal = true#刷写提交机制 journalCommitInterval = 200#守护进程模式 fork = true#刷写数据到日志的频率 syncdelay = 60#storageEngine =wiredTiger #操作日志,单位M oplogSize = 1000#命名空间的文件大小,默认16M,最大2G。 nssize = 16noauth = trueunixSocketPrefix = / tmp configsvr = true
/etc/mongod_21000.conf
数据目录 dbpath=/usr/local/config1/
#日志文件 logpath=/var/log/mongodb/ mongodb_config1.log #日志追加 logappend= true#端口 port = 21000#最大连接数 maxConns = 50pidfilepath = /var/run/ mongo_21000.pid #日志,redo log journal = true#刷写提交机制 journalCommitInterval = 200#守护进程模式 fork = true#刷写数据到日志的频率 syncdelay = 60#storageEngine =wiredTiger #操作日志,单位M oplogSize = 1000#命名空间的文件大小,默认16M,最大2G。 nssize = 16noauth = trueunixSocketPrefix = / tmp configsvr = true
开启配置服务器:
root@mongo1:~# mongod -f /etc/mongod_20000.conf about to fork child process, waiting until server is ready for connections. forked process: 8545 child process started successfully, parent exitingroot@mongo1:~# mongod -f /etc/mongod_21000.conf about to fork child process, waiting until server is ready for connections. forked process: 8595 child process started successfully, parent exiting
同理再起一个22000端口的配置服务器。
#数据目录 dbpath=/usr/local/config2/
#日志文件 logpath=/var/log/mongodb/ mongodb_config2.log #日志追加 logappend= true#端口 port = 22000#最大连接数 maxConns = 50pidfilepath = /var/run/ mongo_22000.pid #日志,redo log journal = true#刷写提交机制 journalCommitInterval = 200#守护进程模式 fork = true#刷写数据到日志的频率 syncdelay = 60#storageEngine =wiredTiger #操作日志,单位M oplogSize = 1000#命名空间的文件大小,默认16M,最大2G。 nssize = 16noauth = trueunixSocketPrefix = / tmp configsvr = true
View Code
2)路由服务器的启动。(A、B上各开启1个,Port:30000)
路由服务器不保存数据,把日志记录一下即可。
# mongos
#日志文件 logpath=/var/log/mongodb/ mongodb_route.log #日志追加 logappend= true#端口 port = 30000#最大连接数 maxConns = 100#绑定地址 #bind_ip= 192.168. 200.* ,..., pidfilepath = /var/run/ mongo_30000.pid configdb =192.168.200.A:20000,192.168.200.A:21000,192.168.200.A:22000 #必须是1个或则3个配置 。
#configdb=127.0.0.1:20000 #报错
#守护进程模式 fork = true
其中最重要的参数是configdb,不能在其后面带的配置服务器的地址写成localhost或则127.0.0.1,需要设置成其他分片也能访问的地址,即192.168.200.A:20000/21000/22000。否则在addshard的时候会报错:
{ "ok" : 0, "errmsg" : "can't use localhost as a shard since all shards need to communicate. either use all shards and configdbs in localhost or all in actual IPs host: 172.16.5.104:20000 isLocalHost:0" }
开启mongos:
root@mongo1:~# mongos -f /etc/mongod_30000.conf 2015-07-10T14:42:58.741+0800 W SHARDING running with 1 config server should be done only for testing purposes and is not recommended for production about to fork child process, waiting until server is ready for connections. forked process: 8965 child process started successfully, parent exiting
3)分片服务器的启动:
就是一个普通的mongod进程:
root@mongo1:~# mongod -f /etc/mongod_40000.conf note: noprealloc may hurt performance in many applications about to fork child process, waiting until server is ready for connections. forked process: 9020 child process started successfully, parent exiting
A服务器上面的服务开启完毕
root@mongo1:~# ps -ef | grep mongo root 9020 1 0 14:47 ? 00:00:06 mongod -f /etc/mongod_40000.conf root 9990 1 0 15:14 ? 00:00:02 mongod -f /etc/mongod_20000.conf root 10004 1 0 15:14 ? 00:00:01 mongod -f /etc/mongod_21000.conf root 10076 1 0 15:20 ? 00:00:00 mongod -f /etc/mongod_22000.conf root 10096 1 0 15:20 ? 00:00:00 mongos -f /etc/mongod_30000.conf
按照上面的方法再到B上开启分片服务和路由服务(配置文件一样),以及在C上开启分片服务。到此分片的配置服务器、路由服务器、分片服务器都已经部署完成。
三、配置分片:下面的操作都是在mongodb的命令行里执行
1)添加分片:sh.addShard(“IP:Port”)
登陆路由服务器mongos 操作:
root@mongo1:~# mongo --port=30000 MongoDB shell version: 3.0.4 connecting to: 127.0.0.1:30000/test mongos>
添加分片:
mongos> sh.status() #查看集群的信息 --- Sharding Status --- sharding version: {"_id" : 1,"minCompatibleVersion" : 5,"currentVersion" : 6,"clusterId" : ObjectId("559f72470f93270ba60b26c6") }shards:balancer:Currently enabled: yesCurrently running: noFailed balancer rounds in last 5 attempts: 0Migration Results for the last 24 hours: No recent migrationsdatabases:{ "_id" : "admin", "partitioned" : false, "primary" : "config" }mongos> sh.addShard("192.168.200.A:40000") #添加分片 { "shardAdded" : "shard0000", "ok" : 1 } mongos> sh.addShard("192.168.200.B:40000") #添加分片 { "shardAdded" : "shard0001", "ok" : 1 } mongos> sh.addShard("192.168.200.C:40000") #添加分片 { "shardAdded" : "shard0002", "ok" : 1 }mongos> sh.status() #查看集群信息 --- Sharding Status --- sharding version: {"_id" : 1,"minCompatibleVersion" : 5,"currentVersion" : 6,"clusterId" : ObjectId("559f72470f93270ba60b26c6") }shards: #分片信息{ "_id" : "shard0000", "host" : "192.168.200.A:40000" }{ "_id" : "shard0001", "host" : "192.168.200.B:40000" }{ "_id" : "shard0002", "host" : "192.168.200.C:40000" }balancer:Currently enabled: yesCurrently running: noFailed balancer rounds in last 5 attempts: 0Migration Results for the last 24 hours: No recent migrationsdatabases:{ "_id" : "admin", "partitioned" : false, "primary" : "config" }
2)开启分片功能:sh.enableSharding(“库名”)、sh.shardCollection(“库名.集合名”,{“key”:1})
mongos> sh.enableSharding("dba") #首先对数据库启用分片 { "ok" : 1 } mongos> sh.status() #查看分片信息 --- Sharding Status ---......databases:{ "_id" : "admin", "partitioned" : false, "primary" : "config" }{ "_id" : "test", "partitioned" : false, "primary" : "shard0000" }{ "_id" : "dba", "partitioned" : true, "primary" : "shard0000" }mongos> sh.shardCollection("dba.account",{"name":1}) #再对集合进行分片,name字段是片键。片键的选择:利于分块、分散写请求、查询数据。 { "collectionsharded" : "dba.account", "ok" : 1 } mongos> sh.status() --- Sharding Status ---...shards:{ "_id" : "shard0000", "host" : "192.168.200.51:40000" }{ "_id" : "shard0001", "host" : "192.168.200.52:40000" }{ "_id" : "shard0002", "host" : "192.168.200.53:40000" } ...databases:{ "_id" : "admin", "partitioned" : false, "primary" : "config" }{ "_id" : "test", "partitioned" : false, "primary" : "shard0000" } { "_id" : "dba", "partitioned" : true, "primary" : "shard0000" } #库dba.accountshard key: { "name" : 1 } #集合chunks:shard0000 1{ "name" : { "$minKey</span><span style="color: #800000;">"</span> : <span style="color: #800080;">1</span> } } -->> { <span style="color: #800000;">"</span><span style="color: #800000;">name</span><span style="color: #800000;">"</span> : { <span style="color: #800000;">"</span><span style="color: #800000;">$maxKey" : 1 } } on : shard0000 Timestamp(1, 0)
上面加粗部分表示分片信息已经配置完成。要是出现:
too many chunks to print, use verbose if you want to force print
想要看到详细的信息则需要执行:
mongos> sh.status({"verbose":1}) 或则 mongos> db.printShardingStatus("vvvv") 或则 mongos> printShardingStatus(db.getSisterDB("config"),1)
四、测试 :对dba库的account集合进行测试,随机写入,查看是否分散到3个分片中。
判断是否为shard:db.runCommand({isdbgrid:1})
mongos> db.runCommand({isdbgrid:1}) { "isdbgrid" : 1, "hostname" : "mongo3c", "ok" : 1 }
通过一个python脚本进行随机写入:分别向A、B 2个mongos各写入10万条记录。
#!/usr/bin/env python
# -*- coding:utf-8 -*-# 随即写MongoDB Shard 测试 importpymongo importtime from random importRandom def random_str(randomlength=8 ): str = ”chars = ‘ AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz0123456789 ’length = len(chars) - 1random =Random() for i inrange(randomlength): str+= chars[random.randint(0, length)] returnstr definc_data(conn): db =conn.dba #db = conn.test collection =db.account for i in range(100000 ): str = ”chars = ‘ AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz0123456789 ’length = len(chars) - 1random =Random() for i in range(15 ): str+= chars[random.randint(0, length)] string =str collection.insert({” name ” : string, “ age ” : 123+i, “ address ” : “ hangzhou ”+ string}) if __name__ == ’ __main__ ’ : conn = pymongo.MongoClient(host= ’ 192.168.200.A/B ’,port=30000 ) StartTime =time.time() print “ ===============$inc=============== ” print “ StartTime : %s ” % StartTime inc_data(conn) EndTime =time.time() print “ EndTime : %s ” % EndTime CostTime = round(EndTime- StartTime) print “ CostTime : %s ” %CostTime
View Code
查看是否分片:db.collection.stats()
mongos> db.account.stats() #查看集合的分布情况 ......"shards" : {"shard0000" : {"ns" : "dba.account","count" : 89710,"size" : 10047520, ......"shard0001" : {"ns" : "dba.account","count" : 19273,"size" : 2158576, ......"shard0002" : {"ns" : "dba.account","count" : 91017,"size" : 10193904, ......
上面加粗部分为集合的基本信息,可以看到分片成功,各个分片都有数据(count)。到此MongoDB分片集群搭建成功。
++++++++++++++++++++++++++++++++++++++++++++++++
感兴趣的同学可以看下面这个比较有趣的现象:
#在写之前分片的基本信息: mongos> sh.status() --- Sharding Status --- ... ...databases:{ "_id" : "admin", "partitioned" : false, "primary" : "config" }{ "_id" : "test", "partitioned" : false, "primary" : "shard0000" }{ "_id" : "dba", "partitioned" : true, "primary" : "shard0000" }dba.accountshard key: { "name" : 1 }chunks:shard0000 1{ "name" : { "$minKey</span><span style="color: #800000;">"</span> : <span style="color: #800080;">1</span> } } -->> { <span style="color: #800000;">"</span><span style="color: #800000;">name</span><span style="color: #800000;">"</span> : { <span style="color: #800000;">"</span><span style="color: #800000;">$maxKey" : 1 } } on : shard0000 Timestamp(1, 0) #可以看到这里片键的写入,都是写在shard0000里面的。
#在写期间的分片基本信息: mongos> sh .status() --- Sharding Status ---... ... databases: { " _id " : " admin ", " partitioned " : false, " primary " : " config "} { " _id " : " test ", " partitioned " : false, " primary " : " shard0000 "} { " _id " : " dba ", " partitioned " : true, " primary " : " shard0000 "} dba.account shard key: { " name " : 1} chunks: #数据块分布 shard0000 1 shard0001 1 shard0002 1 { "name" : { "$minKey" : 1 } } -->> { "name" : "5yyfY8mmR5HyhGJ" } on : shard0001 Timestamp(2, 0) { "name" : "5yyfY8mmR5HyhGJ" } -->> { "name" : "woQAv99Pq1FVoMX" } on : shard0002 Timestamp(3, 0) { "name" : "woQAv99Pq1FVoMX" } -->> { "name" : { "$maxKey" : 1 } } on : shard0000 Timestamp(3, 1 ) #可以看到片键写入的基本分布 #在写完成后的基本信息: mongos> sh .status() --- Sharding Status ---... ... databases: { " _id " : " admin ", " partitioned " : false, " primary " : " config "} { " _id " : " test ", " partitioned " : false, " primary " : " shard0000 "} { " _id " : " dba ", " partitioned " : true, " primary " : " shard0000 "} dba.account shard key: { " name " : 1} chunks: #数据块分布 shard0000 2 shard0001 1 shard0002 2 { "name" : { "$minKey" : 1 } } -->> { "name" : "5yyfY8mmR5HyhGJ" } on : shard0001 Timestamp(2, 0) { "name" : "5yyfY8mmR5HyhGJ" } -->> { "name" : "UavMbMlfszZOFrz" } on : shard0000 Timestamp(4, 0) { "name" : "UavMbMlfszZOFrz" } -->> { "name" : "t9LyVSNXDmf6esP" } on : shard0002 Timestamp(4, 1) { "name" : "t9LyVSNXDmf6esP" } -->> { "name" : "woQAv99Pq1FVoMX" } on : shard0002 Timestamp(3, 4) { "name" : "woQAv99Pq1FVoMX" } -->> { "name" : { "$maxKey" : 1 } } on : shard0000 Timestamp(3, 1) #最后片键写入的分布
上面加粗的信息对比上看到,本来在每个分片上都只有一个块,最后在shard0000、shard0002上有2个块,被拆分了。shard0001不变。这是因为mongos在收到写请求的时候,会检查当前块的拆分阀值点。到达该阀值的时候,会向分片发起一个拆分的请求。例子中shard0000和shard0002里的块被拆分了。分片内的数据进行了迁移(有一定的消耗),最后通过一个均衡器来对数据进行转移分配。所以在写入途中要是看到一个分片中集合的数量变小也是正常的。
balancer: #均衡器Currently enabled: yesCurrently running: yes #正在转移Balancer lock taken at Fri Jul 10 2015 22:57:27 GMT+0800 (CST) by mongo2:30000:1436540125:1804289383:Balancer:846930886
均衡器:均衡器负责数据迁移,周期性的检查分片是否存在不均衡,如果不存在则会开始块的迁移,config.locks集合里的state表示均衡器是否找正在运行,0表示非活动状态,2表示正在均衡。均衡迁移数据的过程会增加系统的负载:目标分片必须查询源分片的所有文档,将文档插入目标分片中,再清除源分片的数据。可以关闭均衡器(不建议):关闭会导致各分片数据分布不均衡,磁盘空间得不到有效的利用。
mongos> sh.setBalancerState(false) #关闭自动均衡器,手动均衡,打开:sh.setBalancerState(true) mongos> db.settings.find() #查看均衡器状态 { "_id" : "balancer", "stopped" : true }
可以为均衡器设置一个均衡时间窗口:activeWindow
mongos> db.settings.update({"_id":"balancer"},{"$set":{"activeWindow":{"start":"08:00","stop":"02:00"}}},true) WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 }) mongos> db.settings.find({"_id":"balancer"}) { "_id" : "balancer", "stopped" : false, "activeWindow" : { "start" : "08:00", "stop" : "02:00" } }
上面说明:均衡只会在早上8点到凌晨2点进行均衡操作。均衡器是以块的数量作为迁移指标,而非数据大小,块的大小默认是64M,可以修改:(config.settings)
mongos> db.settings.find() { "_id" : "chunksize", "value" : 64 } mongos> db.settings.save({"_id":"chunksize","value":32}) WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 }) mongos> db.settings.find() { "_id" : "chunksize", "value" : 32 }
上面把块的默认大小改成了32M,除了通过均衡器自动迁移外,还可以手动迁移数据:sh.moveChunk(“db.collection”,{块地址},”新片名称”)
mongos> db.chunks.find({"_id" : "abc.account-name_\"wPeFnJEvendSTbH\""}).pretty() #先到config.chunks上任意找一个块 {"_id" : "abc.account-name_\"wPeFnJEvendSTbH\"","lastmod" : Timestamp(3, 1),"lastmodEpoch" : ObjectId("55a52ff1fdd9a605a0371327"),"ns" : "abc.account","min" : {"name" : "wPeFnJEvendSTbH" #被移动的块},"max" : {"name" : { "$maxKey" : 1 }},"shard" : "shard0000" #原先所在的分片 } mongos> sh.moveChunk("abc.account",{"name" : "wPeFnJEvendSTbH"},"mablevi") #把abc.account集合中包含name(片键)为""的快迁移到mablevi分片中 { "millis" : 6800, "ok" : 1 } mongos> db.chunks.find({"_id" : "abc.account-name_\"wPeFnJEvendSTbH\""}).pretty() {"_id" : "abc.account-name_\"wPeFnJEvendSTbH\"","lastmod" : Timestamp(5, 0),"lastmodEpoch" : ObjectId("55a52ff1fdd9a605a0371327"),"ns" : "abc.account","min" : {"name" : "wPeFnJEvendSTbH"},"max" : {"name" : { "$maxKey" : 1 }},"shard" : "mablevi" #已被迁移到新片 }
上面是手动移动数据的操作,数据被移动。 要是块超出了64M限制【原因是片键没选好(日期、状态值等),导致一个块无限增大】,则无法进行自动均衡,无法分块。有2个办法:1是加大块的大小(setting),2是拆分sh.splitAt()(推荐)。
所以要是遇到分片写入比单点写入慢就是因为分片路由服务(mongos)需要维护元数据、数据迁移、路由开销等。
++++++++++++++++++++++++++++++++++++++++++++++++
五、高可用:Sharding+Replset
上面的分片都是单点的,要是一个分片坏了,则数据会丢失,利用之前减少的副本集,能否把副本集加入到分片中?下面就来说明下。
1)添加副本集分片服务器(mmm副本集名称):这里测试就只对一个分片加副本集,要实现完全的高可用就需要对所有分片加副本集,避免单点故障
一个普通的副本集:
mmm:PRIMARY> rs.status() {"set" : "mmm","date" : ISODate("2015-07-10T16:17:19Z"),"myState" : 1,"members" : [{"_id" : 2,"name" : "192.168.200.245:27017","health" : 1,"state" : 2,"stateStr" : "SECONDARY","uptime" : 418,"optime" : Timestamp(1436545003, 1),"optimeDate" : ISODate("2015-07-10T16:16:43Z"),"lastHeartbeat" : ISODate("2015-07-10T16:17:17Z"),"lastHeartbeatRecv" : ISODate("2015-07-10T16:17:18Z"),"pingMs" : 0,"syncingTo" : "192.168.200.25:27017"},{"_id" : 3,"name" : "192.168.200.25:27017","health" : 1,"state" : 1,"stateStr" : "PRIMARY","uptime" : 891321,"optime" : Timestamp(1436545003, 1),"optimeDate" : ISODate("2015-07-10T16:16:43Z"),"self" : true},{"_id" : 4,"name" : "192.168.200.245:37017","health" : 1,"state" : 2,"stateStr" : "SECONDARY","uptime" : 36,"optime" : Timestamp(1436545003, 1),"optimeDate" : ISODate("2015-07-10T16:16:43Z"),"lastHeartbeat" : ISODate("2015-07-10T16:17:17Z"),"lastHeartbeatRecv" : ISODate("2015-07-10T16:17:17Z"),"pingMs" : 0,"syncingTo" : "192.168.200.25:27017"}],"ok" : 1 }
View Code
现在需要把这个副本集加入到分片中:
mongos> sh.addShard("mmm/192.168.200.25:27017,192.168.200.245:27017,192.168.200.245:37017") #加入副本集分片 { "shardAdded" : "mmm", "ok" : 1 }mongos> sh.status() --- Sharding Status --- ......shards:{ "_id" : "mmm", "host" : "mmm/192.168.200.245:27017,192.168.200.245:37017,192.168.200.25:27017" } { "_id" : "shard0000", "host" : "192.168.200.51:40000" }{ "_id" : "shard0001", "host" : "192.168.200.52:40000" }{ "_id" : "shard0002", "host" : "192.168.200.53:40000" }balancer:Currently enabled: yesCurrently running: noFailed balancer rounds in last 5 attempts: 0Migration Results for the last 24 hours: 4 : Successdatabases:{ "_id" : "admin", "partitioned" : false, "primary" : "config" }{ "_id" : "test", "partitioned" : false, "primary" : "shard0000" }{ "_id" : "dba", "partitioned" : true, "primary" : "shard0000" }dba.accountshard key: { "name" : 1 }chunks:mmm 1shard0000 1shard0001 1shard0002 2{ "name" : { "$minKey" : 1 } } -->> { "name" : "5yyfY8mmR5HyhGJ" } on : shard0001 Timestamp(2, 0) { "name" : "5yyfY8mmR5HyhGJ" } -->> { "name" : "UavMbMlfszZOFrz" } on : mmm Timestamp(5, 0) { "name" : "UavMbMlfszZOFrz" } -->> { "name" : "t9LyVSNXDmf6esP" } on : shard0002 Timestamp(4, 1) { "name" : "t9LyVSNXDmf6esP" } -->> { "name" : "woQAv99Pq1FVoMX" } on : shard0002 Timestamp(3, 4) { "name" : "woQAv99Pq1FVoMX" } -->> { "name" : { "$maxKey" : 1 } } on : shard0000 Timestamp(5, 1) { "_id" : "abc", "partitioned" : false, "primary" : "shard0000" } #未设置分片
上面加粗部分表示副本集分片已经成功加入,并且新加入的分片会分到已有的分片数据。
mongos> db.account.stats() ... ..."shards" : {"mmm" : {"ns" : "dba.account","count" : 7723, #后加入的分片得到了数据"size" : 741408,"avgObjSize" : 96,"storageSize" : 2793472,"numExtents" : 5,"nindexes" : 2,"lastExtentSize" : 2097152,"paddingFactor" : 1,"systemFlags" : 1,"userFlags" : 0,"totalIndexSize" : 719488,"indexSizes" : {"_id_" : 343392,"name_1" : 376096},"ok" : 1}, ... ...
2)继续用python脚本写数据,填充到副本集中
由于之前的副本集是比较老的版本(2.4),所以在写入副本集分片的时候报错:
mongos> db.account.insert({"name":"UavMbMlfsz1OFrz"}) WriteResult({"nInserted" : 0,"writeError" : {"code" : 83,"errmsg" : "write results unavailable from 192.168.200.25:27017 :: caused by :: Location28563 cannot send batch write operation to server 192.168.200.25:27017 (192.168.200.25)"} })
太混蛋了,错误提示不太人性化,搞了半天。所以说版本一致性还是很重要的。现在重新开了一个副本集:
mablevi:PRIMARY> rs.status() {"set" : "mablevi","date" : ISODate("2015-07-10T18:22:36.761Z"),"myState" : 1,"members" : [{"_id" : 1,"name" : "192.168.200.53:50000","health" : 1,"state" : 1,"stateStr" : "PRIMARY","uptime" : 820,"optime" : Timestamp(1436552412, 213),"optimeDate" : ISODate("2015-07-10T18:20:12Z"),"electionTime" : Timestamp(1436551910, 1),"electionDate" : ISODate("2015-07-10T18:11:50Z"),"configVersion" : 2,"self" : true},{"_id" : 2,"name" : "192.168.200.53:50001","health" : 1,"state" : 2,"stateStr" : "SECONDARY","uptime" : 650,"optime" : Timestamp(1436552412, 213),"optimeDate" : ISODate("2015-07-10T18:20:12Z"),"lastHeartbeat" : ISODate("2015-07-10T18:22:36.737Z"),"lastHeartbeatRecv" : ISODate("2015-07-10T18:22:36.551Z"),"pingMs" : 0,"syncingTo" : "192.168.200.53:50000","configVersion" : 2},{"_id" : 3,"name" : "192.168.200.53:50002","health" : 1,"state" : 2,"stateStr" : "SECONDARY","uptime" : 614,"optime" : Timestamp(1436552412, 213),"optimeDate" : ISODate("2015-07-10T18:20:12Z"),"lastHeartbeat" : ISODate("2015-07-10T18:22:36.742Z"),"lastHeartbeatRecv" : ISODate("2015-07-10T18:22:36.741Z"),"pingMs" : 0,"syncingTo" : "192.168.200.53:50001","configVersion" : 2}],"ok" : 1,"$gleStats" : {"lastOpTime" : Timestamp(1436551942, 1),"electionId" : ObjectId("55a00ae6a08c789ce9e4b50d")} }
View Code
把之前的副本集分片删除了,如何删除见下面3)。
新的副本集加入分片中:
mongos> sh.addShard("mablevi/192.168.200.53:50000,192.168.200.53:50001,192.168.200.53:50002") { "shardAdded" : "mablevi", "ok" : 1 }mongos> sh.status() --- Sharding Status --- ... ...shards:{ "_id" : "mablevi", "host" : "mablevi/192.168.200.53:50000,192.168.200.53:50001,192.168.200.53:50002" }{ "_id" : "shard0000", "host" : "192.168.200.51:40000" }{ "_id" : "shard0001", "host" : "192.168.200.52:40000" }{ "_id" : "shard0002", "host" : "192.168.200.53:40000" } ... ...dba.accountshard key: { "name" : 1 }chunks:mablevi 1shard0000 1shard0001 1shard0002 2{ "name" : { "$minKey" : 1 } } -->> { "name" : "5yyfY8mmR5HyhGJ" } on : shard0001 Timestamp(2, 0) { "name" : "5yyfY8mmR5HyhGJ" } -->> { "name" : "UavMbMlfszZOFrz" } on : mablevi Timestamp(9, 0) #新加入的分片得到数据{ "name" : "UavMbMlfszZOFrz" } -->> { "name" : "t9LyVSNXDmf6esP" } on : shard0002 Timestamp(4, 1) { "name" : "t9LyVSNXDmf6esP" } -->> { "name" : "woQAv99Pq1FVoMX" } on : shard0002 Timestamp(3, 4) { "name" : "woQAv99Pq1FVoMX" } -->> { "name" : { "$maxKey" : 1 } } on : shard0000 Timestamp(9, 1) { "_id" : "abc", "partitioned" : false, "primary" : "shard0000" }{ "_id" : "mablevi", "partitioned" : false, "primary" : "shard0001" }
继续用python写入操作:
mongos> db.account.stats() { ......"shards" : {"mablevi" : {"ns" : "dba.account","count" : 47240,"size" : 5290880, ......
副本集的分片被写入了47240条记录。此时把副本集分片的Primary shutdown掉,再查看:
mongos> db.account.stats() {"sharded" : true,"code" : 13639,"ok" : 0,"errmsg" : "exception: can't connect to new replica set master [192.168.200.53:50000], err: couldn't connect to server 192.168.200.53:50000 (192.168.200.53), connection attempt failed" #由于副本集的Primary被shutdown之后,选举新主还是要几秒的时间,期间数据不能访问,导致分片数据也不能访问 } mongos> db.account.stats() ... ..."shards" : {"mablevi" : {"ns" : "dba.account","count" : 47240, #副本集新主选举完毕之后,分片数据访问正常。数据没有丢失,高可用得到了实现。"size" : 5290880, ... ...
要是让副本集分片只剩下一台(Secondary),则分片会报错:
mongos> db.account.stats() {"sharded" : true,"code" : 10009,"ok" : 0,"errmsg" : "exception: ReplicaSetMonitor no master found for set: mablevi" #数据不能访问 }
3)删除分片: db.runCommand({“removeshard”:”mmm”})
要是觉得分片太多了,想删除,则:
mongos> use admin #需要到admin下面删除 switched to db admin mongos> db.runCommand({"removeshard":"mmm"}) {"msg" : "draining started successfully","state" : "started", #开始删除,数据正在转移"shard" : "mmm","ok" : 1 } mongos> sh.status() --- Sharding Status ---......shards:{ "_id" : "mmm", "host" : "mmm/192.168.200.245:27017,192.168.200.245:37017,192.168.200.25:27017", "draining" : true } #删除的分片数据移动到其他分片{ "_id" : "shard0000", "host" : "192.168.200.51:40000" }{ "_id" : "shard0001", "host" : "192.168.200.52:40000" }{ "_id" : "shard0002", "host" : "192.168.200.53:40000" } ......databases:{ "_id" : "admin", "partitioned" : false, "primary" : "config" }{ "_id" : "test", "partitioned" : false, "primary" : "shard0000" }{ "_id" : "dba", "partitioned" : true, "primary" : "shard0000" }dba.accountshard key: { "name" : 1 }chunks:shard0000 2shard0001 1shard0002 2{ "name" : { "$minKey" : 1 } } -->> { "name" : "5yyfY8mmR5HyhGJ" } on : shard0001 Timestamp(2, 0) { "name" : "5yyfY8mmR5HyhGJ" } -->> { "name" : "UavMbMlfszZOFrz" } on : shard0000 Timestamp(8, 0) { "name" : "UavMbMlfszZOFrz" } -->> { "name" : "t9LyVSNXDmf6esP" } on : shard0002 Timestamp(4, 1) #这里已经没有了被删除分片信息{ "name" : "t9LyVSNXDmf6esP" } -->> { "name" : "woQAv99Pq1FVoMX" } on : shard0002 Timestamp(3, 4) { "name" : "woQAv99Pq1FVoMX" } -->> { "name" : { "$maxKey" : 1 } } on : shard0000 Timestamp(7, 1) { "_id" : "abc", "partitioned" : false, "primary" : "shard0000" }{ "_id" : "mablevi", "partitioned" : false, "primary" : "shard0001" }mongos> db.runCommand({"removeshard":"mmm"}) #再次执行,直到执行成功,要是原来分片的数据比较大,这里比较费时,要是一个主分片则需要执行movePrimary {"msg" : "removeshard completed successfully","state" : "completed", #完成删除"shard" : "mmm","ok" : 1 } mongos> sh.status() --- Sharding Status ---...shards: #分片消失{ "_id" : "shard0000", "host" : "192.168.200.51:40000" }{ "_id" : "shard0001", "host" : "192.168.200.52:40000" }{ "_id" : "shard0002", "host" : "192.168.200.53:40000" } ......{ "name" : { "$minKey" : 1 } } -->> { "name" : "5yyfY8mmR5HyhGJ" } on : shard0001 Timestamp(2, 0) { "name" : "5yyfY8mmR5HyhGJ" } -->> { "name" : "UavMbMlfszZOFrz" } on : shard0000 Timestamp(8, 0) { "name" : "UavMbMlfszZOFrz" } -->> { "name" : "t9LyVSNXDmf6esP" } on : shard0002 Timestamp(4, 1) #已经没有了被删除分片的信息{ "name" : "t9LyVSNXDmf6esP" } -->> { "name" : "woQAv99Pq1FVoMX" } on : shard0002 Timestamp(3, 4) { "name" : "woQAv99Pq1FVoMX" } -->> { "name" : { "$maxKey" : 1 } } on : shard0000 Timestamp(7, 1) { "_id" : "abc", "partitioned" : false, "primary" : "shard0000" }{ "_id" : "mablevi", "partitioned" : false, "primary" : "shard0001" }
分片被删除之后,数据被移到其他分片中,不会丢失。要是想让主分片进行转移则(movePrimary):
mongos> db.adminCommand({"movePrimary":"test","to":"shard0001"}) #把test的主分片从shard0000迁移到shard0001
刷新下配置服务器:db.adminCommand({“flushRouterConfig”:1})
db.adminCommand({"flushRouterConfig":1})
最后来查看下分片成员:db.runCommand({ listshards : 1 })
mongos> use admin #需要进入admin才能执行 switched to db admin mongos> db.runCommand({ listshards : 1 }) {"shards" : [{"_id" : "shard0000","host" : "192.168.200.51:40000"},{"_id" : "shard0001","host" : "192.168.200.52:40000"},{"_id" : "shard0002","host" : "192.168.200.53:40000"},{"_id" : "mablevi","host" : "mablevi/192.168.200.53:50000,192.168.200.53:50001,192.168.200.53:50002"}],"ok" : 1 }
到此已经把MongoDB分片原理、搭建、应用大致已经介绍完。
六、认证分配
上面的所有操作都是在无账号密码下进行的,这样是不安全的,那如何使用账号密码呢?和副本级一样,需要添加KeyFile参数,但是针对上面的三个角色(config、mongos、mongod)账号密码怎么添加呢?官网上已经做了说明:http://docs.mongodb.org/manual/tutorial/enable-authentication-in-sharded-cluster/。下面就对有账号密码认证分片进行相关设置说明。
首先要创建账号(Root角色)和生成一个KeyFile文件,其中mongos 不需要创建账号。
openssl rand -base64 741 > mongodb-keyfile chmod 600 mongodb-keyfile
其实这个文件也可以直接用明文,只要保证各个地方指定的文件是同一个就可以了。
1)mongd: 首先在mongod角色的分片成员上生成key file文件,特别注意的是有副本级的分片,再把这个文件分别复制到其他角色的服务器上。再添加参数:
auth = true keyFile = /usr/local/mongodb-keyfile
2)Config上添加参数:
auth = true keyFile = /usr/local/mongodb-keyfile
3)mongos上添加参数,因为mongos本来就是从config里加载数据的,所以只需要添加keyfile文件即可,不需要找上面createUser。
keyFile = /usr/local/mongodb-keyfile
最后重启各个服务,再进入mongos里查看:
root@mongo1:/usr/local# mongo --port=30000 MongoDB shell version: 3.0.4 connecting to: 127.0.0.1:30000/test mongos> sh.status() #没有认证,没有权限报错。 2015-07-14T23:42:11.800+0800 E QUERY Error: error: { "$err" : "not authorized for query on config.version", "code" : 13 }at Error (<anonymous>)at DBQuery.next (src/mongo/shell/query.js:259:15)at DBCollection.findOne (src/mongo/shell/collection.js:189:22)at printShardingStatus (src/mongo/shell/shardingtest.js:659:55)at Function.sh.status (src/mongo/shell/utils_sh.js:60:5)at (shell):1:4 at src/mongo/shell/query.js:259 mongos> use admin switched to db admin mongos> db.auth('dba','dba') #认证 1 mongos> sh.status() #有权限 --- Sharding Status --- sharding version: {"_id" : 1,"minCompatibleVersion" : 5,"currentVersion" : 6,"clusterId" : ObjectId("55a51ef18bd517d4acec5ef9") }shards:{ "_id" : "mablevi", "host" : "mablevi/192.168.200.53:50000,192.168.200.53:50001,192.168.200.53:50002" }{ "_id" : "shard0000", "host" : "192.168.200.51:40000" }{ "_id" : "shard0001", "host" : "192.168.200.52:40000" }{ "_id" : "shard0002", "host" : "192.168.200.53:40000" }balancer: ... ...databases:{ "_id" : "admin", "partitioned" : false, "primary" : "config" }{ "_id" : "test", "partitioned" : false, "primary" : "shard0000" }{ "_id" : "dba", "partitioned" : true, "primary" : "shard0000" }dba.accountshard key: { "name" : 1 }chunks:mablevi 1shard0000 1shard0001 2shard0002 1{ "name" : { "$minKey" : 1 } } -->> { "name" : "9XXqCaBhfhPIXLq" } on : mablevi Timestamp(2, 0) { "name" : "9XXqCaBhfhPIXLq" } -->> { "name" : "RWINvgjYYQmbZds" } on : shard0002 Timestamp(4, 0) { "name" : "RWINvgjYYQmbZds" } -->> { "name" : "jSPRBNH8rvnzblG" } on : shard0001 Timestamp(4, 1) { "name" : "jSPRBNH8rvnzblG" } -->> { "name" : "okmjUUZuuKgftDC" } on : shard0001 Timestamp(3, 4) { "name" : "okmjUUZuuKgftDC" } -->> { "name" : { "$maxKey" : 1 } } on : shard0000 Timestamp(3, 1)
七、分片备份、还原
因为分片机制里面会有平衡器来迁移数据,所以各个分片里的数据很可能会移动,所以在备份分片时需要做:
①:先停止平衡器的工作,并检查没有chunk move动作,保证dump的时候没有进行数据迁移。
mongos> sh.stopBalancer()
②:锁定数据库,保证数据没有写入:在各个分片上和配置服务器上执行。
> db.fsyncLock() {"info" : "now locked against writes, use db.fsyncUnlock() to unlock","seeAlso" : "http://dochub.mongodb.org/core/fsynccommand","ok" : 1 }
③:执行备份操作,备份各个分片服务器和配置服务器。
mongodump -udba -p12345 -d dba_test --authenticationDatabase admin -o backup/
④:解锁数据库,备份完成之后在分片和配置服务器上解锁数据库,允许修改。
> db.fsyncUnlock() { "ok" : 1, "info" : "unlock completed" }
当数据库出现问题,需要还原的时候,需要还原各个分片和配置服务器,并且重启MongoDB实例。还原数据库需要做:
①:还原各个分片和配置服务器。
mongorestore --host=127.0.0.1 --port=27017 -udba -p12345 -d dba_test --authenticationDatabase admin --drop backup/dba_test
②:重启各个实例
总结:
分片很好的解决了单台服务器磁盘空间、内存、cpu等硬件资源的限制问题,把数据水平拆分出去,降低单节点的访问压力。每个分片都是一个独立的数据库,所有的分片组合起来构成一个逻辑上的完整的数据库。因此,分片机制降低了每个分片的数据操作量及需要存储的数据量,达到多台服务器来应对不断增加的负载和数据的效果。后面文章还会继续对分片的其他方面进行说明介绍。
参考文档:
说明:http://docs.mongodb.org/manual/core/sharding-introduction/
配置:http://docs.mongodb.org/manual/tutorial/deploy-shard-cluster/
应用:http://www.caiyiting.com/blog/2014/replica-sets-sharding-realization.html
MongoDB基本介绍及一些用法相关推荐
- Mongodb数据库介绍
Mongodb数据库介绍 一种非关系型数据库mongodb,mongodb数据库主要用于海量存储,常被用在数据采集项目中. mongodb官方文档 https://docs.mongodb.com/ ...
- html图片标签img的介绍以及基本用法详解
<img> 元素向网页中嵌入一幅图像.<img> 标签有两个必需的属性:src 属性 和 alt 属性. 必需的属性 属性 值 描述 alt text 规定图像的替代文本. s ...
- jQuery框架的介绍以及基本用法--操作dom
jQuery框架的介绍以及基本用法–操作dom 概念 jQuery是一个优秀的javascript轻量级框架之一,兼容css3和各大浏览器,提供了dom,events,animate,ajax等简易操 ...
- MongoDb企业应用实战(一) 写在MongoDB应用介绍之前(ii)
上一篇: MongoDb企业应用实战(一) 写在MongoDB应用介绍之前(i) 有段时间没跟大家去分享和探讨过一些问题,分享过一些经验了(失败过的,痛苦过的才最有看点啊,不知道各位同仁们怎么去看这个 ...
- java中dynamic_介绍@dynamic的用法
介绍@dynamic的用法 Objective-C 2.0提供了属性(@property),可以让编译器自动生成setter和getter方法.如果不想编译器自作主张生成这些setter和getter ...
- MongoDB简单介绍以及基本命令
一.MongoDB简介 1.MongoDB是什么 MongoDB 是一个基于分布式文件存储的数据库.由 C++ 语言编写.旨在为 WEB 应用提供可扩展的高性能数据存储解决方案.MongoDB 是一个 ...
- shell介绍及基本用法
8.1:shell介绍: shell是一个命令解释器,提供用户和机器之间的交互,支持特定语法,支持逻辑判断.循环,并且每个用户都可以有自己的shell: Centos默认的shell是bash(Bou ...
- 求介绍matlab函数用法的书,MATLAB初学者教程--函数用法的简单介绍
1.4 函数用法的简单介绍 1.4.1什么是函数 似乎很多人一听到函数这个词就会想到数学中的某个概念,然后对于恐惧数学的同学就开始打退堂鼓.在matlab当中到处可以用到函数,它的出现可以让我们用很简 ...
- django之ORM介绍与基本用法(一)
文章目录 ORM介绍 ORM用法 字段类型: 字段选项: 关系字段类型 元选项: 模型成员 Manager:管理器类 自定义管理器类:1.修改原始查询集 自定义管理器类:2.新增管理器方法 ORM介绍 ...
最新文章
- input type=file /,美化自定义上传按钮
- Gradle 学习二
- 国防科大计算机专业分数线,2018国防科技大学各省录取分数线_2017年国防科大录取线...
- 深度学总结:Image Style Transfer pytorch方式实现,这个是非基于autoencoder和domain adversrial方式
- 12.4日团队工作总结
- 哈希(hash)表查找速度为什么那么快?快在哪里了?
- wifi管理系统_如何有效选择一款移动考勤管理系统
- 阿里2000亿,腾讯5000亿,我们究竟需要多少数据中心?
- Linux---高级IO
- 银行理财子公司的“超级”玩法
- 时间序列预测算法总结
- Spring Boot 开发微信公众号后台
- 零基础使用Xmind
- 按哪个键进入BIOS设置
- 两台笔记本如何通过网线实现拓展屏
- 推荐 6 个 yyds 的人脸识别系统
- 正则匹配至少8位字母数字组合,不能为纯数字或纯字母
- 上面两点下面一个三角形_三角形的五心是三线共点的产物,聊一聊初中数学共点线的证明思路...
- Gauss型(Gaussian quadrature)求积公式和方法
- 1364 小Y的烦恼
热门文章
- 小学生护眼用什么样的台灯比较好用一点?小学生护眼台灯排行榜
- 长沙亚信科技Java实习面经
- 安卓强制恢复出厂_怎样科学合理地下载、卸载APP?恢复出厂设置存在哪些问题?...
- QT:自带框架安装包制作
- tfs 方法访问数据库 源代码_TFS源代码管理
- 谈谈自己对机器学习如何学习以及未来职业方向的理解(不断更新中)
- Python自定义高精度小数计算来获取巴塞尔问题的近似值
- python定义一个复数类complex、并实现复数相加_用java定义一个复数类Complex,能够创建复数对象,并且实现复数之间的加、减运算...
- 直播App架构及思维导图
- python连接mt4服务器_如何从MetaTrader 4/5终端向外部服务器发送数据?