MongoDB/分片

分片机制:
分片的机制:一开始插入数据时,数据是只插入到其中一块分片上的,插入完毕后,mongodb内部开始在各片之间进行数据的移动,这个过程可能不是立即的,mongodb足够智能会根据当前负载决定是立即进行移动还是稍后移动。这种分片机制,节省了人工维护成本,但是由于其是优先往某个片上插入,等到chunk失衡时,再移动chunk,并且随着数据的增多,shard的实例之间,有chunk来回移动的现象,这将会为服务器带来很大的IO开销。

分片:
启动Shard Server:

mkdir  usr/local/data/shard/s0 usr/local/data/shard/s1  #创建数据目录
mkdir  usr/local/data/shard/log # 创建日志目录
./bin/mongod --port 27017 --dbpath /usr/local/mongodb/data/shard/s0 --fork --logpath /usr/local/mongodb/data/shard/log/s0.log # 启动Shard Server实例1
./bin/mongod --port 27018 --dbpath /usr/local/mongodb/data/shard/s1 --fork --logpath /usr/local/mongodb/data/shard/log/s1.log # 启动Shard Server实例2

启动Config Server

mkdir -p ./data/shard/config #创建数据目录
./bin/mongod --port 27027 --dbpath /usr/local/mongodb/data/shard/config --fork --logpath /usr/local/mongodb/

启动Route Process
mongos启动参数中,chunkSize这一项是用来指定chunk的大小的,单位是MB,默认大小为200MB,为了方便测试Sharding效果,我们把chunkSize指定为 1MB。意思是当这个分片中插入的数据大于1M时开始进行数据转移

./bin/mongos --port 4000 --configdb localhost:27027 --fork --logpath /usr/local/mongodb/data/shard/log/route.log --chunkSize=1 

修改chunk大小
mongos> db.settings.save( { _id:"chunksize", value: 1 } )

配置Sharding:
登录到mongos,添加Shard节点
在为collection分片前,必须让该集合所属的数据库具有分片的功能,一旦你开启了某个数据库的分片,MongoDB会分配一个主片。

./bin/mongo --port 40000 
> use admin #此操作需要连接admin库
> db.runCommand({ addshard:"localhost:27017" }) #添加 Shard Server 或者用 sh.addshard()命令来添加,下同;
{ "shardAdded" : "shard0000", "ok" : 1 }
> db.runCommand({ addshard:"localhost:27018" })
{ "shardAdded" : "shard0001", "ok" : 1 }

> db.runCommand({ enablesharding:"test" }) #设置分片存储的数据库
{ "ok" : 1 }

> db.runCommand({ shardcollection: "test.users", key: { id:1 }}) 
# 设置分片的集合名称。且必须指定Shard Key,系统会自动创建索引,然后根据这个shard Key来计算。
# 实际中尤其不要轻易选择自增_id作为片键,片键也可以是多个字段的组合。
{ "collectionsharded" : "test.users", "ok" : 1 }

 > sh.status(); #查看片的状态
 > printShardingStatus(db.getSisterDB("config"),1); # 查看片状态(完整版);
 > db.stats(); # 查看所有的分片服务器状态
 #查看分片后的情况
 > use config
 switched to db config
 > db.databases.find()
 { "_id" : "test", "primary" : "shard0000", "partitioned" : true }
 > db.chunks.find()
 { "_id" : "test.user-_id_MinKey", "ns" : "test.user", "min" : { "_id" : { "$minKey" : 1 } }, "max" : { "_id" : { "$maxKey" : 1 } }, "shard" : "shard0000", "lastmod" : Timestamp(1, 0), "lastmodEpoch" : ObjectId("5677cc4015fdf4f1ffbb15bd") }

如上建库的时候要登陆mongos来建,建好一个库,mongos来自动给你分配是建立在哪个shard上,但你也可以运行命令来把它移动到别的shard上。例如:在mongos上建立了一个库叫recsys0库。运行db.printShardingStatus()命令我们可以看到这个recsys0是建到了哪个shard,如果建到了shard4,而你想改到shard1,那么可以运行命令,db.runCommand( { movePrimary: “recsys0”, to: “shard1” }),这个库就会从shard4挪到shard1上。可通过db.printShardingStatus()再次检查。

删除分片:
db.runCommand({"removeshard":"192.168.245.131:27017"})

对已存在数据分片:
假设现在存在一个数据很大的children数据库,在192.168.245.129:27019上面,需要将这些数据进行分片。
1)连接到mongos实例,将192.168.245.129:27019添加到分片集群中。

mongos> sh.addShard("192.168.245.129:27019")
{ "shardAdded" : "shard0004", "ok" : 1 }

注意集群分片中不能与新添加的分片中有相同的数据库,否则会报错。

在需要的数据库上开启分片功能

mongos> sh.enableSharding("children")
{ "ok" : 1 }

对children数据库下的集合进行分片。注意:对已存在的数据进行分片,一定要保证shard key字段是索引。
mongos> sh.shardCollection("children.children",{"user_id":1})

shard key:
以上分片都使用了MongoDB本身提供了Auto-Sharding的功能,Auto-Sharding的一些缺陷:

  • 如果选择了单一的Sharding Key,会造成分片不均衡,无法充分利用每个分片集群的能力。为了弥补单一Sharding Key的缺点,引入复合Sharing Key,然而复合Sharding Key会造成性能的消耗。
  • count值计算不准确的问题,数据Chunk在分片之间迁移时,特定数据可能会被计算2次,造成count值计算偏大的问题;
  • Balancer的稳定性&智能性问题,Sharing的迁移发生时间不确定,一旦发生数据迁移会造成整个系统的吞吐量急剧下降。为了应对Sharding迁移的不确定性,我们可以强制指定在业务访问的低峰期做Sharding迁移。

如何选择shard key:
参考1
参考2

手动分片:

由程序来判断:
关闭自动分片:sh.stopBalancer()
只是在4个不同的shard上建立4个库。至于读写操作会对应哪个库,由程序来判断。

MongoDB还带一种手动预分片:
用split命令对空集合进行手动的切分

mongos> use admin
switched to db admin
mongos> db.runCommand({"enablesharding":"myapp"})
mongos> db.runCommand({"shardcollection":"myapp.users","key":{"email":1}})
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 } } );
      }
}

利用moveChunk命令手动的移动分割的块:

 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]})
  }
 }

每个分片设置成副本集:
参考

    原文作者:bluebule
    原文地址: https://www.jianshu.com/p/baed80df9300
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞