将数百万个JSON文档导入MongoDB的最快方法

时间:2013-10-28 15:30:45

标签: java json performance mongodb import

我有超过1000万种形式的JSON文档:

["key": "val2", "key1" : "val", "{\"key\":\"val", \"key2\":\"val2"}"]

在一个文件中。

使用JAVA Driver API导入大约需要3个小时,同时使用以下功能(一次导入一个BSON):

public static void importJSONFileToDBUsingJavaDriver(String pathToFile, DB db, String collectionName) {
    // open file
    FileInputStream fstream = null;
    try {
        fstream = new FileInputStream(pathToFile);
    } catch (FileNotFoundException e) {
        e.printStackTrace();
        System.out.println("file not exist, exiting");
        return;
    }
    BufferedReader br = new BufferedReader(new InputStreamReader(fstream));

    // read it line by line
    String strLine;
    DBCollection newColl =   db.getCollection(collectionName);
    try {
        while ((strLine = br.readLine()) != null) {
            // convert line by line to BSON
            DBObject bson = (DBObject) JSON.parse(JSONstr);
            // insert BSONs to database
            try {
                newColl.insert(bson);
            }
            catch (MongoException e) {
              // duplicate key
              e.printStackTrace();
            }


        }
        br.close();
    } catch (IOException e) {
        e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
    }


}

有更快的方法吗?也许,MongoDB设置可能会影响插入速度? (例如,添加键:“_ id”将作为索引,因此MongoDB不必创建人工密钥,因此不必为每个文档编制索引)或在插入时完全禁用索引创建。 感谢。

7 个答案:

答案 0 :(得分:8)

对不起,你们都在挑选性能问题,而不是核心问题。将逻辑与读取文件和插入分开是一个很小的收获。以二进制模式加载文件(通过MMAP)是一个很小的收获。使用mongo的批量插入是一个很大的收获,但仍然没有骰子。

整个性能瓶颈是BSON bson = JSON.parse(line)。或者换句话说,Java驱动程序的问题是它们需要从json转换为bson,而且这段代码似乎非常缓慢或者执行得很糟糕。通过JSON-simple或特别是通过JSON-smart完整的JSON(编码+解码)比JSON.parse()命令快100倍。

我知道Stack Overflow正在告诉我,我应该回答这个答案,我不是,但请放心,我仍然在寻找这个问题的答案。我无法相信所有关于Mongo性能的讨论,然后这个简单的示例代码失败如此悲惨。

答案 1 :(得分:5)

我已经完成了一个包含约250M记录的多行json文件。我只是使用mongoimport< data.txt,花了10个小时。与你的10M和3小时相比,我认为这要快得多。

另外根据我的经验,编写自己的多线程解析器会大大加快速度。程序很简单:

  1. 将文件打开为BINARY(不是TEXT!)
  2. 在文件中均匀设置标记(偏移)。标记的数量取决于您想要的线程数。
  3. 在标记附近搜索'\ n',校准标记,使它们与线条对齐。
  4. 用线程解析每个块。
  5. 提醒:

    如果需要性能,请不要使用流阅读器或任何内置的基于行的读取方法。他们很慢。只需使用二进制缓冲区并搜索'\ n'来标识一行,并且(最优选地)在缓冲区中进行就地解析而不创建字符串。否则垃圾收集者会对此不满意。

答案 2 :(得分:4)

您可以一次解析整个文件并在mongo文档中插入整个json,避免多个循环,您需要按如下方式分离逻辑:

1)解析文件并检索json对象。

2)解析结束后,将json对象保存在Mongo文档中。

答案 3 :(得分:3)

我有一个稍快的方式(我现在也插入了数百万),插入集合而不是单个文档

insert(List<DBObject> list)

http://api.mongodb.org/java/current/com/mongodb/DBCollection.html#insert(java.util.List)

那就是说,它并没有那么快。我即将尝试设置其他WriteConcerns而不是ACKNOWLEDGED(主要是UNACKNOWLEDGED),看看我是否可以加快速度。有关信息,请参阅http://docs.mongodb.org/manual/core/write-concern/

提高性能的另一种方法是在批量插入后创建索引。但是,除了一次性工作外,这很少是一种选择。

道歉,如果这听起来有点毛茸茸,我还在自己测试一下。好问题。

答案 4 :(得分:2)

使用批量操作insert / upserts。在Mongo 2.6之后,您可以批量Updates/Upserts。下面的示例使用c#驱动程序进行批量更新。

MongoCollection<foo> collection = database.GetCollection<foo>(collectionName);
      var bulk = collection.InitializeUnorderedBulkOperation();
      foreach (FooDoc fooDoc in fooDocsList)
      {
        var update = new UpdateDocument { {fooDoc.ToBsonDocument() } };
        bulk.Find(Query.EQ("_id", fooDoc.Id)).Upsert().UpdateOne(update);
      }
      BulkWriteResult bwr =  bulk.Execute();

答案 5 :(得分:1)

您也可以删除所有索引(当然除了PK索引)并在导入后重建它们。

答案 6 :(得分:0)

您可以使用批量插入

您可以在mongo website阅读文档,也可以在StackOverflow上查看java example