分片简介
分片 (sharding)是指将数据拆分,将其分散存放在不同的机器上的过程 。有时也用分区 (partitioning)来表示这个概念 。将数据分散到不同的机器上,不需要功能 强大的大型计算机就可以储存更多的数据,处理更大的负载 。
几乎所有数据库软件都能进行手动分片 (manual sharding)。应用需要维护与若干不 同数据库服务器的连接 ,每个连接还是完全独立的 。应用程序管理不同服务器上不 同数据的存储 ,还管理在合适 的数据库上查询数据的工作 。这种方能可以很好地工作 ,但是非常难以维护,比如向集群添加节点或从集群删除节点都很困难 ,调整数据分布和负载模式也不轻松 。
MongoDB支持自动分片(autosharding),可以使数据库架构对应用程序不可见 ,也可以简化系统管理。对应用程序而言,好像始终在使用 一个单机的MongoDB服务器一 样。另一方面,MongoDB自动处理数据在分片上的分布,也更容易添加和删除分片。不管从开发角度还是运营角度来说 ,分片都是最困难最复杂的。
一.MongoDB 分片架构
image.png进程mongos负责管理应用发送到MongoDB服务器的所有命令,并且该守护进程将重新发送跨多个分片的查询到多个服务器,再将结果聚集在一起。
MongoDB在集合级别实现分片,而不是数据库级别。在许多系统中,只有一个或两个集合可以增长到需要使用分片的地步。因此应该理智地使用分片;如果不需要的话,就不要为较小的集合增加管理分布数据的开销。
分片系统使用分片键将数据映射到块,块是文档键的逻辑连续范围。每个块标志着分片键值特定连续范围内的许多文档;这些值使mongos控制器可以快速找到包含它所需的文档的块。然后MongoDB分片系统将把块存储在可用的分片系统中;配置服务器将记录每个块存储的分片服务器的位置。这是分片实现的一个重要特性,因为通过它可以从集合中添加和删除分片,而不需要备份和恢复数据。
当在集群中添加新的分片时,该系统将会把许多块迁移到新的服务器集合中,从而平均地分散数据。类似地,从集群中删除分片时,分片控制器将会从即将离线的分片中抽取所有的块,并重新将它们分散到剩下的分片服务器中。
MongoDB的分片设置还需要存储分片服务器的配置,以及集群中每个分片服务器的信息。为了支持该功能,需要使用一台称为配置服务器的MongoDB服务器;该服务器实例是一个以特殊角色运行的mongod服务器。正如之前解释的,配置服务器还可以用作目录,通过它可以找到每个块的位置。在集群中可以具有1台(开发)或3台(生产)配置服务器。推荐在生产环境中使用3台配置服务器,因为配置服务器的崩溃将意味着无法决定分片数据存储在哪个分片中。
二.分片配置
环境如下:
IP | 主机名 | 类别 |
---|---|---|
10.31.1.123 | 10-31-1-123 | 分片1(mongod) |
10.31.1.124 | 10-31-1-124 | 分片0(mongod) |
10.31.1.125 | 10-31-1-125 | 配置服务器(mongod) |
10.31.1.126 | 10-31-1-126 | 分片控制器(mongos) |
2.1 启动配置服务器
配置文件如下:
processManagement:
fork: true
storage:
dbPath: "/usr/local/mongodb/data/db"
journal:
enabled: true
systemLog:
destination: file
path: "/usr/local/mongodb/data/logs/mongodb.log"
logAppend: true
net:
port: 27017
bindIp: 0.0.0.0
sharding:
clusterRole: configsvr
replication:
replSetName: csRS
启动mongod
mongod -f /etc/mongodb.conf &
初始化配置文件;
[root@10-31-1-125 ~]# mongo
MongoDB shell version v4.2.10
connecting to: mongodb://127.0.0.1:27017/?compressors=disabled&gssapiServiceName=mongodb
Implicit session: session { "id" : UUID("c48649a7-0f44-4157-ba28-ec00b584e795") }
MongoDB server version: 4.2.10
>
>
> rs.initiate();
{
"info2" : "no configuration specified. Using a default configuration for the set",
"me" : "10-31-1-125:27017",
"ok" : 1,
"$gleStats" : {
"lastOpTime" : Timestamp(1605089380, 1),
"electionId" : ObjectId("000000000000000000000000")
},
"lastCommittedOpTime" : Timestamp(0, 0)
}
csRS:OTHER>
csRS:PRIMARY>
csRS:PRIMARY>
2.2 启动分片控制器
processManagement:
fork: true
systemLog:
destination: file
path: "/usr/local/mongodb/data/logs/mongodb.log"
logAppend: true
net:
port: 27017
bindIp: 0.0.0.0
sharding:
configDB: csRS/10.31.1.125:27017
启动mongos实例:
mongos -f /etc/mongod.conf &
2.3 启动两个分片服务器
配置文件:
processManagement:
fork: true
storage:
dbPath: "/usr/local/mongodb/data/db"
journal:
enabled: true
systemLog:
destination: file
path: "/usr/local/mongodb/data/logs/mongodb.log"
logAppend: true
net:
port: 27017
bindIp: 0.0.0.0
sharding:
clusterRole: shardsvr
启动mongod
mongod -f /etc/mongodb.conf &
2.4 在分片控制器中添加分片
mongos> use admin;
switched to db admin
mongos>
mongos>
mongos> sh.addShard("10.31.1.124:27017");
{
"shardAdded" : "shard0000",
"ok" : 1,
"operationTime" : Timestamp(1605091884, 7),
"$clusterTime" : {
"clusterTime" : Timestamp(1605091884, 7),
"signature" : {
"hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
"keyId" : NumberLong(0)
}
}
}
mongos> sh.addShard("10.31.1.123:27017");
{
"shardAdded" : "shard0001",
"ok" : 1,
"operationTime" : Timestamp(1605091921, 3),
"$clusterTime" : {
"clusterTime" : Timestamp(1605091921, 3),
"signature" : {
"hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
"keyId" : NumberLong(0)
}
}
}
mongos>
两台分片服务器现在已经被激活,下面检查分片服务器的状态
mongos> sh.status();
--- Sharding Status ---
sharding version: {
"_id" : 1,
"minCompatibleVersion" : 5,
"currentVersion" : 6,
"clusterId" : ObjectId("5fabb772a54a5ab37df10b43")
}
shards:
{ "_id" : "shard0000", "host" : "10.31.1.124:27017", "state" : 1 }
{ "_id" : "shard0001", "host" : "10.31.1.123:27017", "state" : 1 }
active mongoses:
"4.2.10" : 1
autosplit:
Currently enabled: yes
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" : "config", "primary" : "config", "partitioned" : true }
mongos>
现在分片环境已经可以正常运行,但还没有分片数据。
2.5 设置块大小
mongos> use config
switched to db config
mongos> db.settings.save({"_id":"chunksize","value":1});
WriteResult({ "nMatched" : 0, "nUpserted" : 1, "nModified" : 0, "_id" : "chunksize" })
mongos>
设置块大小为1M是方便实验,不然就需要插入大量数据。
2.6 创建分片集合
创建一个名为testdb的数据库,然后在该数据库中激活一个名为t2的集合,赋予id键,用于分片
mongos> use admin
switched to db admin
mongos> db.runCommand({"enablesharding":"testdb"})
{
"ok" : 1,
"operationTime" : Timestamp(1605092685, 3),
"$clusterTime" : {
"clusterTime" : Timestamp(1605092685, 3),
"signature" : {
"hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
"keyId" : NumberLong(0)
}
}
}
mongos> use testdb
switched to db testdb
mongos>
mongos> db.t1.createIndex({"id":1});
{
"raw" : {
"10.31.1.123:27017" : {
"createdCollectionAutomatically" : false,
"numIndexesBefore" : 1,
"numIndexesAfter" : 2,
"ok" : 1
}
},
"ok" : 1,
"operationTime" : Timestamp(1605092550, 3),
"$clusterTime" : {
"clusterTime" : Timestamp(1605092550, 7),
"signature" : {
"hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
"keyId" : NumberLong(0)
}
}
}
mongos> sh.shardCollection("testdb.t1",{"id":1});
{
"collectionsharded" : "testdb.t1",
"collectionUUID" : UUID("209d7c02-2267-4d9f-a18d-d81debb4397b"),
"ok" : 1,
"operationTime" : Timestamp(1605092706, 8),
"$clusterTime" : {
"clusterTime" : Timestamp(1605092706, 8),
"signature" : {
"hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
"keyId" : NumberLong(0)
}
}
}
2.7 向分片添加数据
db.runCommand({"enablesharding":"testdb"})
db.t2.createIndex({"id":1});
sh.shardCollection("testdb.t2",{"id":1});
for (var i = 1; i <= 1000000; i++) db.t2.insert({id : i, name : 'aaa',date: new Date()});
测试记录:
mongos> use testdb
switched to db testdb
mongos>
mongos> db.t2.createIndex({"id":1});
{
"raw" : {
"10.31.1.123:27017" : {
"createdCollectionAutomatically" : false,
"numIndexesBefore" : 1,
"numIndexesAfter" : 2,
"ok" : 1
}
},
"ok" : 1,
"operationTime" : Timestamp(1605093950, 4),
"$clusterTime" : {
"clusterTime" : Timestamp(1605093950, 4),
"signature" : {
"hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
"keyId" : NumberLong(0)
}
}
}
mongos> sh.shardCollection("testdb.t2",{"id":1});
{
"collectionsharded" : "testdb.t2",
"collectionUUID" : UUID("0c6a48a2-f4c7-4945-a5d5-98c9122fa2a5"),
"ok" : 1,
"operationTime" : Timestamp(1605093959, 9),
"$clusterTime" : {
"clusterTime" : Timestamp(1605093959, 9),
"signature" : {
"hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
"keyId" : NumberLong(0)
}
}
}
mongos>
mongos> for (var i = 1; i <= 1000000; i++) db.t2.insert({id : i, name : 'aaa',date: new Date()});
WriteResult({ "nInserted" : 1 })
mongos>
我看了下,数据分布得也太不平均了
--10.31.1.124
> use testdb
switched to db testdb
> db.t2.count()
16951
--10.31.1.123
> use testdb
switched to db testdb
>
> db.t2.count()
983049
--10.31.1.126
mongos> use testdb
switched to db testdb
mongos>
mongos> db.t2.count()
1000000
注意,在每台分片服务器中可能看到不同的文档数目,这取决于检查每台分片服务器的时间。mongos实例开始会在一个分片中初始化所有的块,但随着时间的推移,将对数据集中的数据进行调整,通过移动块的方式将数据平均地分布到所有分片服务器中。因此,指定分片服务器中的记录数可能会不断变化。
三.维护
3.1 添加新分片
3.1.1 启动新的mongod
创建新mongod实例10.31.1.123:27018,配置文件
processManagement:
fork: true
storage:
dbPath: "/usr/local/mongodb/27018/data/db"
journal:
enabled: true
systemLog:
destination: file
path: "/usr/local/mongodb/27018/data/logs/mongodb.log"
logAppend: true
net:
port: 27018
bindIp: 0.0.0.0
sharding:
clusterRole: shardsvr
启动10.31.1.123 上端口为27018的mongodb
processManagement:
fork: true
storage:
dbPath: "/usr/local/mongodb/27018/data/db"
journal:
enabled: true
systemLog:
destination: file
path: "/usr/local/mongodb/27018/data/logs/mongodb.log"
logAppend: true
net:
port: 27017
bindIp: 0.0.0.0
sharding:
clusterRole: shardsvr
3.1.2 添加新的分片
mongos> use admin
switched to db admin
mongos> sh.addShard("10.31.1.123:27018");
{
"shardAdded" : "shard0002",
"ok" : 1,
"operationTime" : Timestamp(1605145744, 3),
"$clusterTime" : {
"clusterTime" : Timestamp(1605145744, 3),
"signature" : {
"hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
"keyId" : NumberLong(0)
}
}
}
mongos>
3.1.3 查询分片状态
mongos> sh.status();
--- Sharding Status ---
sharding version: {
"_id" : 1,
"minCompatibleVersion" : 5,
"currentVersion" : 6,
"clusterId" : ObjectId("5fabb772a54a5ab37df10b43")
}
shards:
{ "_id" : "shard0000", "host" : "10.31.1.124:27017", "state" : 1 }
{ "_id" : "shard0001", "host" : "10.31.1.123:27017", "state" : 1 }
{ "_id" : "shard0002", "host" : "10.31.1.123:27018", "state" : 1 }
active mongoses:
"4.2.10" : 1
autosplit:
Currently enabled: yes
balancer:
Currently enabled: yes
Currently running: no
Failed balancer rounds in last 5 attempts: 0
Migration Results for the last 24 hours:
615 : Success
databases:
{ "_id" : "config", "primary" : "config", "partitioned" : true }
config.system.sessions
shard key: { "_id" : 1 }
unique: false
balancing: true
chunks:
shard0000 466
shard0001 465
shard0002 93
too many chunks to print, use verbose if you want to force print
{ "_id" : "test", "primary" : "shard0000", "partitioned" : false, "version" : { "uuid" : UUID("7a1ab310-f0cb-471a-b14e-fbb17e9ed77c"), "lastMod" : 1 } }
{ "_id" : "testdb", "primary" : "shard0001", "partitioned" : true, "version" : { "uuid" : UUID("99a23d0d-61d3-4527-bee5-4eb2cf7195b3"), "lastMod" : 1 } }
testdb.t1
shard key: { "id" : 1 }
unique: false
balancing: true
chunks:
shard0000 3
shard0001 2
shard0002 2
{ "id" : { "$minKey" : 1 } } -->> { "id" : 2 } on : shard0000 Timestamp(4, 1)
{ "id" : 2 } -->> { "id" : 16914 } on : shard0002 Timestamp(5, 0)
{ "id" : 16914 } -->> { "id" : 25370 } on : shard0002 Timestamp(6, 0)
{ "id" : 25370 } -->> { "id" : 33874 } on : shard0001 Timestamp(6, 1)
{ "id" : 33874 } -->> { "id" : 42330 } on : shard0000 Timestamp(3, 2)
{ "id" : 42330 } -->> { "id" : 50821 } on : shard0000 Timestamp(3, 3)
{ "id" : 50821 } -->> { "id" : { "$maxKey" : 1 } } on : shard0001 Timestamp(4, 0)
testdb.t2
shard key: { "id" : 1 }
unique: false
balancing: true
chunks:
shard0000 2
shard0001 3
shard0002 2
{ "id" : { "$minKey" : 1 } } -->> { "id" : 2 } on : shard0002 Timestamp(6, 0)
{ "id" : 2 } -->> { "id" : 16914 } on : shard0002 Timestamp(5, 0)
{ "id" : 16914 } -->> { "id" : 25370 } on : shard0001 Timestamp(5, 1)
{ "id" : 25370 } -->> { "id" : 33876 } on : shard0001 Timestamp(2, 3)
{ "id" : 33876 } -->> { "id" : 42332 } on : shard0000 Timestamp(6, 1)
{ "id" : 42332 } -->> { "id" : 50826 } on : shard0000 Timestamp(3, 3)
{ "id" : 50826 } -->> { "id" : { "$maxKey" : 1 } } on : shard0001 Timestamp(4, 0)
{ "_id" : "zqs", "primary" : "shard0000", "partitioned" : false, "version" : { "uuid" : UUID("418edb46-901e-4196-8ff3-b372a7283e41"), "lastMod" : 1 } }
mongos>
3.1.4 查询三个分片的记录数
分片系统将数据重新平均分布到扩展后的集群中。随着时间的推移,分片系统将从shard0和shard1存储服务器中迁移出一些块,从而将数据平均分布在组成集群的三台服务器中。该过程将自动发生,即使testcollection集合中没有新的数据插入,也会执行。在此情况下,mongos分片控制器将移动一些块到新的服务器,然后将它们注册到配置服务器中。
分片数据迁移会持续一段时间,这期间看到的数据可能是不准确的
-- 10.31.1.126
-- 刚开始数据异常,一段时间过后,数据恢复正常
mongos> use testdb
switched to db testdb
mongos> db.t2.count()
1016913
mongos> db.t2.count()
1000000
-- 10.31.1.123:27018
> use testdb
switched to db testdb
> db.t2.count()
16913
-- 10.31.1.123:27017
> db.t2.count()
966137
>
-- 10.31.1.124
> use testdb
switched to db testdb
>
> db.t2.count()
16950
>
4.2 删除分片
4.2.1 删除分片
mongos> use admin
switched to db admin
mongos> db.runCommand({removeShard : "10.31.1.123:27018"});
{
"msg" : "draining started successfully",
"state" : "started",
"shard" : "shard0002",
"note" : "you need to drop or movePrimary these databases",
"dbsToMove" : [ ],
"ok" : 1,
"operationTime" : Timestamp(1605146931, 2),
"$clusterTime" : {
"clusterTime" : Timestamp(1605146931, 2),
"signature" : {
"hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
"keyId" : NumberLong(0)
}
}
}
mongos>
命令removeShard返回一条信息,表示移除已经启动。另外还表示mongos已经开始重新将目标分片服务器中的块移到集群中的其它分片服务器。该过程被称为清空分片服务器。还列出了清空过程中不能移出分片服务器的数据库,这些都在dbsToMove数组中。
4.2.2 验证
如db.runCommand({listshards:1}); 出来的没有刚才的分片,则证明刚才的分片已经清理成功
我等待了半个小时,10.31.1.123:27018依旧显示 draining为true,但是数据是正常的了
而且奇特的是,又回到了当初2个分片的时候的数据,而且分布特别不均匀,神奇
mongos> use admin
switched to db admin
mongos> db.runCommand({listshards:1});
{
"shards" : [
{
"_id" : "shard0000",
"host" : "10.31.1.124:27017",
"state" : 1
},
{
"_id" : "shard0001",
"host" : "10.31.1.123:27017",
"state" : 1
},
{
"_id" : "shard0002",
"host" : "10.31.1.123:27018",
"draining" : true,
"state" : 1
}
],
"ok" : 1,
"operationTime" : Timestamp(1605147044, 11),
"$clusterTime" : {
"clusterTime" : Timestamp(1605147044, 11),
"signature" : {
"hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
"keyId" : NumberLong(0)
}
}
}
mongos>
验证几个分片的数据
-- 10.31.1.126
mongos> use testdb
switched to db testdb
mongos> db.t2.count()
1000000
-- 10.31.1.123:27017
> use testdb
switched to db testdb
> db.t2.count()
983049
-- 10.31.1.123:27018
> use testdb
switched to db testdb
> db.t2.count()
0
-- 10.31.1.124:27017
> use testdb
switched to db testdb
> db.t2.count()
16951
参考
1.《MongoDB大数据处理权威指南》(第3版)
2.https://blog.csdn.net/wzy0623/article/details/83180943
3.https://docs.mongodb.com/v4.2/sharding/
网友评论