MongoDB Java API慢速阅读性能

ic3*_*ic3 23 java performance mongodb mongodb-java mongo-java

我们正在从本地MongoDB中读取所有来自集合的文档,并且性能不是很高兴.

我们需要转储所有数据,不要担心为什么,只是相信它真的需要并且没有可能的解决方法.

我们有4mio文件看起来像:

{
    "_id":"4d094f58c96767d7a0099d49",
    "exchange":"NASDAQ",
    "stock_symbol":"AACC",
    "date":"2008-03-07",
    "open":8.4,
    "high":8.75,
    "low":8.08,
    "close":8.55,
    "volume":275800,
    "adj close":8.55
}
Run Code Online (Sandbox Code Playgroud)

我们现在使用这个简单的代码来阅读:

MongoClient mongoClient = MongoClients.create();
MongoDatabase database = mongoClient.getDatabase("localhost");
MongoCollection<Document> collection = database.getCollection("test");

MutableInt count = new MutableInt();
long start = System.currentTimeMillis();
collection.find().forEach((Block<Document>) document -> count.increment() /* actually something more complicated */ );
long start = System.currentTimeMillis();
Run Code Online (Sandbox Code Playgroud)

我们正在以16秒(250k行/秒)的速度阅读整个系列,这对于小型文档来说真的不是很令人印象深刻.请记住,我们要加载800十亿行.没有聚合,地图缩小或类似是可能的.

这是否与MongoDB一样快或者是否有其他方法可以更快地加载文档(其他技术,移动Linux,更多RAM,设置......)?

Mạn*_*yễn 12

您没有指定用例,因此很难告诉您如何调整查询.(即:谁想要一次加载800mil的行只是为了计数?).

鉴于您的架构,我认为您的数据几乎是只读的,您的任务与数据聚合相关.

您当前的工作只是读取数据,(很可能是您的驱动程序将批量读取),然后停止,然后执行一些计算(地狱是的,使用int包装器来增加处理时间),然后重复.这不是一个好方法.如果您没有以正确的方式访问它,则数据库不会神奇地快速.

如果计算不是太复杂,我建议您使用聚合框架而不是将所有内容加载到RAM中.

你应该考虑改善你的聚合:

  1. 将数据集划分为较小的集合.(例如:分区date,分区exchange......).添加索引以支持该分区并在分区上运行聚合然后组合结果(典型的分而治之的方法)
  2. 项目只需要字段
  3. 过滤掉不必要的文件(如果可能)
  4. 如果无法在内存上执行聚合,则允许diskusage(如果每个pipiline达到100MB限制).
  5. 使用内置管道来加速计算(例如:$count为您的示例)

如果您的计算太复杂而无法用聚合框架表达,那么请使用mapReduce.它在mongod流程上运行,数据不需要通过网络传输到您的内存.

更新

所以看起来你想要进行OLAP处理,并且你坚持使用ETL步骤.

您不必每次都必须避免将整个OLTP数据加载到OLAP.只需要对数据仓库加载新的更改.然后第一次数据加载/转储需要更多时间是正常的和可接受的.

首次加载时,您应该考虑以下几点:

  1. Divide-N-Conquer再次将您的数据分解为较小的数据集(具有类似日期/交换/股票标签的谓词......)
  2. 进行并行计算,然后合并您的结果(您必须正确分区数据集)
  3. 在批处理上进行计算而不是处理forEach:加载数据分区然后逐个计算而不是计算.


Ris*_*are 4

collection.find().forEach((Block<Document>) document -> count.increment());

由于您要迭代内存中超过 250k 条记录,因此这一行可能会增加很多时间。

要快速检查是否是这种情况,您可以尝试以下操作 -

long start1 = System.currentTimeMillis();
List<Document> documents = collection.find();
System.out.println(System.currentTimeMillis() - start1);

long start2 = System.currentTimeMillis();
documents.forEach((Block<Document>) document -> count.increment());
System.out.println(System.currentTimeMillis() - start2);
Run Code Online (Sandbox Code Playgroud)

这将帮助您了解从数据库获取文档实际花费了多少时间以及迭代花费了多少时间。