Подтвердить что ты не робот

Очень медленные фильтры с couchDB даже с erlang

У меня есть база данных (couchDB) с около 90k документами в ней. Документы очень просты:

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

теперь я хочу, чтобы в один день была синхронизирована эта база данных с мобильным устройством. Очевидно, 90k docs не должны сразу заходить в телефон. Вот почему я написал функции фильтра. Они должны фильтроваться с помощью "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)

btw это Минуты: секунды. Число - это число элементов, возвращаемых фильтром, и в базе данных было 90k элементов. Большой сюрприз заключался в том, что фильтр Эрланг не был быстрее.

Для запроса всех элементов требуется всего 9 секунд. И создание просмотров около 15. Но мое использование на телефоне не может передать все документы (объем и причины безопасности).

Есть ли способ фильтровать представление, чтобы увеличить производительность? Или что-то не так с моими фильтрами erlang (я не удивляюсь временам для фильтров JavaScript).

EDIT: Как указано pgras, причина, по которой это происходит медленно, отправляется в ответ на этот Вопрос. Чтобы фильтры erlang выполнялись быстрее, мне нужно перейти на "слой" ниже и запрограммировать erlang непосредственно в базу данных, а не как документ _design. Но я не знаю, с чего начать и как это сделать. Любые советы будут полезны.

4b9b3361

Ответ 1

Прошло некоторое время с тех пор, как я задал этот вопрос. Но я думал, что вернусь к нему и расскажу, что мы сделали, чтобы решить эту проблему.

Таким образом, короткий ответ - скорость фильтра не может быть улучшена.

Причина заключается в том, что работают фильтры. Если вы проверите изменения своей базы данных. Они здесь:

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 - это имя для представления. Я полагаю, что функция самоочевидна. Так вот что мы сделали, я надеюсь, что это поможет кому-то, если он столкнется с подобной проблемой.

Ответ 2

Возможно, я ошибаюсь, но функции фильтра должны возвращать логические значения, поэтому попробуйте изменить их на:

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

Он может решить вашу проблему с производительностью...

Edit:

Здесь объясняется, почему он медленный (по крайней мере, с JavaScript)...

Одним из решений было бы использовать представление для выбора идентификаторов документов для синхронизации, а затем для запуска синхронизации, указав doc_ids для синхронизации.

Например, представление будет выглядеть следующим образом:

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

Вы можете вызвать представление с помощью _design/docs/_view/by_producName? key = "Fanta"

И затем запустите репликацию с найденными идентификаторами doc...

Ответ 3

В целом фильтры couchDB медленны. Другие уже объяснили, почему они медленны. Я обнаружил, что единственным разумным способом использования фильтров является использование "с тех пор". В противном случае в достаточно большой базе данных (у меня есть документы 47k, а они сложны docs) фильтры не работают. Мы усвоили это трудным путем, перейдя с dev на prod [несколько сотен документов до ~ 47 тыс. Документов]. Мы также изменили дизайн на запрос в виде, и поскольку нам потребовалось непрерывное поведение, например, мы использовали Spring @Scheduled