最近做数据备份的时候发现了有个很严重的问题,那就是数据丢失(最后证明没丢,是别的问题造成的)。

问题如下:

我通过两种方式在两个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推测有可能出现某些数据更新失败的情况。