一.部署分片集群
在3.2版本中,官方建议对所有生产的config server和shard server都使用复制集.其中config server对复制集有如下要求:
1.必须没有仲裁节点
2.必须没有延时节点
3.必须创建索引,即没有节点配置buildIndex为false
我们这里用来测试就搭建复制集了.
新部署一个分片集群包括4步骤:
1.部署config server
2.部署mongos实例
3.部署mongod server
4.将mongod server添加到集群
5.为集群开启分片
介绍一下测试部署环境,总共三台机器:
主机名 | ip地址 | 角色 | 作用 |
mongodb1 | 192.168.56.80 | config server | 配置服务器 |
mongodb2 | 192.168.56.81 | mongod server/mongos | 分片服务器/路由服务 |
mongodb3 | 192.168.56.82 | mongod server | 分片服务器 |
1.部署config server
由于机器有限,mongodb1用来部署config server,我们使用单机不使用复制集,我们查看mongod的帮助可以看到shard配置信息:
[root@mongodb1 db]# mongod -h
Options:
...
Sharding options:
--configsvr declare this is a config db of a
cluster; default port 27019; default
dir /data/configdb
--configsvrMode arg Controls what config server protocol is
in use. When set to "sccc" keeps server
in legacy SyncClusterConnection mode
even when the service is running as a
replSet
--shardsvr declare this is a shard db of a
cluster; default port 27018
其中说的很明白了,如果要将机器配置为configserver只要在配置文件中添加上
configsvr选项,下面是我们的配置文件:
[root@mongodb1 db]# cat /etc/mongod.conf
port = 27017
dbpath = /data/db
logpath = /data/log/config.log
logappend = true
fork = true
configsvr = true
#replSet = configreplset
启动mongod:
[root@mongodb1 db]# mongod -f /etc/mongod.conf
about to fork child process, waiting until server is ready for connections.
forked process: 3767
child process started successfully, parent exiting
这样config server就配置完成了,还是很简单的,
如果要配置成复制集(一般是三节点),还需要在上面的再配置复制集的信息:
rs.initiate( {
_id: "configReplSet",
configsvr: true,
members: [
{ _id: 0, host: "<host1>:<port1>" },
{ _id: 1, host: "<host2>:<port2>" },
{ _id: 2, host: "<host3>:<port3>" }
]
} )
这里就不详细描述复制集了.
2.部署mongos
mongos实例是轻量服务,并且不需要数据目录,你可以将 mongos 运行在已经部署了其他服务的系统中,比如应用服务器或者运行了mongod 的机器上.mongos 默认运行在 27017 端口上.
mongos启动的时候最重要的参数是
configdb,用来指定config server,因为mongos要缓存config server中的元信息,用来路由数据.
[root@mongodb2 ~]# mongos -h Options:
- ...
Sharding options:
--configdb arg Connection string for communicating with config
servers. Acceptable forms:
CSRS: <config replset name>/<host1:port>,<host2:
port>,[...]
下面是我的mongos参数,如果config server是复制集使用configReplSet/<cfgsvr1:port1>,<cfgsvr2:port2>,<cfgsvr3:port3>:
[root@mongodb2 ~]# cat /etc/mongos.conf
logpath = /data/log/mongos.log
logappend = true
configdb = mongodb1:27017
port = 27018
fork = true
启动mongos服务,注意这里的mongos端口是27018,因为这台机器等会还要启动一个mongod,我设置mongod的端口为27017:
[root@mongodb2 ~]# mongos -f /etc/mongos.conf
2016-06-21T20:27:15.748+0800 W SHARDING [main] Running a sharded cluster with fewer than 3 config servers should only be done for testing purposes and is not recommended for production.
about to fork child process, waiting until server is ready for connections.
forked process: 3878
child process started successfully, parent exiting
3.部署mongod server
mongod server的部署和普通的单实例没太大的区别,添加一个shardsvr参数,如果是复制集要添加replSet参数,这里测试并没有:
[root@mongodb2 ~]# cat /etc/mongod.conf
port=27017
dbpath=/data/db
logpath=/data/log/mongod.log
logappend=true
fork = true
#replSet = rs0
oplogSize = 500
shardsvr = true
分别启动mongodb2,mongodb3的mongod服务,如果是复制集先搭建好复制集,这里就赘述.
[root@mongodb2 db]# mongod -f /etc/mongod.conf
about to fork child process, waiting until server is ready for connections.
forked process: 3944
child process started successfully, parent exiting
4.将mongod添加到集群
连接到mongos,即mongodb2的27018端口:
[root@mongodb2 db]# mongo 127.0.0.1:27018
MongoDB shell version: 3.2.6
connecting to: 127.0.0.1:27018/test
Server has startup warnings:
2016-06-21T20:27:15.759+0800 I CONTROL [main] ** WARNING: You are running this process as the root user, which is not recommended.
2016-06-21T20:27:15.760+0800 I CONTROL [main]
mongos>
可以看到这里的提示符为mongos.使用
sh.addShard("host:port")命令来添加mongod到集群,如果集群是复制集需要使用
sh.adShard("replname/host:port")来添加:
mongos> sh.addShard("mongodb2:27017")
{ "shardAdded" : "shard0000", "ok" : 1 }
mongos> sh.addShard("mongodb3:27017")
{ "shardAdded" : "shard0001", "ok" : 1 }
5.为集群开启分片
首先使用
sh.enableSharding("database")对数据库开启分片,
你也可以使用数据库命令开启分片,语法如下:
db.runCommand( { enableSharding: <database> } )
例如我们对test数据库开启分片功能,连接的仍然是mongos:
mongos> show dbs config 0.000GB
mongos> sh.enableSharding("test");
{ "ok" : 1 }
分片是集合级别的,所以还需要在集合上开启,
1.首先选择一个 shard key ,所选择的片键会影响集群的效率.参见 选择片键的注意事项. 获得注意事项.
2.如果集合中已经包含有数据,需要使用ensureIndex() 在片键上创建索引.如果集合是空的,MongoDB会在 sh.shardCollection() 过程中自动创建索引.
3.使用
sh.shardCollection()方法来为一个集合启用分片,语法如下:
sh.shardCollection("<database>.<collection>", shard-key-pattern)
示例如下:
sh.shardCollection("records.people", { "zipcode": 1, "name": 1 } )
sh.shardCollection("people.addresses", { "state": 1, "_id": 1 } )
sh.shardCollection("assets.chairs", { "type": 1, "_id": 1 } )
sh.shardCollection("events.alerts", { "_id": "hashed" } )
我们先来创建一张users表:
mongos> use test
switched to db test
mongos> for(i=0;i<100;i++){
... db.users.insert(
... {
... "i":i,
... "username":"user"+i,
... "age":Math.floor(Math.random()*120),
... "created":new Date()
... }
... );
... }
WriteResult({ "nInserted" : 1 })
假设我们以i和username为分片键,那么首先创建索引:
mongos> db.users.createIndex({i:1,username:1})
{
"raw" : {
"mongodb2:27017" : {
"createdCollectionAutomatically" : false,
"numIndexesBefore" : 1,
"numIndexesAfter" : 2,
"ok" : 1
}
},
"ok" : 1
}
最后为集合users开启分片:
mongos> sh.shardCollection("test.users",{"i":1,"username":1})
{ "collectionsharded" : "test.users", "ok" : 1 }
如果要使用hash分片,那么首先需要为片键创建hash索引使用db.colname.createIndex({"field":"hashed"});
在使用sh.shardCollection("test.users",{"field":"hashed"})来创建hash分片.
到此mongodb的分片已经搭建完成.可以使用
sh.status()查看集群的状态:
mongos> sh.status()
--- Sharding Status ---
sharding version: {
"_id" : 1,
"minCompatibleVersion" : 5,
"currentVersion" : 6,
"clusterId" : ObjectId("576932a3f43b00b3956fecb7")
}
shards:
{ "_id" : "shard0000", "host" : "mongodb2:27017" }
{ "_id" : "shard0001", "host" : "mongodb3:27017" }
active mongoses:
"3.2.6" : 1
balancer:
Currently enabled: yes
Currently running: no
Failed balancer rounds in last 5 attempts: 0
Migration Results for the last 24 hours:
No recent migrations
databases:
{ "_id" : "test", "primary" : "shard0000", "partitioned" : true }
test.users
shard key: { "i" : 1, "username" : 1 }
unique: false
balancing: true
chunks:
shard0000 1
{ "i" : { "$minKey" : 1 }, "username" : { "$minKey" : 1 } } -->> { "i" : { "$maxKey" : 1 }, "username" : { "$maxKey" : 1 } } on : shard0000 Timestamp(1, 0)
在上面的状态中我们可以看到当前集合users只有一个chunks:shard0000.
6.测试
我们在users集合上插入20w条数据,查看mongodb2,mongodb3上是否都有数据,
mongos> for(i=100;i<200000;i++){
... db.users.insert(
... {
... "i":i,
... "username":"user"+i,
... "age":Math.floor(Math.random()*120),
... "created":new Date()
... }
... );
... }
重新查看状态:
mongos> sh.status()
--- Sharding Status ---
sharding version: {
"_id" : 1,
"minCompatibleVersion" : 5,
"currentVersion" : 6,
"clusterId" : ObjectId("576932a3f43b00b3956fecb7")
}
shards:
{ "_id" : "shard0000", "host" : "mongodb2:27017" }
{ "_id" : "shard0001", "host" : "mongodb3:27017" }
active mongoses:
"3.2.6" : 1
balancer:
Currently enabled: yes
Currently running: no
Failed balancer rounds in last 5 attempts: 0
Migration Results for the last 24 hours:
7 : Success
databases:
{ "_id" : "test", "primary" : "shard0000", "partitioned" : true }
test.users
shard key: { "i" : 1, "username" : 1 }
unique: false
balancing: true
chunks:
shard0000 8
shard0001 7
{ "i" : { "$minKey" : 1 }, "username" : { "$minKey" : 1 } } -->> { "i" : 1, "username" : "user1" } on : shard0001 Timestamp(2, 0)
{ "i" : 1, "username" : "user1" } -->> { "i" : 13, "username" : "user13" } on : shard0001 Timestamp(3, 0)
{ "i" : 13, "username" : "user13" } -->> { "i" : 20, "username" : "user20" } on : shard0001 Timestamp(4, 0)
{ "i" : 20, "username" : "user20" } -->> { "i" : 27, "username" : "user27" } on : shard0001 Timestamp(5, 0)
{ "i" : 27, "username" : "user27" } -->> { "i" : 34, "username" : "user34" } on : shard0001 Timestamp(6, 0)
{ "i" : 34, "username" : "user34" } -->> { "i" : 41, "username" : "user41" } on : shard0001 Timestamp(7, 0)
{ "i" : 41, "username" : "user41" } -->> { "i" : 48, "username" : "user48" } on : shard0001 Timestamp(8, 0)
{ "i" : 48, "username" : "user48" } -->> { "i" : 55, "username" : "user55" } on : shard0000 Timestamp(8, 1)
{ "i" : 55, "username" : "user55" } -->> { "i" : 62, "username" : "user62" } on : shard0000 Timestamp(1, 9)
{ "i" : 62, "username" : "user62" } -->> { "i" : 69, "username" : "user69" } on : shard0000 Timestamp(1, 10)
{ "i" : 69, "username" : "user69" } -->> { "i" : 76, "username" : "user76" } on : shard0000 Timestamp(1, 11)
{ "i" : 76, "username" : "user76" } -->> { "i" : 83, "username" : "user83" } on : shard0000 Timestamp(1, 12)
{ "i" : 83, "username" : "user83" } -->> { "i" : 90, "username" : "user90" } on : shard0000 Timestamp(1, 13)
{ "i" : 90, "username" : "user90" } -->> { "i" : 97, "username" : "user97" } on : shard0000 Timestamp(1, 14)
{ "i" : 97, "username" : "user97" } -->> { "i" : { "$maxKey" : 1 }, "username" : { "$maxKey" : 1 } } on : shard0000 Timestamp(1, 15)
可以看到总共有15个chunks,其中shard0000有8个,shard0001有7个,注意集群分片是以chunk为单位的,而不是以数据的条数.
这里有一个官方示例,题目是将一个replica转换成shard,但是我们也可以根据这个示例搭建一套包含复制集的sharding:
二.集群数据管理
1.在集群中创建chunks
在一个空的分片集合上预分配数据块可以使得客户端将数据写入到已经分好区的集合中.在大多数情况下 sharded cluster 可以自动创建并均衡数据块,但在部分情况下,MongoDB不能足够快地进行数据快的分裂和数据均衡,比如:
如果你想对一个分片上已经存储有数据的集合进行分区.
如果要将大量数据插入到尚未就均衡的集群中,或者插入的数据会导致数据不均衡,比如使用单调递增或递减的数据会使得新数据写入到一个分片中.
因为一些原因,这些操作需要消耗很多资源:
数据块迁移需要将分片中一个数据块的数据全部迁移到另一个分片中.
MongoDB同时只能迁移一个数据块.
MongoDB只有在插入时才会进行数据块分裂.
注意:只对空集合进行预分配.如果集合中已经有数据,在对集合开启分片后,MongoDB会自动进行数据块分裂.在存在数据时手动干预数据块分裂与数据块迁移会导致不可预知的数据块大小和效率低下的数据均衡.
在集合中手动使用 split 创建数据块.
在 mongo 中使用以下指令为 myapp.users 创建数据块,使用的片键为 email .
for ( var x=97; x<97+26; x++ ){
for( var y=97; y<97+26; y+=6 ) {
var prefix = String.fromCharCode(x) + String.fromCharCode(y);
db.runCommand( { split : "myapp.users" , middle : { email : prefix } } );
}
}
2.在集群中分裂chunks
正常情况下,如果数据块的大小已经达到设定的最大值 数据块大小 ,MongoDB会在数据再次插入时进行分裂,不过有时候,需要手动进行数据块分裂:
在使用已有的数据部署完一个集群之后,可能会发生数据很多,但是集群中的 chunks 很少的情况.
你希望插入大量的数据,但是这些数据所属的数据块集中在一个分片上.比如,你想插入大量 shard key 在 300 到 400 之间的数据,但是范围在 250 到 500 之间的数据块都在一个分片上.
如果 mongos 认为以后的写入会从数据迁移中获益,它会立刻将新分裂的数据块迁移到一个新的分片上,均衡器并不区分数据块是人工分裂的还是系统自动分裂的.
注意:在已经存在数据的集群中进行数据块分裂要十分小心,MongoDB自动分裂数据块并进行数据均衡,此时进行数据块分裂要考虑到分裂之后各个数据块的大小,如果分裂之后数据块大小变得不规则,有可能出现各个分片间数据块数量均衡但是数据量不均衡的情况.避免做导致数据块大小不均衡的数据块分裂.
使用 sh.status() 来查看集群中当前的数据块范围.
使用
split 进行数据块分裂,可以使用 middle 参数或者 find 参数. mongo 提供了:method:sh.splitFind() 与 sh.splitAt() 的使用帮助.
splitFind() 方法将包含 第一条 符合查询条件的数据块分为相同大小的两部分.在使用这个方法时需要传递完整的namespace (即 “<database>.<collection>”),方法的查询参数中可以不包含片键,但在大多数情况下,包含片键比较合理.
举例
下面的命令将 records 数据库中 people 集合包含 zipcode 值为 63109 的数据块分为两个数据块:
sh.splitFind( "records.people", { "zipcode": "63109" } )
使用 splitAt() 将数据块分为两个数据块,新数据块使用查询的值作为最小值.
下面的命令将 records 数据库中 people 集合包含 zipcode 值为 63109 的数据块分为两个数据块:
sh.splitAt( "records.people", { "zipcode": "63109" } )
注意:splitAt() 并不确保将数据块分为相同大小的两部分,这个方法按照传递的参数将数据块分为两部分,并不管查询的数据是不是在数据块中.
3.在集群中迁移chunks
在绝大多数情况下,应该使用自动的 balancer 在 shards 之间迁移 chunks ,不过,有时候也需要手动迁移.
使用 pre-splitting 分裂空的集合并手工迁移到每个分片上.在准备批量写入数据时可以这样使用.
如果集群数据非常活跃,自动均衡不能在 均衡时间窗口 内迁移数据,这时需要手工迁移.
使用
moveChunk 手动迁移数据块.
以下的例子假设存在数据库 myapp,``users`` 是其中一个集合,片键为 username,要迁移的数据块中有一个片键值 smith.可以在 mongo 终端中使用以下命令进行数据块的迁移:
db.adminCommand( { moveChunk : "myapp.users",
find : {username : "smith"},
to : "mongodb-shard3.example.net" } )
这个命令将会把含有”smith”的数据块迁移到名字为 mongodb-shard3.example.net 的 shard 上.直到迁移完成命令才会返回.
为了均衡地迁移 myapp.users 集合中的数据块,可以把数据块在各个分片之间逐个迁移,并运行以下命令:
var shServer = [ "sh0.example.net", "sh1.example.net", "sh2.example.net", "sh3.example.net", "sh4.example.net" ];
for ( var x=97; x<97+26; x++ ){
for( var y=97; y<97+26; y+=6 ) {
var prefix = String.fromCharCode(x) + String.fromCharCode(y);
db.adminCommand({moveChunk : "myapp.users", find : {email : prefix}, to : shServer[(y-97)/6]})
}
}
命令 moveChunk 有参数
_secondaryThrottle,如果设置为 true,在数据块进行迁移时,对分片的修改都要同步到 从节点,参见 Change Replication Behavior for Chunk Migration 获得更多信息.
注意,moveChunk 可能会返回以下错误:
The collection's metadata lock is already taken.
如果要迁移的数据块上有太多打开的 游标 ,会产生错误.你可以等待这些游标完成或者手动关闭他们.
4.在集群中合并chunks
mergeChunks 方法允许你将空的数据块合并到同分片相邻的数据块中.如果在设定的片键范围内没有数据,这个数据块就是空的.
空的 数据块 会影响 balancer 对分片间数据均衡情况的正确判断.
空数据块在几种情况下会发生,包括:
如果 预分裂 创建了过多数据块,数据在数据块间的分布可能不均衡:
如果你删除了集群中很多数据,有些数据块可能不会包含数据.
注意,mergechunks方法必须合并的chunk是至少有一个是空块才能合并.使用下面方法确认一个块是空块:
use test
db.runCommand({
"dataSize": "test.users",
"keyPattern": { username: 1 },
"min": { "username": "user36583" },
"max": { "username": "user43229" }
})
如果传递给 dataSize 的数据块是空的,这个命令的输出类似如下:
{ "size" : 0, "numObjects" : 0, "millis" : 0, "ok" : 1 }
使用以下命令,合并存在于同一个 shard 上且至少一个为空数据块的两个数据块:
db.runCommand( { mergeChunks: "test.users",
bounds: [ { "username": "user68982" },
{ "username": "user95197" } ]
} )
成功时, mergeChunks 返回如下输出:
{ "ok" : 1 }
因为任何情况失败, mergeChunks 返回的文档中, ok 子段都为 0 .
5.修改集群中chunks大小
在第一个 mongos 连接到一组 配置服务器 时, 会以默认的64M大小的数据块初始化集群,在大多数情况下,默认的大小工作得很好.然而,如果你发现在默认的数据块大小下,自动迁移锁花费的I/O超过了系统可承受的极限,可以将数据块大小调小.对于自动分裂和迁移,较小的数据块可以使得迁移速度较快且较频繁.合法的数据块大小在1M到1024M之间,包含1M和1024M.
使用以下过程修改数据块的大小:
使用 mongo 终端连接任意一个 mongos .
使用以下命令切换到 Config Database:
use config
使用 save() 保存全局的数据块大小:
db.settings.save( { _id:"chunksize", value: <sizeInMB> } )
配置 chunkSize 和选项 --chunkSize 作为启动参数启动 mongos ,在初始化集群之后 不要 影响数据块大小.
为避免引起混乱,只使用 save() 修改数据块大小,而不使用在启动 mongos 时传递参数修改.
修改数据块大小有一些限制:
自动分裂只发生在写入与更新时.
如果减小了数据块大小,集群需要一段时间才能将所有的数据块分裂到新的值.
分裂不能被回滚
如果增大了数据块的大小,已存在的数据块只能通过写入和更新逐渐达到新的值.
合法的数据块大小范围为1M到1024M,包含1M和1024M.
6.确保分片集合中唯一字段的唯一性
在创建索引时增加 unique 用来保证在一个 collection 中字段的唯一性.但是对于 分片集合这样的索引并不能保证字段的唯一性 ,因为插入和索引操作对于每个分片都是本地操作.
如果你想保证指定的列的唯一性,那么有三种方法:
1.使用 片键 保证唯一性.
MongoDB 可以 保证 shard key 的唯一性.对于复合片键,可以创建包含全部片键的唯一索引.
对 哈希索引 不能有唯一性约束.
2.使用第二个集合保证唯一性.
创建另一个只包含唯一字段的不分片的集合,每次在写入主集合之前先将唯一字段的数据试图写入这个集合,若写入失败,则表示有冲突.
如果你的数据量比较小,可以不使用分片,就可以创建多个唯一索引了.
3.使用本身便能保证唯一性的标识符
一般情况下像 ObjectId 这样的唯一标识符(即 UUID)是可以保证唯一性的.
片键上的唯一性
要使用 unique 条件进行分片,需要像下面这样执行 shardCollection :
db.runCommand( { shardCollection : "test.users" , key : { email : 1 } , unique : true } );
7.对GridFS数据进行分片
在对 GridFS 存储进行分片时,需要注意以下的情况:
files 集合,
大多数情况下不需要对 files 集合进行分片,这个集合通常很小,只包含了一些元信息.集合中也没有合适的片键可以将数据均衡地分布在集群中.如果你 必须 对 files 进行分片,可以使用 _id 字段与应用相关的字段做复合片键.
不将``files`` 分片意味着所有文件的元信息都存储在一个分片上,在生产环境中, 必须 在存储了 files 的分片上使用复制集.
chunks 集合,
用以下命令使用 { files_id : 1 , n : 1 } 做片键为 chunks 集合分片:
db.fs.chunks.createIndex( { files_id : 1 , n : 1 } )
db.runCommand( { shardCollection : "test.fs.chunks" , key : { files_id : 1 , n : 1 } } )
也可以像这样只使用 file_id 字段进行分片:
db.runCommand( { shardCollection : "test.fs.chunks" , key : { files_id : 1 } } )
注意在GridFS存储中,对 chunks 集合进行分片时, 只有 两个片键可以选择,``{ files_id : 1 , n : 1 }`` 与 { files_id : 1 } .
默认的 files_id 是 ObjectId , ObjectId 是递增的,因此所有新写入的数据都会存储到一个单独的分片中,如果这个分片的写负载太大,考虑换一个片键或者在 files 集合中使用不同的 _id 值.
三.集群维护
1.查看集群配置
集群的配置信息都是在
config数据库下:
查看开启分片的数据库
mongos> use config
switched to db config
mongos> show tables
actionlog
changelog
chunks
collections
databases
lockpings
locks
mongos
settings
shards
tags
version
mongos> db.databases.find()
{ "_id" : "test", "primary" : "shard0000", "partitioned" : true }
partitioned为true表示数据库开启的分片.
列出分片列表
使用数据库命令
listShards来查看:
mongos> use admin
switched to db admin
mongos> db.runCommand({listShards:1})
{
"shards" : [
{
"_id" : "shard0000",
"host" : "mongodb2:27017"
},
{
"_id" : "shard0001",
"host" : "mongodb3:27017"
}
],
"ok" : 1
}
查看集群详细信息
使用db.printShardingStatus() 或者 sh.status(),两者是等价的,上面已经演示过了.
mongos> db.printShardingStatus()
--- Sharding Status ---
sharding version: {
"_id" : 1,
"minCompatibleVersion" : 5,
"currentVersion" : 6,
"clusterId" : ObjectId("576932a3f43b00b3956fecb7")
}
shards:
{ "_id" : "shard0000", "host" : "mongodb2:27017" }
{ "_id" : "shard0001", "host" : "mongodb3:27017" }
active mongoses:
"3.2.6" : 1
balancer:
Currently enabled: yes
Currently running: no
Failed balancer rounds in last 5 attempts: 0
Migration Results for the last 24 hours:
7 : Success
databases:
{ "_id" : "test", "primary" : "shard0000", "partitioned" : true }
test.users
shard key: { "i" : 1, "username" : 1 }
unique: false
balancing: true
chunks:
shard0000 8
shard0001 7
{ "i" : { "$minKey" : 1 }, "username" : { "$minKey" : 1 } } -->> { "i" : 1, "username" : "user1" } on : shard0001 Timestamp(2, 0)
{ "i" : 1, "username" : "user1" } -->> { "i" : 13, "username" : "user13" } on : shard0001 Timestamp(3, 0)
{ "i" : 13, "username" : "user13" } -->> { "i" : 20, "username" : "user20" } on : shard0001 Timestamp(4, 0)
{ "i" : 20, "username" : "user20" } -->> { "i" : 27, "username" : "user27" } on : shard0001 Timestamp(5, 0)
{ "i" : 27, "username" : "user27" } -->> { "i" : 34, "username" : "user34" } on : shard0001 Timestamp(6, 0)
{ "i" : 34, "username" : "user34" } -->> { "i" : 41, "username" : "user41" } on : shard0001 Timestamp(7, 0)
{ "i" : 41, "username" : "user41" } -->> { "i" : 48, "username" : "user48" } on : shard0001 Timestamp(8, 0)
{ "i" : 48, "username" : "user48" } -->> { "i" : 55, "username" : "user55" } on : shard0000 Timestamp(8, 1)
{ "i" : 55, "username" : "user55" } -->> { "i" : 62, "username" : "user62" } on : shard0000 Timestamp(1, 9)
{ "i" : 62, "username" : "user62" } -->> { "i" : 69, "username" : "user69" } on : shard0000 Timestamp(1, 10)
{ "i" : 69, "username" : "user69" } -->> { "i" : 76, "username" : "user76" } on : shard0000 Timestamp(1, 11)
{ "i" : 76, "username" : "user76" } -->> { "i" : 83, "username" : "user83" } on : shard0000 Timestamp(1, 12)
{ "i" : 83, "username" : "user83" } -->> { "i" : 90, "username" : "user90" } on : shard0000 Timestamp(1, 13)
{ "i" : 90, "username" : "user90" } -->> { "i" : 97, "username" : "user97" } on : shard0000 Timestamp(1, 14)
{ "i" : 97, "username" : "user97" } -->> { "i" : { "$maxKey" : 1 }, "username" : { "$maxKey" : 1 } } on : shard0000 Timestamp(1, 15)
2.备份集群元信息
1.暂时停止集群的均衡过程.参见 禁用均衡器 以获得更多信息.
2.关掉一个配置服务器
3.备份整个数据文件夹(即通过 dbPath 配置的配置服务器的数据文件夹.).
4.重启之前停止的配置服务器
5.重新打开集群的均衡过程,使集群可以继续数据均衡.参见 禁用均衡器 获得更多管理集群均衡过程的信息.
3.管理集群均衡过程
查看均衡器状态
使用
sh.getBalancerStat()获得状态
mongos> sh.getBalancerState()
true
为均衡窗口设置时间表
有时为了防止在业务期间发生均衡对系统性能造成影响,我们希望均衡只在特定的时间段执行,那么可以为均衡设置时间窗口表,具体步骤先连接mongos确保均衡器状态为打开,最后手工设置settings集合:
use config
sh.setBalancerState( true )
db.settings.update(
{ _id: "balancer" },
{ $set: { activeWindow : { start : "<start-time>", stop : "<stop-time>" } } },
{ upsert: true }
)
删除窗口时间:
use config
db.settings.update({ _id : "balancer" }, { $unset : { activeWindow : true } })
禁用均衡器
运行
sh.stopBalancer()来禁用均衡器
mongos> sh.stopBalancer()
Waiting for active hosts...
Waiting for the balancer lock...
Waiting again for active hosts after balancer is off...
如果有均衡正在运行,系统会继续将这个均衡过程运行完,之后才禁用.
运行以下命令,如果返回为 false可以确认均衡过程被禁用:
mongos> sh.getBalancerState()
false
为确认在禁用均衡过程之后,没有还在运行的均衡过程,可以使用
sh.isBalancerRunning()来查看均衡器当前是否正在运行,例如
以下命令:
use config
while( sh.isBalancerRunning() ) {
print("waiting...");
sleep(1000);
}
还可以使用手工修改settings集合来禁用均衡器:
db.settings.update( { _id: "balancer" }, { $set : { stopped: true } } , { upsert: true } )
开启均衡器
使用
sh.setBalancerState(true)来打开均衡器:
mongos> sh.setBalancerState(true)
在不提供 sh.startBalancer() 方法的驱动中,可以更改 config 数据库将均衡过程打开:
db.settings.update( { _id: "balancer" }, { $set : { stopped: false } } , { upsert: true } )
集合上启用/禁用均衡器
使用
sh.disableBalancing()和
sh.enableBalancing()来禁用和启用集合的均衡器,参数为集合名.
mongos> sh.disableBalancing("test.users")
mongos> sh.enableBalancing("test.users")
4.在集群中删除分片
要在集群中删除分片,那么分片的数据必须先迁移到其它分片中,所以均衡器的状态必须是打开.
首先确定你要删除的分片的名字,可以运行sh.status()查看_id的值,或者使用
db.adminCommand( { listShards: 1 } ):
mongos> db.adminCommand( { listShards: 1 } )
{
"shards" : [
{
"_id" : "shard0000",
"host" : "mongodb2:27017"
},
{
"_id" : "shard0001",
"host" : "mongodb3:27017"
}
],
"ok" : 1
}
再使用
removeShard命令来删除shard:
use admin
db.runCommand( { removeShard: "mongodb0" } )
这条命令返回类似如下的输出:
{
"msg" : "draining ongoing",
"state" : "ongoing",
"remaining" : {
"chunks" : 42,
"dbs" : 1
},
"ok" : 1
}
再次运行查看是否结束,或者还有多少剩余的chunk:
mongos> db.runCommand( { removeShard:"shard0001" })
{
"msg" : "removeshard completed successfully",
"state" : "completed",
"shard" : "shard0001",
"ok" : 1
}
如果这个分片是一个或多个数据库的 primary shard ,上面会存储没有分片的数据,如果不是,则跳过完成迁移任务.
这种情况下,如有要将数据保留在分片中,那么就必须先要使用
movePrimay命令将数据库的primary shard迁移到其它的shard上,具体如下:
1.确认数据库的primary shard
可以使用sh.status()来确认
2.使用movePrimary命令来迁移数据
db.runCommand( { movePrimary: "products", to: "mongodb1" })
其中products是数据库名,mongodb1是需要转移过去的shard名称.
这个命令直到全部数据迁移完成才会返回,可能会花费较长时间.最后返回的结果类似这样:
{ "primary" : "mongodb1", "ok" : 1 }
3.最后使用removeShard来删除shard.
四.分片方法
名字 | 描述 |
---|---|
sh._adminCommand() | 在admin数据库运行 database command ,就像 db.runCommand() ,不过可以保证只在 mongos 上运行. |
sh.getBalancerLockDetails() | Reports on the active balancer lock, if it exists. |
sh._checkFullName() | Tests a namespace to determine if its well formed. |
sh._checkMongos() | |
sh._lastMigration() | 报告最后进行的 chunk 迁移. |
sh.addShard() | 向集群中添加一个 shard |
sh.addShardTag() | 将一个分片与一个标记相关联,用以支持 标记相关的分片. |
sh.addTagRange() | 将片键的范围与某个标记相关联,用以支持 标记相关的分片. |
sh.removeTagRange() | Removes an association between a range shard keys and a shard tag. Use to manage tag aware sharding. |
sh.disableBalancing() | 禁用一个分片数据库中某个集合的均衡过程,这并不影响这个分片数据库中其他分片的均衡过程. |
sh.enableBalancing() | 如果之前使用了命令 sh.disableBalancing() 禁用了某个集合的均衡过程,这个命令将重新启用均衡过程. |
sh.enableSharding() | 对指定的数据库开启分片. |
sh.getBalancerHost() | 返回负责均衡过程的一个 mongos 名字. |
sh.getBalancerState() | 返回一个布尔值,反应 balancer 是否被启用. |
sh.help() | 返回 sh 命令的帮助信息. |
sh.isBalancerRunning() | 返回一个布尔值,报告当前是否有均衡器在进行数据块的迁移. |
sh.moveChunk() | 迁移 sharded cluster 中一个 chunk . |
sh.removeShardTag() | 删除一个分片与一个标记的关联. |
sh.setBalancerState() | |
sh.shardCollection() | 为一个集合开启分片 |
sh.splitAt() | |
sh.splitFind() | 将包含查询文档的一个已经存在的 chunk 分成两个差不多大小的数据块. |
sh.startBalancer() | 启用 balancer 并等待均衡过程开始. |
sh.status() | 就像 db.printShardingStatus() 一样,返回 sharded cluster 的状态信息. |
sh.stopBalancer() | 禁用 balancer 并等待进行中的均衡过程完成. |
sh.waitForBalancer() | 内部命令,等待均衡状态改变. |
sh.waitForBalancerOff() | 内部命令.等待均衡器停止运行. |
sh.waitForDLock() | 内部命令,等待指定的 sharded cluster 分布锁. |
sh.waitForPingChange() | Internal. Waits for a change in ping state from one of the mongos in the sharded cluster. |