最近做數據備份的時候發現了有個很嚴重的問題,那就是數據丟失(最后證明沒丟,是別的問題造成的)。
問題如下:
我通過兩種方式在兩個mongoDB集群中,對一組collection進行備份,最后2個備份數據的數據個數不相同,並且都小於原始collection的count結果。於是便開始尋求解決辦法,流程如下:
1、記錄3組數據,原始數據集按條件count有909217個數據,備份代碼如下,其中replaceOne備份下來的數據有907582條,而使用insertMany備份下來的結果有906281條(注釋是之后加的,之前用的是insertMany):
rdd.foreachPartition { x => {
val mongoURI = new MongoClientURI(uri)
val mongo = new MongoClient(mongoURI)
val db = mongo.getDatabase("wenshu")
val dbColl = db.getCollection("testbackup")
val mongoURI2 = new MongoClientURI(uri2)
val mongo2 = new MongoClient(mongoURI2)
val db2 = mongo2.getDatabase("wenshu")
val dbColl2 = db2.getCollection(backName)
var count = 0
var resList = new ArrayList[Document]
x.foreach(y => {
// count = count + 1
// resList add y
try{
dbColl.replaceOne(eqq("_id", y.get("_id")), y, new UpdateOptions().upsert(true))
dbColl2.insertOne(y)
}catch{
case e: Throwable => e.printStackTrace()
}
// 使用這種方式插入會導致插入的數據和真實數據數量對應不上, 先注釋掉有機會再找原因
// if (count == 10000){
// try{
// dbColl2.insertMany(resList, new InsertManyOptions().ordered(false))
// }catch{
// case e: Throwable => e.printStackTrace()
// }
// resList.clear
// count = 0
// }
})
// if (count > 0)
// try{
// dbColl2.insertMany(resList, new InsertManyOptions().ordered(false))
// }catch{
// case e: Throwable => e.printStackTrace()
// }
2、通過查詢stackoverflow和jira發現數據丟失問題曾經存在過,但都是2.0之前的mongodb,現在商用化之后的mongodb基本沒人出現過數據丟失問題。
3、檢查代碼,發現不是插入代碼錯誤。
4、對抽出來的907582條數據的庫進行備份,還是用上述程序,發現replaceOne的數據有907582,而insertmany只有904291條數據。
5、結合上述條件,推測是insertMany導致部分數據丟失,所以才會出現insertMany結果和replaceOne不一樣。
6、對此結論進行測試,將insertMany改為上述代碼中的insertOne重新備份907582條數據。
7、結果正確,重新備份下來的2份數據都是907582條。目前解決了其中一個問題,就是備份出來的兩份數據不一樣多的問題,接下來考慮備份數據和從總庫中抽取的數據不一致的問題。
8、對mongo shell的count操作查找其工作原理,發現有一些報告count數據不准的問題,結合自身原因推測是count的問題,數據應該只有907582條。
9、通過多抽取幾遍對這個問題進行測試,按同樣條件抽了3遍返回的結果都是907582條,可以認定數據庫中只有907582條滿足此條件的數據。
結論:
1、MongoDB的Count操作有可能返回錯誤的結果。至少在Sharding Cluster,多個索引和2級索引的條件下會出現這種問題。
2、插入時不要使用InsertMany,會導致數據丟失。
3、同理,盡量不要使用updateMany,雖然不會導致數據丟失,但是按照結論2推測有可能出現某些數據更新失敗的情況。
