12

我有一个数据库(couchDB),里面有大约 90k 个文档。文件非常简单,如下所示:

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

现在我想有一天将这个数据库与移动设备同步。显然 90k 文档不应该一次全部转到电话上。这就是我编写过滤器函数的原因。这些应该按“productName”过滤。起初在 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 直接编程到数据库中,而不是作为 _design 文档。但我真的不知道从哪里开始以及如何做到这一点。任何提示都会有所帮助。

4

3 回答 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 是视图的名称。我想这个功能是不言自明的。所以这就是我们所做的,如果他遇到类似的问题,我希望它对某人有所帮助。

于 2014-03-27T17:24:03.147 回答
1

我可能错了,但过滤器函数应该返回布尔值,所以尝试将一个更改为:

function(doc, req){ return doc.productName === 'Fanta';}

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

编辑:

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

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

例如,视图将是:

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

您可以使用 _design/docs/_view/by_producName?key="Fanta" 调用视图

然后使用找到的文档 ID 开始复制...

于 2013-03-13T15:36:36.930 回答
1

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

于 2016-02-21T10:01:41.440 回答