Mongodb Shard集群手动将大块的jumbo chunk拆分

Mongodb Shard Manual Split Jumbo Chunk

Mongodb Shard集群手动将大块的jumbo chunk拆分
Page content

最近压测MongoDB Shard集群时发现集群中四个shard 的shard1的primary机器负载异常的高,其它三个shard 的primary负载在40%左右,shard1的primary负载就已经 飙升超过80%,经过查看日志发现shard中产生了jumbo chunk,这些jumbo chunk都位于shard1上,而且产生 jumbo chunk的两个集合的数据又占整个库的75%,这 意味着整个库超过75%的数据都在shard1上,而理想情况 下每个shard的数据量应该占总数据量的25%左右。

所以我想是不是分片键用的不对,导致了数据分布不均匀, 但是使用的分片键是基于一个类似id的在整个集合中不会重复的 键,并使用该键的hashed key来作为分片键的,按道理来说 是不会导致数据分布不均匀的。根据官方文档,如果要更换 分片键,需要备份数据,还原数据,重新创建索引,重新 进行分片操作,很麻烦。

后面决定找出jumbo chunk,并使用jumbo chunk的分片键的 范围的中间值来进行手动拆分,拆分后每个chunk的大小减半 mongodb shard集群的balancer就会自动迁移这些chunk, 最终数据就会平衡。

找出jumbo chunk的shard key范围

mongos> sh.status(true)
 ProdDB.CollectionGift
                        shard key: { "_p" : "hashed" }
                        unique: false
                        balancing: true
                        chunks:
                                shard1  33  # 可以看到大部分数据都位于shard1
                                shard2  1
                                shard3  1
                                shard4  1
                        { "_p" : NumberLong("x") } -->> { "_p" : NumberLong("y") } on : shard1 Timestamp(1, 2) jumbo
                        { "_p" : { "$minKey" : 1 } } -->> { "_p" : NumberLong("-8727794919006459418") } on : shard1 Timestamp(4, 1) jumbo
                        { "_p" : NumberLong("8146976269431009615") } -->> { "_p" : NumberLong("8675967601979213236") } on : shard1 Timestamp(1, 33) jumbo
                        { "_p" : NumberLong("8675967601979213236") } -->> { "_p" : { "$maxKey" : 1 } } on : shard2 Timestamp(2, 0)
                        ...

 ProdDB.CollectionHelp
                        shard key: { "s" : "hashed" }
                        unique: false
                        balancing: true
                        chunks:
                                shard1  22  # 可以看到大部分数据都位于shard1
                                shard2  1
                        { "s" : { "$minKey" : 1 } } -->> { "s" : NumberLong("-8346286305843832955") } on : shard1 Timestamp(2, 1) jumbo
                        { "s" : NumberLong("7924985712140216103") } -->> { "s" : NumberLong("8729898180728191084") } on : shard1 Timestamp(1, 21) jumbo
                        { "s" : NumberLong("8729898180728191084") } -->> { "s" : { "$maxKey" : 1 } } on : shard2 Timestamp(2, 0)
                        ...

获取每个chunk的shard key范围的中间值

~$ cat CollectionHelp-shardKey-range.log
{ "_p" : NumberLong("-8703053857662077137") } -->> { "_p" : NumberLong("-8180818334030330478") } on : shard2 Timestamp(4, 1) jumbo
{ "_p" : NumberLong("-8180818334030330478") } -->> { "_p" : NumberLong("-7620192240011356646") } on : shard2 Timestamp(1, 2) jumbo
{ "_p" : NumberLong("-7620192240011356646") } -->> { "_p" : NumberLong("-7078210833400611990") } on : shard2 Timestamp(1, 3) jumbo
{ "_p" : NumberLong("-7078210833400611990") } -->> { "_p" : NumberLong("-6412177414777805306") } on : shard2 Timestamp(1, 4) jumbo
{ "_p" : NumberLong("-6412177414777805306") } -->> { "_p" : NumberLong("-5783382915263969727") } on : shard2 Timestamp(1, 5) jumbo
{ "_p" : NumberLong("-5783382915263969727") } -->> { "_p" : NumberLong("-5194486614445495982") } on : shard2 Timestamp(1, 6) jumbo
...
~$ cat CollectionGift-shardKey-range.log
{ "s" : NumberLong("-8299268740683692778") } -->> { "s" : NumberLong("-7409175569407993593") } on : shard2 Timestamp(1, 1) jumbo
{ "s" : NumberLong("-7409175569407993593") } -->> { "s" : NumberLong("-6592529563360768336") } on : shard2 Timestamp(1, 2) jumbo
{ "s" : NumberLong("-6592529563360768336") } -->> { "s" : NumberLong("-5742384428218933523") } on : shard2 Timestamp(1, 3) jumbo
{ "s" : NumberLong("-5742384428218933523") } -->> { "s" : NumberLong("-4845463018335517015") } on : shard2 Timestamp(1, 4) jumbo
...

# 获取中间值
~$ cat CollectionHelp-shardKey-range.log | awk '{ print ""$2"-("$2"-("$1"))/2" }' | bc 
~$ cat CollectionGift-shardKey-range.log | awk '{ print ""$2"-("$2"-("$1"))/2" }' | bc 

split

sh.splitAt( "ProdDB.CollectionGift", { "_p": mid })
sh.splitAt( "ProdDB.CollectionHelp", { "s": mid })

# 使用awk来拼接mongodb shard命令
~$ cat CollectionHelp-shardKey-range.log | awk '{ print ""$2"-("$2"-("$1"))/2" }' | bc | awk '{print "sh.splitAt(\"ProdDB.CollectionHelp\", { \"_p\": "$0" })" }'
sh.splitAt("ProdDB.CollectionGift", { "_p": -8441936095846203807 })
sh.splitAt("ProdDB.CollectionGift", { "_p": -7900505287020843562 })
sh.splitAt("ProdDB.CollectionGift", { "_p": -7349201536705984318 })
sh.splitAt("ProdDB.CollectionGift", { "_p": -6745194124089208648 })
...
~$ cat CollectionGift-shardKey-range.log | awk '{ print ""$2"-("$2"-("$1"))/2" }' | bc | awk '{print "sh.splitAt(\"ProdDB.CollectionGift\", { \"s\": "$0" })" }'
sh.splitAt("ProdDB.CollectionHelp", { "s": -7854222155045843185 })
sh.splitAt("ProdDB.CollectionHelp", { "s": -7000852566384380964 })
sh.splitAt("ProdDB.CollectionHelp", { "s": -6167456995789850929 })
sh.splitAt("ProdDB.CollectionHelp", { "s": -5293923723277225269 })
...

# 最后将命令由管道传给mongo,使用mongos端口

~$ cat CollectionHelp-shardKey-range.log | awk '{ print ""$2"-("$2"-("$1"))/2" }' | bc | awk '{print "sh.splitAt(\"ProdDB.CollectionHelp\", { \"_p\": "$0" })" }' | mongo --port mongosPort ProdDB
~$ cat CollectionGift-shardKey-range.log | awk '{ print ""$2"-("$2"-("$1"))/2" }' | bc | awk '{print "sh.splitAt(\"ProdDB.CollectionGift\", { \"s\": "$0" })" }' | mongo --port mongosPort ProdDB