MongoDB的核心优势之一可扩展性,给运维带来的极大便利与节约成本,业务初期可以部署小的集群或者副本集,后续可以水平扩容节点或者把副本集转换成集群模式来满足业务快速增长.其中集群模式下集合也可以非分片.本次主要讨论将非空的非分片集合转换成分片集合时注意事项以及遇到的问题.否则转换后造成业务不可用且转换是不可逆都操作,此动作发生时,需要在测试环境中应用经过完整的测试后可在生产环境上线,转换动作就是索引+shardCollection 2个组合动作即可(非常简单),如何把副本集转换成集群模式不在本次讨论范围内.
相比非分片集合,分片集合主要利用分片键能够实现负载均衡,如分片策略设计不合理、查询不带分片键等都会导致集群性能低,那么分片集群规划必须与业务相结合,才能最大化集群都性能. 那么分片方式如何设计?MongoDB中支持范围与哈希分片方式,范围分片能够更有利于基于分片键的范围查询,哈希分片更有利于基于分片键等值查询以及均衡写入.不管是那种方式都需要规划合理的分片键.
好的分片键通常满足如下特征:
1、分片键基数高、低频率
2、写请求能够均衡分布
3、大部分查询路由到目标分片而非广播
【注意事项】
1、非空集合的分片键需要预先创建索引,否则无法将非分片集合转成分片集合,
此操作不可逆,分片集合不能转成非分片集合
2、非分片集合转成分片,根据采用chunk size以及文档平均大小来决定非分片集合
最大值,例如分片键平均是64字节时采用默认64M chunk,支持最大8TB的集合.参考
如下图.
3、原应用操作的是非分片集合,需要注意插入、更新、删除分片键问题,否则转换后会
导致应用报错,例如插入不带分片键的文档,更新采用upsert方式以及
findandmodify必须带分片键才能执行.
4、如果非分片转分片时,提示couldn't find valid index for shard key:
1、分片键是索引可能是多key索引,例如数组 2、遇到bug.
5、4.4版本支持插入不带分片键的文档,分片键对应值为null.4.4版本之前必须
带完整的分片键.
6、非分片转换成分片集合,mongo使用writeConcern是majority级别.
[shardCollection内部主要执行步骤]
1、执行过程会检查分片键索引是否存在
2、执行checkShardingIndex来检查索引是否满足完整分片键
如下命令就是检查索引是否满足条件的语句
db.runCommand({"checkShardingIndex":"xiaoxu.POCCOLL",
"keyPattern":{"callP":"hashed"}})
3、对每一条记录都进行验证(如果大表可能执行时间比较久且存在表大小限制问题)
4、更新config.collections信息
5、创建初始化chunk
其中checkShardingIndex是很关键步骤,需要对对每一条记录都要检查.
【基于range还是hashed分片规则】
1、集合中分片键不存在索引,需要预先创建好分片键索引.
2、非空集合根据chunk大小不同,对原集合有大小限制
3、mongos执行sh.shardCollection(namespace,{分片键信息})
例如hash分片
sh.shardCollection("xiaoxu.POCCOLL",{"callP":"hashed"})
例如range分片
sh.shardCollection("xiaoxu.POCCOLL",{"callP":1})
备注:如果是range分片,通常采用组合分片键,4.4版本支持range+hash组合方式
【以非空hashed分片为例,会出现如下错误】
【转换时报错】
1、如果没有索引,直接提示错误,无法进行分片
"Please create an index that starts with the proposed shard
key before sharding the collection"
【解决方案】
db.POCCOLL.createIndex({"callP":"hashed"})
sh.shardCollection("xiaoxu.POCCOLL",{"callP":"hashed"})
2、如果文档中缺少分片字段,也无法进行分片,分片字段值可以是null或者空。
”There are documents which have missing or incomplete shard
key fields ({ : 2338878944348059895 }). Please ensure that
all documents in the collection include all fields from
the shard key."
【解决方案--处理有问题都数据】
备注:对应应用必须更改,后续插入文档中包括分片字段,否则转换后应用报错
1、找到文档填充分片字段或者删除文档
db.POCCOLL.find({callP:{$exists:false}})
{ "_id" : ObjectId("607f830825795fbf8cc40f7b"), "name" : "xiaojing", "address" : "shanghai" }
{ "_id" : ObjectId("607f831125795fbf8cc40f7c"), "name" : "xiaoxing", "address" : "shanghai" }
【例如删除记录】
mongos> db.POCCOLL.remove({callP:{$exists:false}})
WriteResult({ "nRemoved" : 2 })
【例如更新记录】--更新需要按需
mongos>db.POCCOLL.update({callP:{$exists:false}},
{$set:{callP:""})
2、如果索引创建有问题或者分片j键不符合要求,比如是多key索引直接提示如下错误
"couldn't find valid index for shard key"
【转换后报错】
1、【更新】条件中不包括分片键时或者_id,满足多条记录,此时multi=false提示报错.
{multi:false} update on a sharded collection must either
contain an exact match on _id or must target a single shard
but this update targeted _id (and have the collection default
collation) or must target a single shard (and have the simple
collation), but this update targeted 2 shards.
备注:如果_id不是分片键,更新操作同样是下发到所有分片,可能会存在更新多条问题
,即使是multi:false也不生效.
因为_id是非分片键时,只能保证单个分片内值是唯一,而不是全局唯一。
2、【替换】条件中必须包括完整分片键,否则报错,4.2版本支持更新分片键,4.2之前版本不支持
Shard key update is not allowed without specifying the full
shard key in the query
3、【更新插入—更新与替换】使用upsert:true,条件必须包括完整分片键,否则报错,Failed to target upsert by query :: could not extract exact shard key备注:4.4版本允许设置分片键为null+组合其他条件作为查询条件,进行插入更新操作.
mongos>db.POCCOLL.update({"address":"jiangsu",callP:null},
{callP:1771235175,name:"xiaoxu",address:"jiuting"},
{upsert:true})
WriteResult({
"nMatched" : 0,
"nUpserted" : 1,
"nModified" : 0,
"_id" : ObjectId("607fb561be529318222c78cf")
})
4、4.4版本之前插入文档时必须包括完整分片键信息,4.4版本可以不包括分片键.所有不包括分片键的文档放在一个chunk里面.
"document { _id: ObjectId('607fb8ebd432266ecaf9e44a'),
name: \"nanjing\", address: \"nnajing\" }
does not contain shard key for
pattern { callP: \"hashed\" }"
删除可以通过多个维护去删除,可以包括分片键也可以不包括分片—与非分片下操作一致.当删除分片集合数据时指定justOne:true时,必须指定分片键等值条件或者_id.否则会报错.
mongos> db.students2.remove({},{justOne:true})
WriteResult({
"nRemoved" : 0,
"writeError" : {
"code" : 61,
"errmsg" : "A single delete on a sharded collection must
contain an exact match on _id (and have the collection
default collation) or contain the shard key (and have the
simple collation). Delete request: { q: {}, limit: 1 },
shard key pattern: { _id: 1.0 }"
}
})
5、当使用findAndModify时,查询必须包括分片键的等值条件.从4.4版本,文档中可以不包括分片键,可以使用组合条件:分片键:null+其他条件.例如
{ _id: <value>, <shardkeyfield>: null } // _id of the document missing
shard key
mongos>db.POCCOLL.findAndModify({query:{name:"xiaojing"},
update:{$set:{address:"jiuting"}}})
2021-04-23T10:15:33.316+0800 E QUERY
[js] uncaught exception: Error:
findAndModifyFailed failed: {
"ok" : 0,
"errmsg" : "Query for sharded findAndModify must
contain the shard key",
"code" : 61,
"codeName" : "ShardKeyNotFound",
【非分片转分片键后--修改分片键】
【分片键修改值】
备注: 4.2版本支持修改分片键,但分片键是_id不能修改.
1、通过mongs去操作
2、必须在事务内或使用retryWrites参数
3、multi等于false,一次执行修改一条记录
4、必须包括完整shard key在查询条件中
【实际操作】
mongos>db.POCCOLL.update({callP:{$gt:18849795174}},
{$set:{callP:123456789}})
WriteResult({
"nMatched" : 0,
"nUpserted" : 0,
"nModified" : 0,
"writeError" : {
"code" : 72,
"errmsg" : "A {multi:false} update on a sharded
collection must either contain an exact match
on _id or must target a single shard but this update
targeted _id (and have the collection default
collation) or must target a single shard
(and have the simple collation),
but this update targeted 2 shards.
Update request: { q: { callP:
{ $gt: 18849795174.0 } },
u: { $set: { callP: 123456789.0 } },
multi: false, upsert: false },
shard key pattern: { callP: \"hashed\" }"
}
})
mongos>db.POCCOLL.update({callP:18849795174},{$set:
{callP:123456789}})
WriteResult({
"nMatched" : 0,
"nUpserted" : 0,
"nModified" : 0,
"writeError" : {
"code" : 20,
"errmsg" : "Must run update to shard key field
in a multi-statement transaction or with
retryWrites: true."
}
})
mongos>db.POCCOLL.update({"_id" : ObjectId(
"607fd5d8d2a6e53cbdfd21fd")},
{$set:{callP:1889785174}})
WriteResult({
"nMatched" : 0,
"nUpserted" : 0,
"nModified" : 0,
"writeError" : {
"code" : 31025,
"errmsg" : "Shard key update is not allowed
without specifying the full shard key in
the query"}
})
mongo --port 21051 -uadmin --retryWrites
mongos>db.POCCOLL.update({callP:18849795174},
{$set:{callP:123456789}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0,
"nModified" : 1 })
mongos>db.POCCOLL.update({callP:18849795174},
{$set:{callP:123456789}},{multi:true})
WriteResult({
"nMatched" : 0,
"nUpserted" : 0,
"nModified" : 0,
"writeError" : {
"code" : 72,
"errmsg" : "Multi-update operations are not
allowed when updating the shard key field."
}
})
本次文章结束.