couchDB的过滤速度非常慢,即使使用erlang也是如此

very slow filters with couchDB even with erlang

本文关键字:erlang 过滤 速度 非常 couchDB      更新时间:2023-09-26

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

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

现在我希望有一天将这个数据库与移动设备同步。显然,9万名医生不应该同时打电话。这就是我写过滤函数的原因。这些应该通过"productName"进行过滤。首先在Javascript中,然后在Erlang中获得性能。这些Filter函数在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文档。但我真的不知道从哪里开始,怎么做。

我问这个问题已经有一段时间了。但我想我应该回来分享我们最终解决这个问题的方法。

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

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

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

我可能是错的,但过滤器函数应该返回布尔值,所以尝试改变一个:

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

这是一个解释为什么它是缓慢的(至少与JavaScript)…

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

例如:

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

你可以用_design/docs/_view/by_producName?关键="芬达"

然后用找到的doc id开始复制…

一般来说,couchDB过滤器很慢。其他人已经解释了为什么他们行动缓慢。我发现使用过滤器的唯一合理方法是使用"since"。否则,在一个相当大的数据库中(我的数据库有47k个文档,它们是复杂的文档),过滤器不起作用。我们从开发部门迁移到生产部门(几百个文档到47k个文档)的过程中吸取了教训。我们还将设计更改为查询视图,因为我们需要一个类似连续提要的行为,我们使用了Spring的@Scheduled