前天因为工作需要,开始着手对数据库中两千多万的数据中其中一个字段重复的数据进行去重。
原本使用一些测试的数据测试后,前天写的那个方法是可行的,但是当面对这个两千万的真实数据时,我却发现这方法有些不顶用了,最终只好又经过若干次的尝试,总算成功去重。
最终总结一下整个过程:
1、这个方法就是上一篇所讲的,利用mongodb的游标
dbcursor和while循环的方式。
var res=db.test.find(); while(res.hasNext()){ var res1=db.test.find(); var re=res.next(); while(res1.hasNext()){ var re1=res1.next(); if(re.age==re1.age){ db.test.remove({"age":re1.age}); } } db.test.insert(re); }
原本我用了10000调数据进行测试,循环完毕后,就如预期一样只剩下1条数据。
但是面对两千万的数据后,执行到一半就报错,并且一直卡在那里。
我也不知道这情况究竟算是正常还是不正常,反正是等了半天的时间还是卡在那里,整个集合的数据也没有任何的变化。
我想大概是一次性处理的数据太多了吧,我的循环那样执行下去,就需要循环两千万乘以两千万次,这个次数是在国语庞大,于是只好采取迂回的措施,把两千万拆分成20个集合,一个集合只装一百万。
但是即便是一百万的数据,当我在执行这个方法时,还是卡在了那里。
于是我不禁就想,难到我要把20个集合再拆分成四十个集合,一个只装五十万?
四十个集合,这工作量貌似有点太大,我选择无奈的放弃。
2、第一种方法失败的情况下,我只好另寻他途,然后便想到了唯一索引的方法。
唯一索引的dropDups可以在建立索引时删除重复的数据,我想这个总应该是可行的吧。
然而事实却证明我还是错了,最终还是以失败告终。
在我执行如下方法
建立唯一索引的时候,又是屡屡报错,并给我意外退出
!
db.alarm1.ensureIndex({"msgContent":1},{"unique":true,dropDups:true})
直接在建立索引的时候删除数据无法达到目的,我只好再次采用迂回的方式,在一个全新的空集合中建立一个索引 :
db.alarmBack.ensureIndex({"msgContent":1},{"unique":true})
然后再把数据重新的导入到这个已经存在了唯一索引的集合中,我想应该可以了吧。
但是,无奈的是,又失败了!
因为我从生产数据库导出的数据用了mongodump的方式,所以导入的时候也用的mongorestore的方式,而这个方式实际上是恢复数据,在恢复数据的同时,连索引也一起恢复了。
最让我抓狂的是,恢复索引也就罢了,竟然还在恢复的时候把我建的唯一索引给干掉了!这样一来,唯一索引没了,自然无法达到去重的目的,方法再次以失败告终。
我不知道mongodump和mongorestore是否有相关参数可以指定不要索引,有空了一定要试一下(太坑了吧)。
3、上述两个方法都失败了,第二个方法失败的同时,我就想到要试一下另外一种导入和导出的方法:mongoimport和mongoexport。
我隐约记得这两个方法导入导出的时候速度好像比mongodump和mongorestore慢,但是现在没有办法的情况下只好一试。
但是事实证明这个方法在这种情况下居然可行,最终我使用第二种方法中的第二种方式,先在空白集合中建一个唯一索引,然后导入要去重的数据,成功的对这两千多万的数据去重。
不过真的是慢啊,单纯的导入,我用mongodump连mongoimport一半的时间都没用到,不知道是否是因为姿势不对,暂且也不想去管它了!
任务结束,但是心中还留下一些疑问,我想如果第二种方法中我导出的元数据是没有索引的,那么当我导入的时候,不知道它是否还会把我原本的唯一索引干掉;还有就是,或许直接用java代码处理导出的文件也可以,甚至可能更快,不过暂时有别的事情,也就不做尝试了。