MongoDB分库分表
MongoDB分片为我们提供了可扩展的解决方案,可以在大量服务器之间存储大量数据,而不是存储在单个服务器上。
什么是分库分表?
MongoDB分片为我们提供了可扩展的解决方案,可以在大量服务器之间存储大量数据,而不是存储在单个服务器上。实际上,将指数增长的数据存储在单台计算机上是不可行的。查询存储在单个服务器上的大量数据可能导致高资源利用率,并且可能无法提供令人满意的读写吞吐量。基本上,存在两种类型的缩放方法来处理系统中不断增长的数据:
垂直
水平
垂直扩展可通过添加功能更强大的处理器,升级RAM或为系统添加更多磁盘空间来增强单个服务器的性能。但是,在实际使用案例中使用现有的技术和硬件配置来应用垂直扩展可能会产生影响。
水平扩展可与添加更多服务器并在多台服务器上分配负载一起使用。由于每台机器都将处理整个数据集的子集,因此它提供了更高的效率和成本效益的解决方案,而不是部署高端硬件。但是,这需要对具有大量服务器的复杂基础结构进行额外维护。
Mongo DB分片适用于水平缩放技术。
分片组件
要在MongoDB中实现分片,需要以下组件:
Shard是一个Mongo实例,用于处理原始数据的子集。碎片必须部署在副本集中。
Mongos是Mongo实例,并充当客户端应用程序和分片群集之间的接口。它用作分片的查询路由器。
Config Server 是一个Mongo实例,它存储元数据信息和群集的配置详细信息。MongoDB要求将配置服务器部署为副本集。
分片架构
MongoDB集群由许多副本集组成。
每个副本集至少包含3个或更多mongo实例。分片群集可能包含多个mongo分片实例,并且每个分片实例都在分片副本集中工作。该应用程序与Mongos进行交互,而Mongos又与分片进行通信。因此,在分片中,应用程序永远不会直接与分片节点进行交互。查询路由器基于分片密钥在分片节点之间分配数据子集。
请按照以下步骤进行分片
第1步
启动副本集中的配置服务器并启用它们之间的复制。
mongod --configsvr --port 27019 --replSet rs0 --dbpath C:datadata1 --bind_ip localhost
mongod --configsvr --port 27018 --replSet rs0 --dbpath C:datadata2 --bind_ip localhost
mongod --configsvr --port 27017 --replSet rs0 --dbpath C:datadata3 --bind_ip localhost
第2步
在其中一台配置服务器上初始化副本集。
rs.initiate( { _id : "rs0", configsvr: true, members: [ { _id: 0, host: "IP:27017" }, { _id: 1, host: "IP:27018" }, { _id: 2, host: "IP:27019" } ] })
rs.initiate( { _id : "rs0", configsvr: true, members: [ { _id: 0, host: "IP:27017" }, { _id: 1, host: "IP:27018" }, { _id: 2, host: "IP:27019" } ] })
{
"ok" : 1,
"$gleStats" : {
"lastOpTime" : Timestamp(1593569257, 1),
"electionId" : ObjectId("000000000000000000000000")
},
"lastCommittedOpTime" : Timestamp(0, 0),
"$clusterTime" : {
"clusterTime" : Timestamp(1593569257, 1),
"signature" : {
"hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
"keyId" : NumberLong(0)
}
},
"operationTime" : Timestamp(1593569257, 1)
}
第3步
开始在副本集中分片服务器并启用它们之间的复制。
mongod --shardsvr --port 27020 --replSet rs1 --dbpath C:datadata4 --bind_ip localhost
mongod --shardsvr --port 27021 --replSet rs1 --dbpath C:datadata5 --bind_ip localhost
mongod --shardsvr --port 27022 --replSet rs1 --dbpath C:datadata6 --bind_ip localhost
MongoDB将第一个分片服务器初始化为Primary,要移动主分片服务器,请使用movePrimary方法。
第4步
在其中一个分片服务器上初始化副本集。
rs.initiate( { _id : "rs0", members: [ { _id: 0, host: "IP:27020" }, { _id: 1, host: "IP:27021" }, { _id: 2, host: "IP:27022" } ] })
rs.initiate( { _id : "rs0", members: [ { _id: 0, host: "IP:27020" }, { _id: 1, host: "IP:27021" }, { _id: 2, host: "IP:27022" } ] })
{
"ok" : 1,
"$clusterTime" : {
"clusterTime" : Timestamp(1593569748, 1),
"signature" : {
"hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
"keyId" : NumberLong(0)
}
},
"operationTime" : Timestamp(1593569748, 1)
}
第5步
启动分片集群的芒果
mongos --port 40000 --configdb rs0/localhost:27019,localhost:27018, localhost:27017
第6步
连接mongo路由服务器
mongo --port 40000
现在,添加分片服务器。
sh.addShard( "rs1/localhost:27020,localhost:27021,localhost:27022")
sh.addShard( "rs1/localhost:27020,localhost:27021,localhost:27022")
{
"shardAdded" : "rs1",
"ok" : 1,
"operationTime" : Timestamp(1593570212, 2),
"$clusterTime" : {
"clusterTime" : Timestamp(1593570212, 2),
"signature" : {
"hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
"keyId" : NumberLong(0)
}
}
}
步骤7
在mongo shell上,启用对数据库和集合的分片。
在数据库上启用分片
sh.enableSharding("geekFlareDB")
sh.enableSharding("geekFlareDB")
{
"ok" : 1,
"operationTime" : Timestamp(1591630612, 1),
"$clusterTime" : {
"clusterTime" : Timestamp(1591630612, 1),
"signature" : {
"hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
"keyId" : NumberLong(0)
}
}
}
第8步
要分片收集分片键(在本文的后面进行介绍)。
语法:sh.shardCollection("dbName.collectionName", { "key" : 1 } )
sh.shardCollection("geekFlareDB.geekFlareCollection", { "key" : 1 } )
{
"collectionsharded" : "geekFlareDB.geekFlareCollection",
"collectionUUID" : UUID("0d024925-e46c-472a-bf1a-13a8967e97c1"),
"ok" : 1,
"operationTime" : Timestamp(1593570389, 3),
"$clusterTime" : {
"clusterTime" : Timestamp(1593570389, 3),
"signature" : {
"hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
"keyId" : NumberLong(0)
}
}
}
请注意,如果该集合不存在,请如下创建。
db.createCollection("geekFlareCollection")
{
"ok" : 1,
"operationTime" : Timestamp(1593570344, 4),
"$clusterTime" : {
"clusterTime" : Timestamp(1593570344, 5),
"signature" : {
"hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
"keyId" : NumberLong(0)
}
}
}
第9步
将数据插入集合中。Mongo日志将开始增长,并指示平衡器正在运行,并试图在分片之间平衡数据。
第10步
最后一步是检查分片的状态。可以通过在Mongos路由节点上运行以下命令来检查状态。
分片状态
通过在mongo route节点上运行以下命令来检查分片状态。
sh.status()
mongos> sh.status()
--- Sharding Status ---
sharding version: {
"_id" : 1,
"minCompatibleVersion" : 5,
"currentVersion" : 6,
"clusterId" : ObjectId("5ede66c22c3262378c706d21")
}
shards:
{ "_id" : "rs1", "host" : "rs1/localhost:27020,localhost:27021,localhost:27022", "state" : 1 }
active mongoses:
"4.2.7" : 1
autosplit:
Currently enabled: yes
balancer:
Currently enabled: yes
Currently running: no
Failed balancer rounds in last 5 attempts: 5
Last reported error: Could not find host matching read preference { mode: "primary" } for set rs1
Time of Reported error: Tue Jun 09 2020 15:25:03 GMT+0530 (India Standard Time)
Migration Results for the last 24 hours:
No recent migrations
databases:
{ "_id" : "config", "primary" : "config", "partitioned" : true }
config.system.sessions
shard key: { "_id" : 1 }
unique: false
balancing: true
chunks:
rs1 1024
too many chunks to print, use verbose if you want to force print
{ "_id" : "geekFlareDB", "primary" : "rs1", "partitioned" : true, "version" : { "uuid" : UUID("a770da01-1900-401e-9f34-35ce595a5d54"), "lastMod" : 1 } }
geekFlareDB.geekFlareCol
shard key: { "key" : 1 }
unique: false
balancing: true
chunks:
rs1 1
{ "key" : { "$minKey" : 1 } } -->> { "key" : { "$maxKey" : 1 } } on : rs1 Timestamp(1, 0)
geekFlareDB.geekFlareCollection
shard key: { "product" : 1 }
unique: false
balancing: true
chunks:
rs1 1
{ "product" : { "$minKey" : 1 } } -->> { "product" : { "$maxKey" : 1 } } on : rs1 Timestamp(1, 0)
{ "_id" : "test", "primary" : "rs1", "partitioned" : false, "version" : { "uuid" : UUID("fbc00f03-b5b5-4d13-9d09-259d7fdb7289"), "lastMod" : 1 } }
资料分配
Mongos路由器根据分片密钥在分片之间分配负载,并平均分配数据。平衡器开始起作用。
在分片之间分发数据的关键组件是
平衡器在平衡分片节点之间的数据子集方面发挥作用。当Mongos服务器开始在各个分片之间分配负载时,Balance会运行。启动后,Balancer更均匀地分发数据。要检查平衡器运行状态,sh.status()或sh.getBalancerState() 或sh.isBalancerRunning()。
mongos> sh.isBalancerRunning()
true
mongos>
要么
mongos> sh.getBalancerState()
true
mongos>
插入数据后,我们可以注意到Mongos守护程序中有一些活动,说明该活动正在为特定分片移动一些块,依此类推,例如,平衡器将在尝试在各个分片之间平衡数据。运行平衡器可能会导致性能问题;因此建议在特定的平衡器窗口内运行平衡器。
mongos> sh.status()
--- Sharding Status ---
sharding version: {
"_id" : 1,
"minCompatibleVersion" : 5,
"currentVersion" : 6,
"clusterId" : ObjectId("5efbeff98a8bbb2d27231674")
}
shards:
{ "_id" : "rs1", "host" : "rs1/127.0.0.1:27020,127.0.0.1:27021,127.0.0.1:27022", "state" : 1 }
{ "_id" : "rs2", "host" : "rs2/127.0.0.1:27023,127.0.0.1:27024,127.0.0.1:27025", "state" : 1 }
active mongoses:
"4.2.7" : 1
autosplit:
Currently enabled: yes
balancer:
Currently enabled: yes
Currently running: yes
Failed balancer rounds in last 5 attempts: 5
Last reported error: Could not find host matching read preference { mode: "primary" } for set rs2
Time of Reported error: Wed Jul 01 2020 14:39:59 GMT+0530 (India Standard Time)
Migration Results for the last 24 hours:
1024 : Success
databases:
{ "_id" : "config", "primary" : "config", "partitioned" : true }
config.system.sessions
shard key: { "_id" : 1 }
unique: false
balancing: true
chunks:
rs2 1024
too many chunks to print, use verbose if you want to force print
{ "_id" : "geekFlareDB", "primary" : "rs2", "partitioned" : true, "version" : { "uuid" : UUID("a8b8dc5c-85b0-4481-bda1-00e53f6f35cd"), "lastMod" : 1 } }
geekFlareDB.geekFlareCollection
shard key: { "key" : 1 }
unique: false
balancing: true
chunks:
rs2 1
{ "key" : { "$minKey" : 1 } } -->> { "key" : { "$maxKey" : 1 } } on : rs2 Timestamp(1, 0)
{ "_id" : "test", "primary" : "rs2", "partitioned" : false, "version" : { "uuid" : UUID("a28d7504-1596-460e-9e09-0bdc6450028f"), "lastMod" : 1 } }
mongos>
分片密钥确定在分片之间分发分片集合文档的逻辑。分片键可以是要在要插入的集合的所有文档中都存在的索引字段或索引复合字段。数据将被划分为块,每个块将与基于范围的分片键相关联。根据范围查询路由器将决定哪个分片将存储块。
可以通过考虑以下五个属性来选择分片键:
基数
写分布
阅读分布
读取定位
阅读地点
理想的分片密钥可使MongoDB在所有分片之间平均分配负载。选择一个好的分片键非常重要。
删除分片节点
从群集中删除分片之前,要求用户确保将数据安全地迁移到其余分片。在删除所需的分片节点之前,MongoDB会负责将数据安全地排放到其他分片节点。
运行以下命令以删除所需的碎片。
第1步
首先,我们需要确定要删除的分片的主机名。下面的命令将列出群集中存在的所有分片以及分片的状态。
db.adminCommand( { listShards: 1 } )
mongos> db.adminCommand( { listShards: 1 } )
{
"shards" : [
{
"_id" : "rs1",
"host" : "rs1/127.0.0.1:27020,127.0.0.1:27021,127.0.0.1:27022",
"state" : 1
},
{
"_id" : "rs2",
"host" : "rs2/127.0.0.1:27023,127.0.0.1:27024,127.0.0.1:27025",
"state" : 1
}
],
"ok" : 1,
"operationTime" : Timestamp(1593572866, 15),
"$clusterTime" : {
"clusterTime" : Timestamp(1593572866, 15),
"signature" : {
"hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
"keyId" : NumberLong(0)
}
}
}
第2步
发出以下命令以从集群中除去所需的分片。发出平衡器后,平衡器将负责清除排空分片节点中的块,然后平衡其余分片节点之间剩余块的分布。
db.adminCommand( { removeShard: "shardedReplicaNodes" } )
mongos> db.adminCommand( { removeShard: "rs1/127.0.0.1:27020,127.0.0.1:27021,127.0.0.1:27022" } )
{
"msg" : "draining started successfully",
"state" : "started",
"shard" : "rs1",
"note" : "you need to drop or movePrimary these databases",
"dbsToMove" : [ ],
"ok" : 1,
"operationTime" : Timestamp(1593572385, 2),
"$clusterTime" : {
"clusterTime" : Timestamp(1593572385, 2),
"signature" : {
"hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
"keyId" : NumberLong(0)
}
}
}
第3步
要检查排放碎片的状态,请再次发出相同的命令。
db.adminCommand( { removeShard: "rs1/127.0.0.1:27020,127.0.0.1:27021,127.0.0.1:27022" } )
我们需要等到数据清除完成。消息和状态字段将显示是否已完成数据清除,如下所示
"msg" : "draining ongoing",
"state" : "ongoing",
我们还可以使用命令检查状态sh.status()。一旦删除分片节点将不会反映在输出中。但是,如果持续进行排空,则分片节点将具有排空状态为true。
第4步
继续使用上面相同的命令检查排水状态,直到所需的碎片完全清除。
完成后,命令的输出将反映消息和完成状态。
"msg" : "removeshard completed successfully",
"state" : "completed",
"shard" : "rs1",
"ok" : 1,
第5步
最后,我们需要检查集群中剩余的碎片。要检查状态,请输入sh.status() 或db.adminCommand( { listShards: 1 } )
mongos> db.adminCommand( { listShards: 1 } )
{
"shards" : [
{
"_id" : "rs2",
"host" : "rs2/127.0.0.1:27023,127.0.0.1:27024,127.0.0.1:27025",
"state" : 1
}
],
"ok" : 1,
"operationTime" : Timestamp(1593575215, 3),
"$clusterTime" : {
"clusterTime" : Timestamp(1593575215, 3),
"signature" : {
"hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
"keyId" : NumberLong(0)
}
}
}
在这里,我们可以看到已删除的分片不再存在于分片列表中。
分片胜过复制的好处
在复制中,主节点处理所有写操作,而辅助服务器则需要维护备份副本或提供只读操作。但是,在与副本集进行分片时,负载将分散在许多服务器之间。
单个副本集限制为12个节点,但分片数量没有限制。
复制需要高端硬件或垂直扩展来处理大型数据集,与在分片中添加其他服务器相比,这太昂贵了。
在复制中,可以通过添加更多从属/辅助服务器来增强读取性能,而在分片中,可以通过添加更多分片节点来增强读取和写入性能。
分片限制
分片群集不支持跨分片的唯一索引,直到唯一索引带有完整分片键的前缀为止。
一个或多个文档上的分片集合的所有更新操作必须在查询中包含分片键或_id字段。
如果集合的大小不超过指定的阈值,则可以对其进行分片。可以基于所有分片密钥的平均大小和配置的块大小来估计此阈值。
分片包括最大收集大小或分割数的操作限制。
选择错误的分片键会导致性能问题。
结论
MongoDB提供内置的分片,以实现大型数据库而不会影响性能。我希望以上内容可以帮助您设置MongoDB分片。