将云数据库 MongoDB 版分片集群实例升级到6.0版本(小版本7.0.1及以上)或更高版本后,通过sh.status()查看分片信息时,会发现各Shard上的Chunk数量明显不均衡,甚至每个Shard只有1个Chunk。本文说明这一现象的原因,并介绍如何正确判断数据均衡情况。
问题现象
使用云数据库 MongoDB 版分片集群实例时,部分用户在将实例升级到6.0版本(小版本7.0.1及以上)后,通过sh.status()查看分片信息,发现各Shard上的Chunk数量不均衡,与升级前每个Shard上Chunk数量大致相等的情况明显不同。常见的现象包括:
-
6.0版本(小版本7.0.1起):各Shard上的Chunk数量差异较大,不再像旧版本那样大致相等。
-
7.0及以上版本:各Shard上可能只有1个Chunk。
通常会产生以下疑问:
-
数据是不是没有均衡分布?
-
Balancer是不是没有正常工作?
-
是不是升级出了什么问题?
这是升级后的正常行为,数据仍然是均衡分布的,无需担心。新版本不再以Chunk数量作为均衡标准,而是以实际数据大小为依据。
原因说明
旧版本行为(6.0版本小版本7.0.1之前)
在云数据库 MongoDB 版6.0版本(小版本7.0.1)之前,分片集群的数据均衡机制如下:
-
每个Chunk默认最大64 MB。
-
当数据写入使Chunk超过64 MB时,会自动分裂(AutoSplit)为多个小Chunk。
-
Balancer定期检测各Shard上的Chunk数量,发现数量差异超过阈值后执行迁移(
moveChunk)。 -
最终各Shard上会有大量Chunk,且数量大致相等。
这种机制下,您通过sh.status()能看到每个Shard上有多个Chunk,可以直观地感受到数据是均衡分布的。
6.0版本变化(小版本7.0.1起,对应社区版6.0.3)
云数据库 MongoDB 版从6.0版本(小版本7.0.1)开始,对应社区版MongoDB 6.0.3的Balancer改进,进行了以下重大变更:
-
取消自动分裂(AutoSplit):Chunk写满后不再自动分裂,仅在迁移前进行必要的分割。
-
均衡判断标准改变:从基于Chunk数量改为基于实际数据大小。
-
迁移阈值调整:当不同分片上同一集合的数据大小差异超过3倍ChunkSize(云数据库 MongoDB 版6.0版本起默认ChunkSize为128 MB,即差异超过384 MB)时触发迁移。
-
术语变更:Chunk概念弱化,改称为Range;
moveChunk被moveRange取代。 -
集合分片前不再需要执行
sh.enableSharding()命令。
影响:由于取消了AutoSplit且不再以Chunk数量为均衡依据,各Shard上的Chunk数量可能不相等,这是正常现象。更多信息,请参见sh.enableAutoSplit()。
7.0版本新增:AutoMerger自动合并
从MongoDB 7.0开始,引入了AutoMerger功能:
-
Balancer会自动将同一Shard上可合并的连续Chunk合并在一起。
-
AutoMerger作为均衡操作的一部分在后台运行,默认启用。
-
每隔
autoMergerIntervalSecs秒进行一次自动合并。 -
如果设置了均衡窗口,AutoMerger仅在该窗口期间运行。
影响:在7.0及以上版本中,由于AutoMerger的存在,各Shard上的Chunk最终可能合并为1个Chunk。这同样是正常行为。更多信息,请参见AutoMerger。
为什么做这些改变?
旧版本基于Chunk数量做均衡判断存在以下问题:
|
问题 |
说明 |
|
Jumbo Chunk |
分片键区分度不足时,同一Chunk中所有文档的分片键相同,Chunk无法分割,导致各Shard的Chunk数量相同但磁盘空间使用不同。 |
|
写入倾斜 |
范围分片键含递增属性时,新数据集中写入一个Shard。 |
|
资源消耗 |
频繁的AutoSplit和moveChunk消耗大量CPU资源,影响业务性能。 |
新版本基于实际数据大小做均衡判断,更加准确地反映数据分布情况,同时减少了不必要的Chunk分裂和迁移操作。
版本对应关系
|
云数据库MongoDB版本 |
对应社区版 |
Balancer行为 |
|
4.4版本 |
MongoDB 4.4 |
旧行为:AutoSplit + Chunk数量均衡 |
|
5.0版本 |
MongoDB 5.0 |
旧行为:AutoSplit + Chunk数量均衡 |
|
6.0版本(小版本7.0.0) |
MongoDB 6.0.0 |
旧行为:AutoSplit + Chunk数量均衡 |
|
6.0版本(小版本7.0.1起) |
MongoDB 6.0.3 |
取消AutoSplit + 数据大小均衡(Chunk数量可能不均衡) |
|
7.0及以上版本 |
MongoDB 7.0+ |
取消AutoSplit + 数据大小均衡 + AutoMerger自动合并Chunk |
版本前后对比
|
对比项 |
6.0版本(小版本7.0.1之前) |
6.0版本(小版本7.0.1起) |
7.0及以上版本 |
|
自动分裂(AutoSplit) |
开启,Chunk写满自动分裂 |
关闭 |
关闭 |
|
自动合并(AutoMerger) |
无 |
无 |
开启,自动合并Chunk |
|
均衡判断依据 |
Chunk数量 |
实际数据大小 |
实际数据大小 |
|
各Shard的Chunk数量 |
多个,数量大致相等 |
不均衡,数量可能不相等 |
可能合并为1个 |
|
迁移阈值 |
Chunk数量差异≥1(4.2+) |
数据大小差异超过3倍ChunkSize |
数据大小差异超过3倍ChunkSize |
|
|
分片前必须执行 |
6.0+可跳过 |
可跳过 |
|
查看均衡方式 |
|
|
|
实测数据
以下为使用YCSB工具分别向4.4、6.0、8.0版本的云数据库 MongoDB 版分片集群实例各插入5000万条数据后的实测结果。
4.4版本(旧行为)
执行sh.status(),重点关注输出中的chunkMetadata字段——每个Shard上有435个Chunk,数量相等,这是旧版本AutoSplit自动分裂的结果:
mongos> sh.status()
shardingVersion
{ _id: 1, clusterId: ObjectId('69fbfda6dec76b373f5eda81') }
---
shards
[
{
_id: 'd-uf6d*****04f784',
host: 'mgset-100239890/10.**.**.1*9:3005,20.**.**.1*9:3002',
state: 1
},
{
_id: 'd-uf6f*****39f74',
host: 'mgset-100***91/20.**.**.**7:3002,20.**.**.**8:3002',
state: 1
}
]
---
active mongoses
[ { '4.4.30': 2 } ]
---
autosplit
{ 'Currently enabled': 'yes' }
---
balancer
{
'Currently enabled': 'yes',
'Failed balancer rounds in last 5 attempts': 0,
'Currently running': 'no',
'Migration Results for the last 24 hours': { '947': 'Success' }
}
---
databases
[
{
database: {
_id: 'ycsb',
primary: 'd-uf6f*****39f74',
partitioned: true
},
collections: {
'ycsb.test1': {
shardKey: { _id: 'hashed' },
unique: false,
balancing: true,
chunkMetadata: [
{ shard: 'd-uf6d*****04f784', nChunks: 435 },
{ shard: 'd-uf6f*****39f74', nChunks: 435 }
],
chunks: [
'too many chunks to print, use verbose if you want to force print'
],
tags: []
}
}
}
]
执行db.test1.getShardDistribution(),关注每个Shard的data(数据大小)和chunks(Chunk数量)字段:
mongos> use ycsb
switched to db ycsb
mongos> db.test1.getShardDistribution()
Shard d-uf6d*****04f784 at mgset-100239890/10.**.**.1*9:3005,20.**.**.1*9:3002
{
data: '27.2GiB',
docs: 25014674,
chunks: 435,
'estimated data per chunk': '64.04MiB',
'estimated docs per chunk': 57504
}
---
Shard d-uf6f*****39f74 at mgset-100239891/20.**.**.**7:3002,20.**.**.**8:3002
{
data: '36.23GiB',
docs: 33313293,
chunks: 435,
'estimated data per chunk': '85.29MiB',
'estimated docs per chunk': 76582
}
---
Totals
{
data: '63.44GiB',
docs: 58327967,
chunks: 870,
'Shard d-uf6d*****04f784': [
'42.88 % data',
'42.88 % docs in cluster',
'1KiB avg obj size on shard'
],
'Shard d-uf6f*****39f74': [
'57.11 % data',
'57.11 % docs in cluster',
'1KiB avg obj size on shard'
]
}
6.0版本(小版本7.0.1起)
执行sh.status(),关注chunkMetadata字段——各Shard上的Chunk数量明显不均衡(216 vs 1):
mongos> sh.status()
shardingVersion
{ _id: 1, clusterId: ObjectId('69fbfdc420578638757c1d06') }
---
shards
[
{
_id: 'd-uf610*****d8904',
host: 'mgset-100239905/20.**.**.**1:3002,20.**.**.**3:3002',
state: 1,
topologyTime: Timestamp({ t: 1778122310, i: 2 })
},
{
_id: 'd-uf6aa*****67314',
host: 'mgset-100239903/20.**.**.**5:20.**.**.**6:3002',
state: 1,
topologyTime: Timestamp({ t: 1778122310, i: 9 })
}
]
---
active mongoses
[ { '6.0.27': 2 } ]
---
autosplit
{ 'Currently enabled': 'yes' }
---
balancer
{
'Currently running': 'no',
'Currently enabled': 'yes',
'Failed balancer rounds in last 5 attempts': 0,
'Migration Results for the last 24 hours': { '216': 'Success' }
}
---
databases
[
{
database: {
_id: 'ycsb',
primary: 'd-uf6aa*****67314',
partitioned: false,
version: {
uuid: UUID('4ee45231-9585-4dba-8f34-f624860e1a79'),
timestamp: Timestamp({ t: 1778122930, i: 1 }),
lastMod: 1
}
},
collections: {
'ycsb.test2': {
shardKey: { _id: 'hashed' },
unique: false,
balancing: true,
allowMigrations: true,
chunkMetadata: [
{ shard: 'd-uf610*****d8904', nChunks: 216 },
{ shard: 'd-uf6aa*****67314', nChunks: 1 }
],
chunks: [
'too many chunks to print, use verbose if you want to force print'
],
tags: []
}
}
}
]
执行db.test2.getShardDistribution(),关注每个Shard的data占比——虽然Chunk数量差异巨大(216 vs 1),但数据仍然均衡分布(50.31% vs 49.68%):
mongos> use ycsb
switched to db ycsb
mongos> db.test2.getShardDistribution()
Shard d-uf6aa*****67314 at mgset-100239903/20.**.**.**5:20.**.**.**6:3002
{
data: '27.36GiB',
docs: 32285618,
chunks: 1,
'estimated data per chunk': '27.36GiB',
'estimated docs per chunk': 32285618
}
---
Shard d-uf610*****d8904 at mgset-100239905/20.**.**.**1:3002,20.**.**.**3:3002
{
data: '27.01GiB',
docs: 24841944,
chunks: 216,
'estimated data per chunk': '128.09MiB',
'estimated docs per chunk': 115009
}
---
Totals
{
data: '54.38GiB',
docs: 57127562,
chunks: 217,
'Shard d-uf6aa*****67314': [
'50.31 % data',
'56.51 % docs in cluster',
'1KiB avg obj size on shard'
],
'Shard d-uf610*****d8904': [
'49.68 % data',
'43.48 % docs in cluster',
'1KiB avg obj size on shard'
]
}
8.0版本(含AutoMerger)
迁移刚完成时,执行sh.status(),关注chunkMetadata字段——各Shard上仍有多个Chunk(55 vs 1):
mongos> sh.status()
shardingVersion
{ _id: 1, clusterId: ObjectId('69fbfdbdd7b0e3d9551dd833') }
---
shards
[
{
_id: 'd-uf65a*****ac94',
host: 'mgset-100239911/20.**.**.**7:3000,20.**.**.**6:3002',
state: 1,
topologyTime: Timestamp({ t: 1778122320, i: 8 }),
replSetConfigVersion: Long('-1')
},
{
_id: 'd-uf6a8*****c9b14',
host: 'mgset-100239912/20.**.**.**8:3003,20.**.**.**9:3003',
state: 1,
topologyTime: Timestamp({ t: 1778122319, i: 10 }),
replSetConfigVersion: Long('-1')
}
]
---
active mongoses
[ { '8.0.18': 2 } ]
---
autosplit
{ 'Currently enabled': 'yes' }
---
balancer
{
'Currently enabled': 'yes',
'Failed balancer rounds in last 5 attempts': 0,
'Currently running': 'no',
'Migration Results for the last 24 hours': { '216': 'Success' }
}
---
databases
[
{
database: {
_id: 'ycsb',
primary: 'd-uf6a8*****c9b14',
version: {
uuid: UUID('98799357-048e-45bc-a472-387165e0fe74'),
timestamp: Timestamp({ t: 1778122931, i: 2 }),
lastMod: 1
}
},
collections: {
'ycsb.test2': {
shardKey: { _id: 'hashed' },
unique: false,
balancing: true,
allowMigrations: true,
chunkMetadata: [
{ shard: 'd-uf65a*****ac94', nChunks: 55 },
{ shard: 'd-uf6a8*****c9b14', nChunks: 1 }
],
chunks: [
'too many chunks to print, use verbose if you want to force print'
],
tags: []
}
}
}
]
等待AutoMerger自动合并完成后,再次执行sh.status(),关注chunkMetadata字段——每个Shard上的Chunk已自动合并为1个:
mongos> sh.status()
shardingVersion
{ _id: 1, clusterId: ObjectId('69fbfdbdd7b0e3d9551dd833') }
---
shards
[
{
_id: 'd-uf65a*****ac94',
host: 'mgset-100239911/20.**.**.**7:3000,20.**.**.**6:3002',
state: 1,
topologyTime: Timestamp({ t: 1778122320, i: 8 }),
replSetConfigVersion: Long('-1')
},
{
_id: 'd-uf6a8*****c9b14',
host: 'mgset-100239912/20.**.**.**8:3003,20.**.**.**9:3003',
state: 1,
topologyTime: Timestamp({ t: 1778122319, i: 10 }),
replSetConfigVersion: Long('-1')
}
]
---
active mongoses
[ { '8.0.18': 2 } ]
---
autosplit
{ 'Currently enabled': 'yes' }
---
balancer
{
'Currently enabled': 'yes',
'Failed balancer rounds in last 5 attempts': 0,
'Currently running': 'no',
'Migration Results for the last 24 hours': { '216': 'Success' }
}
---
databases
[
{
database: {
_id: 'ycsb',
primary: 'd-uf6a8*****c9b14',
version: {
uuid: UUID('98799357-048e-45bc-a472-387165e0fe74'),
timestamp: Timestamp({ t: 1778122931, i: 2 }),
lastMod: 1
}
},
collections: {
'ycsb.test2': {
shardKey: { _id: 'hashed' },
unique: false,
balancing: true,
allowMigrations: true,
chunkMetadata: [
{ shard: 'd-uf65a*****ac94', nChunks: 1 },
{ shard: 'd-uf6a8*****c9b14', nChunks: 1 }
],
chunks: [
{ min: { _id: MinKey() }, max: { _id: Long('-58615400192952365') }, 'on shard': 'd-uf65a*****ac94', 'last modified': Timestamp({ t: 217, i: 2 }) },
{ min: { _id: Long('-58615400192952365') }, max: { _id: MaxKey() }, 'on shard': 'd-uf6a8*****c9b14', 'last modified': Timestamp({ t: 217, i: 1 }) }
],
tags: []
}
}
}
]
执行db.test2.getShardDistribution(),关注每个Shard的data占比——尽管每个Shard只有1个Chunk,数据仍然均衡分布(49.67% vs 50.32%):
mongos> use ycsb
switched to db ycsb
mongos> db.test2.getShardDistribution()
Shard d-uf65a*****ac94 at mgset-100239911/20.**.**.**7:3000,20.**.**.**6:3002
{
data: '27.01GiB',
docs: 24841944,
chunks: 1,
'estimated data per chunk': '27.01GiB',
'estimated docs per chunk': 24841944
}
---
Shard d-uf6a8*****c9b14 at mgset-100239912/20.**.**.**8:3003,20.**.**.**9:3003
{
data: '27.37GiB',
docs: 35898246,
chunks: 1,
'estimated data per chunk': '27.37GiB',
'estimated docs per chunk': 35898246
}
---
Totals
{
data: '54.39GiB',
docs: 60740190,
chunks: 2,
'Shard d-uf65a*****ac94': [
'49.67 % data',
'40.89 % docs in cluster',
'1KiB avg obj size on shard'
],
'Shard d-uf6a8*****c9b14': [
'50.32 % data',
'59.1 % docs in cluster',
'1KiB avg obj size on shard'
]
}
结论:三个版本的实测数据表明,无论Chunk数量如何变化(870→217→2),数据始终均衡分布在各Shard上。Chunk数量的差异仅是内部管理机制的变化,不影响实际数据分布和业务。
如何正确判断数据是否均衡
升级到6.0版本(小版本7.0.1)及以上版本后,请使用以下方式判断数据均衡情况:
推荐方式:getShardDistribution()
连接到mongos后执行以下命令:
use <your_database>
db.<your_collection>.getShardDistribution()
该命令会返回每个Shard上该集合的数据大小、文档数量和占比,能真实反映数据分布情况。
辅助方式:db.stats()
use <your_database>
db.stats()
可以查看数据库在各Shard节点的整体数据存储情况。
不再推荐:sh.status()看Chunk数量
sh.status()仍然可以执行,但Chunk数量不再反映数据均衡情况。看到各Shard的Chunk数量不相等或只有1个Chunk都是正常的,不代表数据分布有问题。
常见问题
Q:升级后Chunk数量不均衡或变为1,数据会丢失吗?
A:不会。Chunk只是MongoDB内部管理数据的逻辑单元,Chunk数量的变化不影响实际数据。数据仍然完整保存在各Shard上。
Q:需要手动执行split命令来分割Chunk吗?
A:不需要。新版本的Balancer会在需要迁移数据时自动进行必要的分割,无需手动干预。
Q:升级后Balancer还在工作吗?
A:是的。Balancer仍在正常工作,只是判断标准从Chunk数量改为了数据大小。当各分片间的数据大小差异超过阈值时,Balancer会自动执行数据迁移。
Q:6.0和7.0版本升级后的Chunk表现有什么不同?
A:6.0版本(小版本7.0.1起)取消了AutoSplit并改用数据大小均衡,各Shard的Chunk数量可能不均衡。7.0版本在此基础上新增了AutoMerger,会自动合并Chunk,最终每个Shard可能只有1个Chunk。两种情况下数据都是均衡分布的。
Q:迁移过程中看到多个Chunk是正常的吗?
A:是正常的。在数据迁移期间,Balancer会对数据进行必要的分割,因此会临时出现多个Chunk。对于7.0及以上版本,迁移完成后AutoMerger会自动合并这些Chunk;对于6.0版本,这些Chunk可能会保持不变,但不影响数据均衡。
Q:这个变化影响我的业务吗?
A:不影响。这个改变对业务是透明的,实际上新版本减少了不必要的Chunk分裂和迁移操作,性能反而更好。