天天看點

MongoDB 非分片集合轉分片集合

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級别.           

複制

MongoDB 非分片集合轉分片集合

[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."
}
})           

複制

本次文章結束.