草庐IT

performance - 我们如何改进MongoDB MapReduce函数,该函数需要很长时间才能检索数据并导致内存不足错误?

coder 2023-11-01 原文

从mongo检索数据花费的时间太长,即使对于小数据集也是如此。对于更大的数据集,我们会得到javascript引擎的内存不足错误。我们尝试了几种模式设计和几种检索数据的方法。如何优化mongodb/mapreduce函数/mongowire以更快地检索更多数据?
我们对MongoDB还不是很有经验,因此不确定我们是否遗漏了优化步骤,或者只是使用了错误的工具。
1。背景
为了绘图和回放,我们希望随着时间的推移存储多个对象的更改。目前我们每个项目有几十个对象,但我们需要存储数千个对象。对象可能每秒更改一次,也可能长时间不更改。delphi后端通过mongowire和superobjects对mongodb进行写入和读取,数据显示在web前端。
2。模式设计
我们将对象的变化存储在每小时记录中的分钟秒毫秒对象中。模式设计如所述。样品:
O:目标1,
日期:$日期,
v:{0:{0:{0:{speed:8,rate:0.8}},1:{0:{0:{speed:9}},…}
我们把索引放在{dt: -1, o: 1}{o:1}上。
三。检索数据
我们使用mapreduce基于分钟秒毫秒对象构造一个新日期,并将该对象放回v:
O:目标1,
日期:$日期,
v:{速度:8,速率:0.8}
在mapreduce函数之前,文档的平均大小约为525kb,并且已经更新了约29000次。对这样一个文档进行mapreduce后,结果约为746kb。
3.1使用MapReduce通过Mongo Shell检索数据
我们使用以下映射函数:

function mapF(){    
    for (var i = 0; i < 3600; i++){
        var imin = Math.floor(i / 60);
        var isec = (i % 60);

        var min = ''+imin;
        var sec = ''+isec;

        if (this.v.hasOwnProperty(min) && this.v[min].hasOwnProperty(sec)) {
            for (var ms in this.v[min][sec]) {
                if (imin !== 0 && isec !== 0 && ms !== '0' && this.v[min][sec].hasOwnProperty(ms)) {// is our keyframe              
                    var currentV = this.v[min][sec][ms];

                    //newT is new date computed by the min, sec, ms above

                    if (toDate > newT && newT > fromDate) {
                        if (fields && fields.length > 0) {

                            for (var p = 0, length = fields.length; p < length; p++){
                                //check if field is present and put it in newV
                            }

                            if (newV) {
                                emit(this.o, {vs: [{o: this.o, dt: newT, v: newV}]});
                            }
                        } else {
                            emit(this.o, {vs: [{o: this.o, dt: newT, v: currentV}]});               
                        }
                    }
                }
            }
        }
    }
};

reduce函数基本上只是传递数据。调用mapreduce:
db.collection.mapReduce( mapF,reduceF,
                        {out: {inline: 1},
                         query: {o: {$in: objectNames]}, dt: {$gte: keyframeFromDate, $lt: keyframeToDate}},
                         sort: {dt: 1},
                         scope: {toDate: toDateWithinKeyframe, fromDate:  fromDateWithinKeyframe, fields: []},
                         jsMode: true});

在1小时内检索2个对象:2,4秒。
在5小时内检索2个对象:8,3秒。
对于这个方法,我们必须在运行时编写js和bat文件,并读回json数据。我们还没有衡量他的时间,因为坦率地说,我们不太喜欢这个主意。
此方法的另一个问题是,当我们尝试检索较长时间和/或更多对象的数据时,v8 javascript引擎会出现内存不足错误。使用带有更多RAM的PC在一定程度上可以防止内存不足,但这并不能使数据检索更快。
这个here提到了splitvector,我们可以使用它来划分工作负载。但我们不确定如何使用keypattern和maxchunksizebytes选项。我们可以同时为odt使用键模式吗?
我们可能使用多个集合,但我们的数据集目前还没有那么大,所以我们担心需要多少集合。
3.2使用MapReduce通过MongoWire检索数据
对于使用mapreduce通过mongowire检索数据,我们使用与上面相同的mapreduce函数。我们使用以下delphi代码启动te查询:
FMongoWire.Get('$cmd',BSON([
      'mapreduce', ‘collection’,
      'map', bsonJavaScriptCodePrefix + FMapVCRFunction.Text,
      'reduce', bsonJavaScriptCodePrefix + FReduceVCRFunction.Text,
      'out', BSON(['inline', 1]),
      'query', mapquery,
      'sort', BSON(['dt', -1]),
      'scope', scope
    ]));

用这种方法检索数据大约需要3-4次(!)更慢的。然后将数据从bson(ibsondocument)转换为json(superobject),这是该方法中非常耗时的部分。为了检索原始数据,我们使用tmongowirequery将bsondocument翻译成部分,而这个mapreduce函数直接使用tmongowire并尝试翻译完整的结果。这也许可以解释为什么这需要这么长时间,而通常它是相当快的。如果我们能够减少mapreduce返回结果所需的时间,那么这可能是我们下一步要关注的问题。
3.3 delphi中原始数据的检索与解析
将原始数据检索到delphi所需的时间比前面的方法稍长,但可能由于使用了tmongowirequery,从bson到json的转换要快得多。
4。问题
我们能对模式设计做进一步的优化吗?
如何使mapreduce函数更快?
我们怎样才能防止
V8发动机内存错误?有人能提供更多的信息吗
分裂向量函数?
如何最好地使用delphi中的mapreduce?我们能用吗
MongowireQuery代替Mongowire?
5。规格
MongoDB 3.0.3版
Mongowire从2015年开始(最近更新)
Delphi2010(也有XE5)
4GB内存(也在8GB内存上试用,内存不足较少,但读取时间差不多)

最佳答案

哎呀,真是个问题!首先:我不是MongoDB的专家。我写tmongowire是为了了解一点mongodb。另外,我真的(真的)不喜欢包装器有过多的重载来做相同的事情,但是对于所有类型的特定类型。很久以前程序员没有泛型,但我们有变体。所以我基于变体构建了一个mongodb包装器(和ibsondocument)。这就是说,我显然做了一些人们喜欢用的东西,通过保持它的简单性能相当好。(我最近没有花太多时间在这上面,但最重要的是迎合了版本3以来的新身份验证方案。)
现在,关于你的具体设置。你说你用mapreduce从500kb到700kb?我认为有迹象表明你在工作中使用了错误的工具。我不确定默认的mongo shell与您在TMongoWire.Get上执行相同操作时有什么不同,但是如果我假设mapreduce在通过连线发送响应之前首先组装响应,那么性能就会下降。
所以我的建议是:考虑使用tmongowirequery是对的。它提供了一种快速处理数据的方法,因为服务器会将数据流化,但还有更多。
我强烈建议使用数组来存储秒列表。即使不是所有秒都有数据,也要在没有数据的秒上存储null,这样每分钟数组就有60个项。这就是为什么:
在设计tmongowirequery时出现的一个细节是,假设您一次处理一个(bson)文档,并且文档的内容大致相似,至少在值名上是这样。因此,通过在枚举响应时使用同一个ibsondocument实例,您实际上不必取消分配和重新分配所有这些变量,从而节省了大量时间。
这适用于简单的文档,但实际上在数组上也很好。这就是我创建ibsondocumentenumerator的原因。您需要在预期的文档数组位置预先加载带有ibsondocumentenumerator的ibsondocument实例,并且您需要以与tmongowirequery大致相同的方式处理该数组:使用相同的ibsondocument实例枚举它,因此当后续的documeNT具有相同的密钥,不必重新分配密钥就节省了时间。
不过,在您的情况下,您仍然需要通过连线拉取整个小时的数据,以便选择所需的秒数。正如我之前所说,我不是MongoDB专家,但我怀疑有更好的方法来存储这样的数据。或者每秒使用一个单独的文档(我想这会让索引做更多的工作,MongoDB可以采用这个插入率),或者使用一个特定的查询构造,以便MongoDB知道如何将秒数组缩短为您请求的数据(这就是$splice所做的吗?)
下面是一个如何在{name:"fruit",items:[{name:"apple"},{name:"pear"}]}这样的文档上使用ibsondocumentenumerator的示例。

q:=TMongoWireQuery.Create(db);
try
  q.Query('test',BSON([]));
  e:=BSONEnum;
  d:=BSON(['items',e]);
  d1:=BSON;
  while q.Next(d) do
   begin
    i:=0;
    while e.Next(d1) do
     begin
      Memo1.Lines.Add(d['name']+'#'+IntToStr(i)+d1['name']);
      inc(i);
     end;
   end;
finally
  q.Free;
end;

关于performance - 我们如何改进MongoDB MapReduce函数,该函数需要很长时间才能检索数据并导致内存不足错误?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30774069/

有关performance - 我们如何改进MongoDB MapReduce函数,该函数需要很长时间才能检索数据并导致内存不足错误?的更多相关文章

  1. ruby - 如何使用 Nokogiri 的 xpath 和 at_xpath 方法 - 2

    我正在学习如何使用Nokogiri,根据这段代码我遇到了一些问题:require'rubygems'require'mechanize'post_agent=WWW::Mechanize.newpost_page=post_agent.get('http://www.vbulletin.org/forum/showthread.php?t=230708')puts"\nabsolutepathwithtbodygivesnil"putspost_page.parser.xpath('/html/body/div/div/div/div/div/table/tbody/tr/td/div

  2. ruby - 如何从 ruby​​ 中的字符串运行任意对象方法? - 2

    总的来说,我对ruby​​还比较陌生,我正在为我正在创建的对象编写一些rspec测试用例。许多测试用例都非常基础,我只是想确保正确填充和返回值。我想知道是否有办法使用循环结构来执行此操作。不必为我要测试的每个方法都设置一个assertEquals。例如:describeitem,"TestingtheItem"doit"willhaveanullvaluetostart"doitem=Item.new#HereIcoulddotheitem.name.shouldbe_nil#thenIcoulddoitem.category.shouldbe_nilendend但我想要一些方法来使用

  3. ruby-on-rails - Ruby net/ldap 模块中的内存泄漏 - 2

    作为我的Rails应用程序的一部分,我编写了一个小导入程序,它从我们的LDAP系统中吸取数据并将其塞入一个用户表中。不幸的是,与LDAP相关的代码在遍历我们的32K用户时泄漏了大量内存,我一直无法弄清楚如何解决这个问题。这个问题似乎在某种程度上与LDAP库有关,因为当我删除对LDAP内容的调用时,内存使用情况会很好地稳定下来。此外,不断增加的对象是Net::BER::BerIdentifiedString和Net::BER::BerIdentifiedArray,它们都是LDAP库的一部分。当我运行导入时,内存使用量最终达到超过1GB的峰值。如果问题存在,我需要找到一些方法来更正我的代

  4. python - 如何使用 Ruby 或 Python 创建一系列高音调和低音调的蜂鸣声? - 2

    关闭。这个问题是opinion-based.它目前不接受答案。想要改进这个问题?更新问题,以便editingthispost可以用事实和引用来回答它.关闭4年前。Improvethisquestion我想在固定时间创建一系列低音和高音调的哔哔声。例如:在150毫秒时发出高音调的蜂鸣声在151毫秒时发出低音调的蜂鸣声200毫秒时发出低音调的蜂鸣声250毫秒的高音调蜂鸣声有没有办法在Ruby或Python中做到这一点?我真的不在乎输出编码是什么(.wav、.mp3、.ogg等等),但我确实想创建一个输出文件。

  5. ruby-on-rails - 如何验证 update_all 是否实际在 Rails 中更新 - 2

    给定这段代码defcreate@upgrades=User.update_all(["role=?","upgraded"],:id=>params[:upgrade])redirect_toadmin_upgrades_path,:notice=>"Successfullyupgradeduser."end我如何在该操作中实际验证它们是否已保存或未重定向到适当的页面和消息? 最佳答案 在Rails3中,update_all不返回任何有意义的信息,除了已更新的记录数(这可能取决于您的DBMS是否返回该信息)。http://ar.ru

  6. ruby-on-rails - 'compass watch' 是如何工作的/它是如何与 rails 一起使用的 - 2

    我在我的项目目录中完成了compasscreate.和compassinitrails。几个问题:我已将我的.sass文件放在public/stylesheets中。这是放置它们的正确位置吗?当我运行compasswatch时,它不会自动编译这些.sass文件。我必须手动指定文件:compasswatchpublic/stylesheets/myfile.sass等。如何让它自动运行?文件ie.css、print.css和screen.css已放在stylesheets/compiled。如何在编译后不让它们重新出现的情况下删除它们?我自己编译的.sass文件编译成compiled/t

  7. ruby - 如何将脚本文件的末尾读取为数据文件(Perl 或任何其他语言) - 2

    我正在寻找执行以下操作的正确语法(在Perl、Shell或Ruby中):#variabletoaccessthedatalinesappendedasafileEND_OF_SCRIPT_MARKERrawdatastartshereanditcontinues. 最佳答案 Perl用__DATA__做这个:#!/usr/bin/perlusestrict;usewarnings;while(){print;}__DATA__Texttoprintgoeshere 关于ruby-如何将脚

  8. ruby - 如何指定 Rack 处理程序 - 2

    Rackup通过Rack的默认处理程序成功运行任何Rack应用程序。例如:classRackAppdefcall(environment)['200',{'Content-Type'=>'text/html'},["Helloworld"]]endendrunRackApp.new但是当最后一行更改为使用Rack的内置CGI处理程序时,rackup给出“NoMethodErrorat/undefinedmethod`call'fornil:NilClass”:Rack::Handler::CGI.runRackApp.newRack的其他内置处理程序也提出了同样的反对意见。例如Rack

  9. ruby - 如何每月在 Heroku 运行一次 Scheduler 插件? - 2

    在选择我想要运行操作的频率时,唯一的选项是“每天”、“每小时”和“每10分钟”。谢谢!我想为我的Rails3.1应用程序运行调度程序。 最佳答案 这不是一个优雅的解决方案,但您可以安排它每天运行,并在实际开始工作之前检查日期是否为当月的第一天。 关于ruby-如何每月在Heroku运行一次Scheduler插件?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/8692687/

  10. ruby-on-rails - 如何从 format.xml 中删除 <hash></hash> - 2

    我有一个对象has_many应呈现为xml的子对象。这不是问题。我的问题是我创建了一个Hash包含此数据,就像解析器需要它一样。但是rails自动将整个文件包含在.........我需要摆脱type="array"和我该如何处理?我没有在文档中找到任何内容。 最佳答案 我遇到了同样的问题;这是我的XML:我在用这个:entries.to_xml将散列数据转换为XML,但这会将条目的数据包装到中所以我修改了:entries.to_xml(root:"Contacts")但这仍然将转换后的XML包装在“联系人”中,将我的XML代码修改为

随机推荐