即使使用Erlang,CouchDB的过滤器非常缓慢。

12

我有一个包含大约90k个文档的数据库(couchDB)。这些文档非常简单,就像这样:

{
   "_id": "1894496e-1c9e-4b40-9ba6-65ffeaca2ccf",
   "_rev": "1-2d978d19-3651-4af9-a8d5-b70759655e6a",
   "productName": "Cola"
}

现在我希望有一天可以将这个数据库与移动设备同步。显然,90000个文档不应该一次全部放到手机上。这就是为什么我编写了过滤函数。它们应该按“产品名称”进行过滤。起初使用JavaScript编写,后来使用Erlang提高性能。这些过滤函数在JavaScript中的代码如下:

{
   "_id": "_design/local_filters",
   "_rev": "11-57abe842a82c9835d63597be2b05117d",
   "filters": {
       "by_fanta": "function(doc, req){ if(doc.productName == 'Fanta'){ return doc;}}",
       "by_wasser": "function(doc, req){if(doc.productName == 'Wasser'){ return doc;}}",
       "by_sprite": "function(doc, req){if(doc.productName == 'Sprite'){ return doc;}}"
   }
}

而在Erlang中可以这样写:

{
   "_id": "_design/erlang_filter",
   "_rev": "74-f537ec4b6508cee1995baacfddffa6d4",
   "language": "erlang",
   "filters": {
       "by_fanta": "fun({Doc}, {Req}) ->  case proplists:get_value(<<\"productName\">>, Doc) of <<\"Fanta\">> -> true; _ -> false end end.",
       "by_wasser": "fun({Doc}, {Req}) ->  case proplists:get_value(<<\"productName\">>, Doc) of <<\"Wasser\">> -> true; _ -> false end end.",
       "by_sprite": "fun({Doc}, {Req}) ->  case proplists:get_value(<<\"productName\">>, Doc) of <<\"Sprite\">> -> true; _ -> false end end."       
   }
}

为了讲得简单,目前还没有查询条件,只有一个“硬编码”的字符串。所有的过滤器都有效。但问题在于它们太慢了。我先用Java写了一个测试程序,后来用Perl重写以测试过滤文档所需的时间。这是其中一个Perl脚本:
$dt = DBIx::Class::TimeStamp->get_timestamp();

$content = get("http://127.0.0.1:5984/mobile_product_test/_changes?filter=local_filters/by_sprite");

$dy = DBIx::Class::TimeStamp->get_timestamp() - $dt;
$dm = $dy->minutes();
$dz = $dy->seconds();

@contArr = split("\n", $content);

$arraysz = @contArr;
$arraysz = $arraysz - 3;

$\="\n";
print($dm.':'.$dz.' with '.$arraysz.' Elements (JavaScript)');

现在是令人伤心的部分。这些是我经常遇到的时候:

2:35 with 2 Elements (Erlang)
2:40 with 10000 Elements (Erlang)
2:38 with 30000 Elements (Erlang)
2:31 with 2 Elements (JavaScript)
2:40 with 10000 Elements (JavaScript)
2:51 with 30000 Elements (JavaScript)

顺便提一下,这些是分钟:秒。数字是过滤器返回的元素数量,数据库中有90k个元素。令人惊讶的是,Erlang 过滤器并没有变得更快。

请求所有元素只需要9秒。创建视图大约需要15秒。但是出于卷和安全原因,我无法在手机上传输所有文档。

是否有一种方法可以在视图上进行过滤以提高性能?还是我的 Erlang 过滤函数有问题(JavaScript 过滤器的时间我不感到意外)。

编辑: 正如 pgras 指出的那样,为什么这很慢的原因在这个问题的答案中已经发布。要使 Erlang 过滤器运行更快,我需要在一个“层”下编程并将 Erlang 直接编入数据库而不是作为设计文档。但我确实不知道从哪里开始,也不知道该怎么做。任何提示都有帮助。


您在使用_changes时没有传入since参数,因此它将过滤掉数据库中的所有内容,一旦进行第一次同步,您是否会在后续的同步中使用since参数呢?您提到了使用视图,一个-key="Fanta"-的-fast-视图是否能够解决您的其他需求呢? - pgras
我想稍后使用过滤器将数据库复制到移动设备上,我只是使用http请求来测试过滤器。我尝试使用过滤器进行复制,并获得了类似的结果。我认为视图不会有帮助,因为稍后应该有一个带有过滤器的查询。 - Arne Fischer
经历了同样的事情。当使用Erlang进行过滤复制时,我获得了约55%的改进,但仍然非常缓慢。 - ryan1234
3个回答

4

我已经有一段时间没有问这个问题了。但是我想回到这个问题,并分享我们最终解决它的方法。

所以简短的答案是过滤器速度不能真正得到提高。

原因在于过滤器的工作方式。如果您检查数据库更改。它们在这里:

http://<ip>:<port>/<databaseName>/_changes

这个文档包含你的数据库中所有更改。如果你在数据库中做了任何事情,新行都会被添加。当需要使用筛选器时,筛选器会从json解析为指定的语言,并用于这个文件中的每一行。就我所知,解析也是对每一行都进行的。这不是很有效率,也无法改变。
所以我个人认为,在大多数情况下,筛选器太慢了,无法使用。这意味着我们必须找到一种解决方法。我并不意味着我有一个通用解决方案。我只能说,对于我们来说,可以使用视图代替过滤器。视图在内部生成树形结构,与过滤器相比速度非常快。一个简单的过滤器也存储在设计文档中,可能如下所示:
{
"_id": "_design/all",
"language": "javascript",
"views": {
    "fantaView": {
        "map": "function(doc) { \n   if (doc.productName == 'Fanta')  \n    emit(doc.locale, doc)\n} "
    }
}
}

fantaView是视图的名称。我猜这个函数很容易理解。所以这就是我们所做的,希望对于遇到类似问题的人有所帮助。


1

总的来说,CouchDB过滤器速度较慢。其他人已经解释了为什么它们速度慢。我发现唯一合理使用过滤器的方法是使用“since”。否则,在一个相当大的数据库中(我的有47k个文档,它们是复杂的文档),过滤器不起作用。我们通过从dev到prod迁移[从几百个文档到~47k个文档]的艰难方式学到了这一点。我们还更改了设计,查询了视图,并且因为我们需要类似于连续的反馈行为,所以我们使用了Spring的@Scheduled。


1
我可能错了,但过滤函数应该返回布尔值,所以尝试将其中一个更改为:
function(doc, req){ return doc.productName === 'Fanta';}

它可能解决您的性能问题...

编辑:

这里是一个关于为什么它很慢(至少在JavaScript中)的解释...

一种解决方案是使用视图来选择要同步的文档的ID,然后通过指定要同步的doc_ids来开始同步。

例如,该视图将是:

function(doc){
  emit(doc.productName, doc._id)
}

你可以使用_design/docs/_view/by_productName?key="Fanta"来调用视图。
然后,使用找到的文档ID开始复制...

好的,这只是 JavaScript 函数,而 Erlang 函数则返回布尔值。 - Arne Fischer
@ArneFischer,是的,在回答后我看到了它...我有一些问题,但我会在初始问题的评论中提问... - pgras
使用视图创建文档列表,然后将其传递到复制是我们的做法。这似乎有些笨拙,但工作得还不错。有人对这种方法进行过速度测试吗? - Mike McKay
我们最终也使用了视图。据我所知,它们使用标准的B-Tree。因此它们具有良好的性能,但我无法给出具体数字。 - Arne Fischer

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接