1. 分片简介
1.1 分片概念
分片是指跨机器拆分数据的过程,有时也会用术语分区。MongoDB既可以手工分片,也支持自动分片
1.2 理解集群组件
分片的目标之一是由多个分片组成的集群对应用程序来说就像是一台服务器。为了此实现,需要在分片前面运行一个或多个称为mongos的路由进程。mongos维护着一个“目录”,指明了哪个分片包含哪些数据。
应用程序可以正常连接到此路由器并发出请求。路由服务器哪些数据在哪些分片上,可以将请求转发到适当的分片。如果有对请求的响应,路由服务器会收集他们,并在必要时进行合并,然后在发送给应用程序
1.3 在单机集群上进行分片
# mongo --nodb --norc
MongoDB shell version v4.2.6
>st = ShardingTest({
name: "one-min-shards",
chunkSize:1,
shards: 2,
rs:{
nodes:3,
oplogSize:10
},
other: {
enableBalancer:true
}
})
当ShardingTest完成集群设置后,将启动并运行10个进程:两个副本集(各有3个节点)、一个配置服务器副本集(有3个节点)、一个mongos
在新终端窗口执行ps -ef|grep mongo
可以看到如下信息:
00:00:09 /usr/bin/mongod --oplogSize 10 --port 20000 --replSet one-min-shards-rs0 --dbpath /data/db/one-min-shards-rs0-0 --shardsvr --setParameter migrationLockAcquisitionMaxWaitMS=30000 --setParameter writePeriodicNoops=false --setParameter numInitialSyncConnectAttempts=60 --bind_ip 0.0.0.0 --setParameter enableTestCommands=1 --setParameter disableLogicalSessionCacheRefresh=true --setParameter orphanCleanupDelaySecs=1
root 105 66 1 21:57 pts/0 00:00:10 /usr/bin/mongod --oplogSize 10 --port 20001 --replSet one-min-shards-rs0 --dbpath /data/db/one-min-shards-rs0-1 --shardsvr --setParameter migrationLockAcquisitionMaxWaitMS=30000 --setParameter writePeriodicNoops=false --setParameter numInitialSyncConnectAttempts=60 --bind_ip 0.0.0.0 --setParameter enableTestCommands=1 --setParameter disableLogicalSessionCacheRefresh=true --setParameter orphanCleanupDelaySecs=1
root 140 66 1 21:57 pts/0 00:00:10 /usr/bin/mongod --oplogSize 10 --port 20002 --replSet one-min-shards-rs0 --dbpath /data/db/one-min-shards-rs0-2 --shardsvr --setParameter migrationLockAcquisitionMaxWaitMS=30000 --setParameter writePeriodicNoops=false --setParameter numInitialSyncConnectAttempts=60 --bind_ip 0.0.0.0 --setParameter enableTestCommands=1 --setParameter disableLogicalSessionCacheRefresh=true --setParameter orphanCleanupDelaySecs=1
root 293 66 1 21:58 pts/0 00:00:09 /usr/bin/mongod --oplogSize 10 --port 20003 --replSet one-min-shards-rs1 --dbpath /data/db/one-min-shards-rs1-0 --shardsvr --setParameter migrationLockAcquisitionMaxWaitMS=30000 --setParameter writePeriodicNoops=false --setParameter numInitialSyncConnectAttempts=60 --bind_ip 0.0.0.0 --setParameter enableTestCommands=1 --setParameter disableLogicalSessionCacheRefresh=true --setParameter orphanCleanupDelaySecs=1
root 328 66 1 21:58 pts/0 00:00:09 /usr/bin/mongod --oplogSize 10 --port 20004 --replSet one-min-shards-rs1 --dbpath /data/db/one-min-shards-rs1-1 --shardsvr --setParameter migrationLockAcquisitionMaxWaitMS=30000 --setParameter writePeriodicNoops=false --setParameter numInitialSyncConnectAttempts=60 --bind_ip 0.0.0.0 --setParameter enableTestCommands=1 --setParameter disableLogicalSessionCacheRefresh=true --setParameter orphanCleanupDelaySecs=1
root 363 66 1 21:58 pts/0 00:00:09 /usr/bin/mongod --oplogSize 10 --port 20005 --replSet one-min-shards-rs1 --dbpath /data/db/one-min-shards-rs1-2 --shardsvr --setParameter migrationLockAcquisitionMaxWaitMS=30000 --setParameter writePeriodicNoops=false --setParameter numInitialSyncConnectAttempts=60 --bind_ip 0.0.0.0 --setParameter enableTestCommands=1 --setParameter disableLogicalSessionCacheRefresh=true --setParameter orphanCleanupDelaySecs=1
root 525 66 2 21:58 pts/0 00:00:11 /usr/bin/mongod --oplogSize 40 --port 20006 --replSet one-min-shards-configRS --dbpath /data/db/one-min-shards-configRS-0 --journal --configsvr --storageEngine wiredTiger --setParameter writePeriodicNoops=false --setParameter numInitialSyncConnectAttempts=60 --bind_ip 0.0.0.0 --setParameter enableTestCommands=1 --setParameter disableLogicalSessionCacheRefresh=true --setParameter orphanCleanupDelaySecs=1
root 567 66 1 21:58 pts/0 00:00:11 /usr/bin/mongod --oplogSize 40 --port 20007 --replSet one-min-shards-configRS --dbpath /data/db/one-min-shards-configRS-1 --journal --configsvr --storageEngine wiredTiger --setParameter writePeriodicNoops=false --setParameter numInitialSyncConnectAttempts=60 --bind_ip 0.0.0.0 --setParameter enableTestCommands=1 --setParameter disableLogicalSessionCacheRefresh=true --setParameter orphanCleanupDelaySecs=1
root 609 66 1 21:58 pts/0 00:00:10 /usr/bin/mongod --oplogSize 40 --port 20008 --replSet one-min-shards-configRS --dbpath /data/db/one-min-shards-configRS-2 --journal --configsvr --storageEngine wiredTiger --setParameter writePeriodicNoops=false --setParameter numInitialSyncConnectAttempts=60 --bind_ip 0.0.0.0 --setParameter enableTestCommands=1 --setParameter disableLogicalSessionCacheRefresh=true --setParameter orphanCleanupDelaySecs=1
root 792 66 0 21:58 pts/0 00:00:00 /usr/bin/mongos -v --port 20009 --configdb one-min-shards-configRS/2bffe09ec303:20006,2bffe09ec303:20007,2bffe09ec303:20008 --bind_ip 0.0.0.0 --setParameter enableTestCommands=1 --setParameter disableLogicalSessionCacheRefresh=true
整个集群会将日志存储到当前shell中,因此打开第二个终端窗口,并启动另一个mongo shell:
# mongo --port 20009
mongos> use accounts
switched to db accounts
mongos> for(var i=0; i<100000;i++){db.users.insert({'username': 'user'+i, 'created_at': new Date()})}
mongos> db.users.count()
100000
mongos> sh.status()
--- Sharding Status ---
sharding version: {
"_id" : 1,
"minCompatibleVersion" : 5,
"currentVersion" : 6,
"clusterId" : ObjectId("62d2c414601aeb7a88c169f7")
}
shards:
{ "_id" : "one-min-shards-rs0", "host" : "one-min-shards-rs0/2bffe09ec303:20000,2bffe09ec303:20001,2bffe09ec303:20002", "state" : 1 }
{ "_id" : "one-min-shards-rs1", "host" : "one-min-shards-rs1/2bffe09ec303:20003,2bffe09ec303:20004,2bffe09ec303:20005", "state" : 1 }
active mongoses:
"4.2.6" : 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" : "accounts", "primary" : "one-min-shards-rs1", "partitioned" : false, "version" : { "uuid" : UUID("ba658ebe-bbd1-44cd-b2c9-cb48d3810551"), "lastMod" : 1 } }
{ "_id" : "config", "primary" : "config", "partitioned" : true }
config.system.sessions
shard key: { "_id" : 1 }
unique: false
balancing: true
chunks:
one-min-shards-rs0 1
{ "_id" : { "$minKey" : 1 } } -->> { "_id" : { "$maxKey" : 1 } } on : one-min-shards-rs0 Timestamp(1, 0)
要对一个特定的集合进行分片,首先需要在集合的数据库上启用分片,如下:
mongos> sh.enableSharding("accounts")
在对集合进行分片时,需要选择一个片键。片键是MongoDB用来拆分数据的一个或几个字段。随着集合的增大,片键会成为集合中最重要的索引。只有创建了索引的字段才能够作为片键。
mongos> db.users.createIndex({'username':1})
现在可以通过username片键来对集合进行分片了
mongos> sh.shardCollection("accounts.users", {"username":1})
mongos> sh.status()
--- Sharding Status ---
sharding version: {
"_id" : 1,
"minCompatibleVersion" : 5,
"currentVersion" : 6,
"clusterId" : ObjectId("62d2c414601aeb7a88c169f7")
}
shards:
{ "_id" : "one-min-shards-rs0", "host" : "one-min-shards-rs0/2bffe09ec303:20000,2bffe09ec303:20001,2bffe09ec303:20002", "state" : 1 }
{ "_id" : "one-min-shards-rs1", "host" : "one-min-shards-rs1/2bffe09ec303:20003,2bffe09ec303:20004,2bffe09ec303:20005", "state" : 1 }
active mongoses:
"4.2.6" : 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:
6 : Success
databases:
{ "_id" : "accounts", "primary" : "one-min-shards-rs1", "partitioned" : true, "version" : { "uuid" : UUID("ba658ebe-bbd1-44cd-b2c9-cb48d3810551"), "lastMod" : 1 } }
accounts.users
shard key: { "username" : 1 }
unique: false
balancing: true
chunks:
one-min-shards-rs0 6
one-min-shards-rs1 7
{ "username" : { "$minKey" : 1 } } -->> { "username" : "user17256" } on : one-min-shards-rs0 Timestamp(2, 0)
{ "username" : "user17256" } -->> { "username" : "user24515" } on : one-min-shards-rs0 Timestamp(3, 0)
{ "username" : "user24515" } -->> { "username" : "user31775" } on : one-min-shards-rs0 Timestamp(4, 0)
{ "username" : "user31775" } -->> { "username" : "user39034" } on : one-min-shards-rs0 Timestamp(5, 0)
{ "username" : "user39034" } -->> { "username" : "user46294" } on : one-min-shards-rs0 Timestamp(6, 0)
{ "username" : "user46294" } -->> { "username" : "user53553" } on : one-min-shards-rs0 Timestamp(7, 0)
{ "username" : "user53553" } -->> { "username" : "user60812" } on : one-min-shards-rs1 Timestamp(7, 1)
{ "username" : "user60812" } -->> { "username" : "user68072" } on : one-min-shards-rs1 Timestamp(1, 7)
{ "username" : "user68072" } -->> { "username" : "user75331" } on : one-min-shards-rs1 Timestamp(1, 8)
{ "username" : "user75331" } -->> { "username" : "user82591" } on : one-min-shards-rs1 Timestamp(1, 9)
{ "username" : "user82591" } -->> { "username" : "user89851" } on : one-min-shards-rs1 Timestamp(1, 10)
{ "username" : "user89851" } -->> { "username" : "user9711" } on : one-min-shards-rs1 Timestamp(1, 11)
{ "username" : "user9711" } -->> { "username" : { "$maxKey" : 1 } } on : one-min-shards-rs1 Timestamp(1, 12)
{ "_id" : "config", "primary" : "config", "partitioned" : true }
config.system.sessions
shard key: { "_id" : 1 }
unique: false
balancing: true
chunks:
one-min-shards-rs0 1
{ "_id" : { "$minKey" : 1 } } -->> { "_id" : { "$maxKey" : 1 } } on : one-min-shards-rs0 Timestamp(1, 0)
可以看到,这个集合被分为了13个块,其中6个块在分片one-min-shards-rs0上,7个块在分片one-min-shards-rs1上。
现在数据已经分布在分片上了,现在我们查询特定用户名:
mongos> db.users.find({username: "user12345"})
{ "_id" : ObjectId("62d2ca068dbf17063ba4d62f"), "username" : "user12345", "created_at" : ISODate("2022-07-16T14:24:06.891Z") }
通过explain可以观察到数据分布在分片one-min-shards-rs0上
mongos> db.users.find({username: "user12345"}).explain()
{
"queryPlanner" : {
"mongosPlannerVersion" : 1,
"winningPlan" : {
"stage" : "SINGLE_SHARD",
"shards" : [
{
"shardName" : "one-min-shards-rs0",
"connectionString" : "one-min-shards-rs0/2bffe09ec303:20000,2bffe09ec303:20001,2bffe09ec303:20002",
"serverInfo" : {
"host" : "2bffe09ec303",
"port" : 20001,
"version" : "4.2.6",
"gitVersion" : "20364840b8f1af16917e4c23c1b5f5efd8b352f8"
},
"plannerVersion" : 1,
"namespace" : "accounts.users",
"indexFilterSet" : false,
"parsedQuery" : {
"username" : {
"$eq" : "user12345"
}
},
"queryHash" : "379E82C5",
"planCacheKey" : "965E0A67",
"winningPlan" : {
"stage" : "FETCH",
"inputStage" : {
"stage" : "SHARDING_FILTER",
"inputStage" : {
"stage" : "IXSCAN",
"keyPattern" : {
"username" : 1
},
"indexName" : "username_1",
"isMultiKey" : false,
"multiKeyPaths" : {
"username" : [ ]
},
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 2,
"direction" : "forward",
"indexBounds" : {
"username" : [
"[\"user12345\", \"user12345\"]"
]
}
}
}
},
"rejectedPlans" : [ ]
}
]
}
}
}
当查询全部数据时,可以发现查询必须方位两个分片才能找到所有数据。通常来说,如果查询中没有使用片键,mongos就不得不将查询发送到每个分片上。
mongos> db.users.find({}).explain()
{
"queryPlanner" : {
"mongosPlannerVersion" : 1,
"winningPlan" : {
"stage" : "SHARD_MERGE",
"shards" : [
{
"shardName" : "one-min-shards-rs0",
"connectionString" : "one-min-shards-rs0/2bffe09ec303:20000,2bffe09ec303:20001,2bffe09ec303:20002",
"serverInfo" : {
"host" : "2bffe09ec303",
"port" : 20001,
"version" : "4.2.6",
"gitVersion" : "20364840b8f1af16917e4c23c1b5f5efd8b352f8"
},
"plannerVersion" : 1,
"namespace" : "accounts.users",
"indexFilterSet" : false,
"parsedQuery" : {
},
"queryHash" : "8B3D4AB8",
"planCacheKey" : "8B3D4AB8",
"winningPlan" : {
"stage" : "SHARDING_FILTER",
"inputStage" : {
"stage" : "COLLSCAN",
"direction" : "forward"
}
},
"rejectedPlans" : [ ]
},
{
"shardName" : "one-min-shards-rs1",
"connectionString" : "one-min-shards-rs1/2bffe09ec303:20003,2bffe09ec303:20004,2bffe09ec303:20005",
"serverInfo" : {
"host" : "2bffe09ec303",
"port" : 20005,
"version" : "4.2.6",
"gitVersion" : "20364840b8f1af16917e4c23c1b5f5efd8b352f8"
},
"plannerVersion" : 1,
"namespace" : "accounts.users",
"indexFilterSet" : false,
"parsedQuery" : {
},
"queryHash" : "8B3D4AB8",
"planCacheKey" : "8B3D4AB8",
"winningPlan" : {
"stage" : "SHARDING_FILTER",
"inputStage" : {
"stage" : "COLLSCAN",
"direction" : "forward"
}
},
"rejectedPlans" : [ ]
}
]
}
},
"serverInfo" : {
"host" : "2bffe09ec303",
"port" : 20009,
"version" : "4.2.6",
"gitVersion" : "20364840b8f1af16917e4c23c1b5f5efd8b352f8"
},
"ok" : 1,
"operationTime" : Timestamp(1658027749, 1),
"$clusterTime" : {
"clusterTime" : Timestamp(1658030075, 1),
"signature" : {
"hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
"keyId" : NumberLong(0)
}
}
}
定向查询:包含片键并可以发送到单个分片或分片子集的查询
分散-收集查询:必须发送到所有分片的查询
现在可以在原来的shell,按几次Enter键返回命令行,运行sh.stop()
干净的关闭所有服务器
2. 配置分片
2.1 启动服务器
2.1.1 启动配置服务器
配置服务器是集群的大脑,保存着关于每个服务器包含哪些数据的所有元数据,因此,必须首先创建配置服务器。
由于资源限制,在同一机器上启动三个进程
# mkdir /home/mongo/cs1
# mkdir /home/mongo/cs2
# mkdir /home/mongo/cs3
# mongod --configsvr --replSet configRS --bind_ip localhost --port 27038 --dbpath /home/mongo/cs1
# mongod --configsvr --replSet configRS --bind_ip localhost --port 27039 --dbpath /home/mongo/cs2
# mongod --configsvr --replSet configRS --bind_ip localhost --port 27040 --dbpath /home/mongo/cs3
# mongo --host localhost --port 27038
> rs.initiate(
{
_id: "configRS",
configsvr: true,
members: [
{_id: 0, host: "localhost:27038"},
{_id: 1, host: "localhost:27039"},
{_id: 2, host: "localhost:27040"}
]
})
可以看到,配置服务器副本集至此启动成功
configRS:PRIMARY> rs.isMaster()
{
"hosts" : [
"localhost:27038",
"localhost:27039",
"localhost:27040"
],
"setName" : "configRS",
"setVersion" : 1,
"ismaster" : true,
"secondary" : false
......
}
2.1.2 启动mongos路由进程
在3个配置服务器都运行后,启动一个mongos进程以供应用程序进行连接。为了确保高可用性,至少应该创建两个mongos进程。
# mongos --configdb configRS/localhost:27038,localhost:27039,localhost:27040 --bind_ip_all --port 27029 --fork --logpath /home/mongo/log.log
2.1.3 将副本集转换为分片
假设你已经创建了副本集,或参考之前文章8.MongoDB系列之创建副本集(一)进行创建副本集
连接副本集成员,并查看主节点
# mongo localhost:27018
study:PRIMARY> rs.isMaster()
{
"hosts" : [
"localhost:27018",
"localhost:27019",
"localhost:27020"
],
"setName" : "study",
"setVersion" : 2,
"ismaster" : true
.....
}
依次关闭从节点,并如下方式重新启动
mongod --replSet study --shardsvr --port 27019 --bind_ip localhost --dbpath /home/data/rs2
mongod --replSet study --shardsvr --port 27020 --bind_ip localhost --dbpath /home/data/rs3
现在将mongo shell连接到主节点,然后让其退位:
# mongo localhost:27018
study:PRIMARY> rs.stepDown()
# mongod --replSet study --shardsvr --port 27018 --bind_ip localhost --dbpath /home/data/rs1
现在可以将副本集作为分片添加到集群中了。通过mongos连接到admin数据库
# mongo localhost:27029/admin
> sh.addShard("study/localhost:27018,localhost:27019,localhost:27020")
mongos> sh.status()
--- Sharding Status ---
sharding version: {
"_id" : 1,
"minCompatibleVersion" : 5,
"currentVersion" : 6,
"clusterId" : ObjectId("62db816632b8e5b7cc0a5018")
}
shards:
{ "_id" : "study", "host" : "study/localhost:27018,localhost:27019,localhost:27020", "state" : 1 }
active mongoses:
"4.2.6" : 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 }
{ "_id" : "study", "primary" : "study", "partitioned" : false, "version" : { "uuid" : UUID("edc96711-21fd-4199-b47a-45a3b89c2f1c"), "lastMod" : 1 } }
至此分片配置完成!
2.2 MongoDB如何追踪集群数据
每个mongos都必须能够根据一个片键来查找一个文档。MongoDB会将文档已数据块的形式进行分组,这些数据块是片键指定范围内的文档。块总是存在于分片上,因此MongoDB可以用较小的表来维护数据块跟分片的映射。一个块总是属于且仅属于一个块,因此,不能使用数组字段来作为片键。
2.2.1 块范围
块在shell中显示范围为 m i n K e y 和 minKey和 minKey和maxKey, 块存储在config.chunks集合中
2.2.2 拆分块
各个分片的主节点mongod进程会跟踪他们当前的块,一旦达到某个阈值,就会检查该块是否需要拆分。
mongodb尝试分裂某个块却无法成功的过程被称为拆分风暴。防止拆分风暴的唯一方法是确保配置服务器尽可能正常运行。
2.3. 均衡器
均衡器负责是数据的迁移。它会定期检查分片之间是否存在不平衡,如果存在,就会对块进行迁移。
2.4. 变更流
变更流允许应用程序耿总数据库中数据的实时变更。通过跟踪oplog实现。跨分片集群的变更使用全局逻辑时钟来保持有序性。
3. 选择片键
3.1 片键类型
3.1.1 升序片键
升序片键通常类似于date或ObjectId–随着时间稳步增长的字段。
这种模式通常会使MongoDB更难保持块的平衡,因为所有的块都是由一个分片创建的。
3.1.2 随机分发的片键
随机分发的键可以是用户名,电子邮件地址,UUID,MD5哈希值等
由于写操作是随机分发的,因此分片应该以大致相同的速度增长,从而减少需要进行的迁移操作数据。
3.1.3 基于位置的片键
基于位置的片键可以是用户的IP、经纬度或者地址。
> sh.addShardToZone("shard0000", "USPS")
> sh.addShardToZone("shard0000", "Apple")
> sh.addShardToZone("shard0002", "Apple")
> sh.updateZoneKeyRange("test.ips", {"ip": "056:000:000:000"}, {"ip": "057:000:000:000"}, "Apple")
3.2. 片键策略
3.2.1 哈希片键
为了尽快的加载数据,哈希片键是最好的选择。如果在大量查询中使用升序键,但又希望写操作随机分发,那么哈希片键是不错的选择。但是我们无法使用哈希片碱执行指定目标的范围查询。
> db.users.createIndex({"username": "hashed"})
> db.shardCollection("app.users", {"username": ""hashed})
3.2.2 GridFS的哈希片键
在files_id字段上创建一个哈希索引,呢么每个文件都会在集群中随机分发,并且同一个文件将始终被包含在单个块上。
> db.fs.chunks.ensureIndex({"files_id": "hashed"})
> sh.shardCollection("test.fs.chunks", {"files_id": "hashed"})
3.2.3 多热点
以下描述的技术会创建多个热点(最好是每个分片上都有几个热点),以便写操作在集群中均匀分发,但在同一个分片上写操作是递增的。
为了实现,需要复合片键:第一个键值是粗略的随机值,技术较小,第二个键值是一个升序键。
可以将此配置想象成每个块都是一个升序文档的栈,每个分片上有多个栈,每个栈都是递增的,直到块被拆分。
4. 分片管理
4.1 查看当前状态
4.1.1 查看配置信息
mongos> use config
// 查看分片
mongos> db.shards.find()
{ "_id" : "study", "host" : "study/localhost:27018,localhost:27019,localhost:27020", "state" : 1 }
// 查看分片集合信息
mongos> db.collections.find().pretty()
{
"_id" : "config.system.sessions",
"lastmodEpoch" : ObjectId("62db964a7decd7b3c3f36bc1"),
"lastmod" : ISODate("1970-02-19T17:02:47.296Z"),
"dropped" : false,
"key" : {
"_id" : 1
},
"unique" : false,
"uuid" : UUID("5351f3f9-63e3-44b8-af9a-a0261e681bef")
}
// 查询集合中块信息
mongos> db.chunks.find().pretty()
{
"_id" : "config.system.sessions-_id_MinKey",
"ns" : "config.system.sessions",
"min" : {
"_id" : { "$minKey" : 1 }
},
"max" : {
"_id" : { "$maxKey" : 1 }
},
"shard" : "study",
"lastmod" : Timestamp(1, 0),
"lastmodEpoch" : ObjectId("62db964a7decd7b3c3f36bc1"),
"history" : [
{
"validAfter" : Timestamp(1658558026, 4),
"shard" : "study"
}
]
}
// 跟踪集群拆分和迁移情况
mongos> db.changelog.find({'what': 'split'}).pretty()
4.1.2 跟踪网络连接
mongos> db.adminCommand({'connPoolStats':1})
{
"numClientConnections" : 0,
"numAScopedConnections" : 0,
"totalInUse" : 0,
"totalAvailable" : 9,
"totalCreated" : 14,
"totalRefreshing" : 0,
"pools" : {
"NetworkInterfaceTL-ShardRegistry" : {
"poolInUse" : 0,
"poolAvailable" : 3,
"poolCreated" : 8,
"poolRefreshing" : 0,
"localhost:27038" : {
"inUse" : 0,
"available" : 1,
"created" : 2,
"refreshing" : 0
},
"localhost:27039" : {
"inUse" : 0,
"available" : 1,
"created" : 5,
"refreshing" : 0
},
"localhost:27040" : {
"inUse" : 0,
"available" : 1,
"created" : 1,
"refreshing" : 0
}
},
"NetworkInterfaceTL-TaskExecutorPool-0" : {
"poolInUse" : 0,
"poolAvailable" : 6,
"poolCreated" : 6,
"poolRefreshing" : 0,
"localhost:27018" : {
"inUse" : 0,
"available" : 1,
"created" : 1,
"refreshing" : 0
},
"localhost:27019" : {
"inUse" : 0,
"available" : 1,
"created" : 1,
"refreshing" : 0
},
"localhost:27020" : {
"inUse" : 0,
"available" : 1,
"created" : 1,
"refreshing" : 0
},
"localhost:27038" : {
"inUse" : 0,
"available" : 1,
"created" : 1,
"refreshing" : 0
},
"localhost:27039" : {
"inUse" : 0,
"available" : 1,
"created" : 1,
"refreshing" : 0
},
"localhost:27040" : {
"inUse" : 0,
"available" : 1,
"created" : 1,
"refreshing" : 0
}
}
},
"hosts" : {
"localhost:27018" : {
"inUse" : 0,
"available" : 1,
"created" : 1,
"refreshing" : 0
},
"localhost:27019" : {
"inUse" : 0,
"available" : 1,
"created" : 1,
"refreshing" : 0
},
"localhost:27020" : {
"inUse" : 0,
"available" : 1,
"created" : 1,
"refreshing" : 0
},
"localhost:27038" : {
"inUse" : 0,
"available" : 2,
"created" : 3,
"refreshing" : 0
},
"localhost:27039" : {
"inUse" : 0,
"available" : 2,
"created" : 6,
"refreshing" : 0
},
"localhost:27040" : {
"inUse" : 0,
"available" : 2,
"created" : 2,
"refreshing" : 0
}
},
"replicaSets" : {
"configRS" : {
"hosts" : [
{
"addr" : "localhost:27038",
"ok" : true,
"ismaster" : false,
"hidden" : false,
"secondary" : true,
"pingTimeMillis" : 0
},
{
"addr" : "localhost:27039",
"ok" : true,
"ismaster" : true,
"hidden" : false,
"secondary" : false,
"pingTimeMillis" : 0
},
{
"addr" : "localhost:27040",
"ok" : true,
"ismaster" : false,
"hidden" : false,
"secondary" : true,
"pingTimeMillis" : 0
}
]
},
"study" : {
"hosts" : [
{
"addr" : "localhost:27018",
"ok" : true,
"ismaster" : false,
"hidden" : false,
"secondary" : true,
"pingTimeMillis" : 0,
"tags" : {
"dc" : "sh"
}
},
{
"addr" : "localhost:27019",
"ok" : true,
"ismaster" : true,
"hidden" : false,
"secondary" : false,
"pingTimeMillis" : 0,
"tags" : {
"dc" : "sh"
}
},
{
"addr" : "localhost:27020",
"ok" : true,
"ismaster" : false,
"hidden" : false,
"secondary" : true,
"pingTimeMillis" : 0,
"tags" : {
"dc" : "bj"
}
}
]
}
},
"ok" : 1,
"operationTime" : Timestamp(1658569778, 1),
"$clusterTime" : {
"clusterTime" : Timestamp(1658569778, 1),
"signature" : {
"hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
"keyId" : NumberLong(0)
}
}
}
可以在命令行配置 --maxConns选项来限制其可以创建的连接数
4.2 服务器管理
4.2.1 添加服务器
可以在任何时候添加新的mongos进程。
可以使用addShard命令添加新的分片
4.2.2 修改分片中的服务器
要更改一个分片的成员,需要直接连接到该分片的主节点(而不是通过mongos),并重新配置副本集。集群配置会监测到变更并自动更新config.shards.不要手动修改config.shards
4.2.3 删除分片
> db.adminCommand({'removeShard': 'study'})
当有数据库将被移除的分片作为其主分片,则会提示you need to drop or movePrimary these databases
此时先将此数据库移动到其他分片, 在执行删除即可
> db.adminCommand({'movePrimary': 'video', to: 'shard01'})
> db.adminCommand({'removeShard': 'study'})
4.3. 数据均衡
4.3.1 均衡器
关闭均衡器是大部分管理操作的先决条件。
// 关闭均衡器
mongos> sh.setBalancerState(false)
// 检查是否仍有均衡过程正在进行
mongos> db.locks.find({'_id': 'balancer'})['state']
mongos> sh.setBalancerState(true)
// 设置均衡时间窗口
mongos> db.settings.update({_id: "balancer"}, {$set: {"activeWindow": {start: "13:00", stop: "16:00"}}}, {upsert:true})
WriteResult({ "nMatched" : 0, "nUpserted" : 1, "nModified" : 0, "_id" : "balancer" })
4.3.2 修改块的大小
// 修改块大小为32M
mongos> db.settings.save({'_id': 'chunksize', 'value': 32})
WriteResult({ "nMatched" : 0, "nUpserted" : 1, "nModified" : 0, "_id" : "chunksize" })
4.3.3 移动块
> sh.moveChunk('video.movies', {imdId: 50000}, "shard0002")
4.3.4 超大块
当一个块大于config.settings中所设置的最大块大小时,均衡器就不允许移动这个块了。这些不可拆分、不可移动的块被称为超大块
// 检测超大块,其会被标记有jumbo属性
> sh.status()
...
{"x": 5} -->> {"x":6} on : shard0001 jumbo
...
// 拆分超大块,需要先找出片键的拆分点
> sh.splitAt('video.movies', {'imdId': NumberLong('7000000000000000000000')})
对于分发超大块,可以关闭均衡器,临时设置块大小大于该超大块,然后手工移动该块,后恢复块大小及启动均衡器。为了避免超大块,可以修改片键使其具有更小的粒度,如年月日片键,修改为年月日小时分钟秒。
4.4 刷新配置
当mongos不能从配置服务器正确更新配置,则可以
> db.adminCommand({'flushRouterConfig': 1})
如果还没有解决,则需要重新启动所有的mongos或mongod进程以清除所有缓存数据
欢迎关注公众号算法小生或沈健的技术博客shenjian.online