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

2022-09-04 07:35:32

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

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

在一个文件中。

使用 JAVA 驱动程序 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不必创建人工键,从而为每个文档创建索引)或在插入时完全禁用索引创建。谢谢。


答案 1

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

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

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


答案 2

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

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

  1. 以二进制(不是文本!)的形式打开文件!
  2. 在文件中均匀地设置标记(偏移量)。标记数取决于所需的线程数。
  3. 在标记附近搜索“\n”,校准标记以使其与线条对齐。
  4. 使用线程解析每个块。

提醒:

当您需要性能时,请勿使用流读取器或任何内置的基于行的读取方法。它们很慢。只需使用二进制缓冲区并搜索“\n”来标识行,并且(最好)在缓冲区中执行就地解析而不创建字符串。否则垃圾回收器不会对此感到满意。