13

我有超过 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 不必为每个文档创建人工键和索引)或在插入时完全禁用索引创建。谢谢。

4

7 回答 7

10

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

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

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

于 2014-09-22T14:41:12.620 回答
5

我已经导入了一个包含 ~250M 记录的多行 json 文件。我只是使用 mongoimport < data.txt 并且花了 10 个小时。与您的 10M 与 3 小时相比,我认为这要快得多。

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

  1. 以 BINARY(不是 TEXT!)格式打开文件
  2. 在文件中均匀设置标记(偏移)。标记的数量取决于您想要的线程数。
  3. 在标记附近搜索“\n”,校准标记以使其与线条对齐。
  4. 用一个线程解析每个块。

提醒:

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

于 2013-10-29T05:29:14.990 回答
4

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

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

2)一旦解析结束,将json对象保存在Mongo Document中。

于 2013-10-29T05:28:48.317 回答
3

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

insert(List<DBObject> list)

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

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

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

抱歉,如果这听起来有点毛骨悚然,我仍在自己测试。好问题。

于 2013-10-28T15:36:16.843 回答
2

使用批量操作插入/更新插入。在Mongo 2.6您可以进行批量更新/更新插入之后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();
于 2014-12-02T20:46:14.127 回答
2

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

于 2013-10-28T17:54:36.870 回答
0

您可以使用批量插入

您可以在mongo 网站上阅读文档,也可以在 StackOverflow 上查看这个java 示例

于 2014-10-02T20:13:09.207 回答