【问题标题】:very slow filters with couchDB even with erlang即使使用 erlang,使用 couchDB 的过滤器也非常慢
【发布时间】:2013-03-01 13:48:17
【问题描述】:

我有一个数据库 (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 指出的那样,速度慢的原因已发布在this 问题的答案中。为了让 erlang 过滤器运行得更快,我需要在下面添加一个“层”并将 erlang 直接编程到数据库中,而不是作为 _design 文档。但我真的不知道从哪里开始以及如何做到这一点。任何提示都会有所帮助。

【问题讨论】:

  • 你使用 _changes 没有参数,所以它会过滤数据库的所有内容,一旦第一次同步完成,你会使用 since 参数进行后续同步吗?您提到使用视图,带有 key="Fanta" 的 -fast- 视图会解决您的其他需求吗?
  • 我想稍后使用过滤器将数据库复制到移动设备,我只使用http请求来测试过滤器。我尝试使用过滤器进行复制并得到了类似的结果。我认为视图不会有帮助,因为稍后应该有一个带有过滤器的查询。
  • 经历同样的事情。使用 Erlang 进行过滤复制时,我得到了大约 55% 的改进,但它仍然非常缓慢。

标签: javascript view filter erlang couchdb


【解决方案1】:

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

【讨论】:

    【解决方案2】:

    自从我问这个问题以来已经有一段时间了。但我想我会回到它并分享我们最终为解决这个问题所做的工作。

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

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

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

    【讨论】:

      【解决方案3】:

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

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

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

      编辑:

      Here 解释了为什么它很慢(至少对于 JavaScript)...

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

      例如,视图将是:

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

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

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

      【讨论】:

      • 这只是 javaScript 函数,erlang 确实返回布尔值。
      • @ArneFischer,是的,我在回答后看到了...我有一些问题,但我在最初问题的 cmets 中问...
      • 使用视图创建文档列表,然后传递给复制是我们所做的。它看起来很笨重,但工作正常。有人对这种方法进行过速度测试吗?
      • 我们最终也使用了视图。据我所知,他们使用标准的 B-Tree。所以他们有很好的表现,但我不能给你数字。
      猜你喜欢
      • 1970-01-01
      • 2018-02-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-01-21
      相关资源
      最近更新 更多