#python #search #lucene #indexing #sphinx
#python #Поиск #lucene #индексирование #sphinx
Вопрос:
Я пытаюсь создать веб-интерфейс для поиска по большому количеству огромных файлов конфигурации (приблизительно 60000 файлов, каждый размером от 20 КБайт до 50 МБайт). Эти файлы также часто обновляются (~ 3 раза в день).
Требования:
- Параллелизм
- Необходимо определить номера строк для каждой соответствующей строки
- Хорошая производительность обновления
Что я изучил:
- Lucene: Для определения номера строки каждая строка должна храниться в отдельном документе Lucene, каждое из которых содержит два поля (номер строки и строка). Это затрудняет обновление.
- SOLR и Sphinx: оба основаны на Lucene, у них одинаковая проблема и они не позволяют идентифицировать номер строки.
- Таблица SQL с полнотекстовым индексом: опять же, нет способа показать номер строки.
- Таблица SQL с каждой строкой в отдельной строке: тестировал это с помощью SQLite или MySQL, и производительность обновления была худшей из всех вариантов. Обновление документа объемом 50 МБ заняло более часа.
- eXist-db: мы преобразовали каждый текстовый файл в XML следующим образом:
<xml><line number="1">test</line>...</xml>
. Обновления занимают ~ 5 минут, что в некоторой степени работает, но мы по-прежнему недовольны этим. - Whoosh для Python: очень похоже на Lucene. Я реализовал прототип, который работает путем удаления / повторного импорта всех строк данного файла. При использовании этого метода обновление документа размером 50 МБ занимает около 2-3 минут.
- Утилиты GNU id: предложенный sarnold, это невероятно быстрый (документ размером 50 МБ обновляется менее чем за 10 секунд на моей тестовой машине) и был бы идеальным, если бы у него была разбивка на страницы и API.
Как бы вы реализовали альтернативу?
Комментарии:
1. Я не знаю, почему вы сказали, что Lucene работает медленно, я использовал OpenGrok (который, как я полагаю, использует Lucene) в проектах гораздо большего размера, чем этот, скорость хорошая, и обновление совсем не сложное.
2. Lucene не работает медленно, идентифицируя / обновляя все строки каждого документа, если они хранятся таким образом.
3. Почему бы вам не взглянуть на OpenGrok и не попробовать его.
4. При обновлении ваших файлов вы точно знаете, какие номера строк обновляются? Также я почти уверен, что Sphinx не основан на lucene.
Ответ №1:
Возможно, вы захотите изучить инструментарий GNU idutils. В локальной копии исходных текстов ядра Linux он может выдавать вывод, подобный этому:
$ gid ugly
include/linux/hil_mlc.h:66: * a positive return value causes the "ugly" branch to be taken.
include/linux/hil_mlc.h:101: int ugly; /* Node to jump to on timeout */
Восстановление индекса из холодного кэша выполняется достаточно быстро:
$ time mkid
real 1m33.022s
user 0m17.360s
sys 0m2.730s
Восстановление индекса из «теплого» кэша происходит намного быстрее:
$ time mkid
real 0m15.692s
user 0m15.070s
sys 0m0.520s
Индекс занимает всего 46 мегабайт для моих 2,1 гигабайт данных — что немного по сравнению с вашим, но соотношение кажется хорошим.
Поиск 399 вхождений foo
занял всего 0.039
секунды:
$ time gid foo > /dev/null
real 0m0.038s
user 0m0.030s
sys 0m0.000s
Обновить
Ларсмансу было любопытно узнать о производительности git grep
в исходных текстах ядра — это отличный способ показать, какой прирост производительности gid(1)
обеспечивает.
В холодном кэше git grep foo
(который вернул 1656 записей, намного больше, чем idutils):
$ time git grep foo > /dev/null
real 0m19.231s
user 0m1.480s
sys 0m0.680s
Как только кэш был прогрет, git grep foo
работает намного быстрее:
$ time git grep foo > /dev/null
real 0m0.264s
user 0m1.320s
sys 0m0.330s
Поскольку мой набор данных полностью помещается в оперативную память, как только кэш прогреется, git grep
это довольно удивительно: это всего в семь раз медленнее, чем gid(1)
утилита, и, конечно, было бы более чем достаточно быстро для интерактивного использования. Если рассматриваемый набор данных не может быть полностью кэширован (что, вероятно, действительно становится интересным), то преимущество индекса в производительности безошибочно.
Две жалобы на idutils:
-
Нет разбивки на страницы. Это определенно недостаток, хотя, по моему опыту, он выполняется достаточно быстро, чтобы просто сохранить результаты поиска в другом месте. Если поиск будет возвращать значительный процент исходного набора данных, то хранение частичных результатов определенно будет раздражать.
-
Нет API: действительно, API нет. Но исходный код доступен;
src/lid.c
функцияreport_grep()
принимает связанный список файлов, которые соответствуют выходным данным. Небольшая работа с этой функцией должна даже предлагать разбивку на страницы. (Это потребует некоторых действий.) В конце концов, у вас будет C API, который все еще может быть не идеальным. Но его настройка не выглядит ужасно.
Однако недостатком, который, вероятно, является наихудшим, является отсутствие инкрементного обновления базы данных. Если все файлы обновляются три раза в день, это не имеет большого значения. Если некоторые файлы обновляются три раза в день, это приводит к ненужной работе. Если несколько файлов обновляются три раза в день, должно быть лучшее решение.
Комментарии:
1. 1. Мне любопытно, как это соотносится с
git grep
производительностью?2. Только что протестирован, и id-utils, безусловно, достаточно быстр. Однако отсутствие API и разбивки на страницы делают его невыполнимым для веб-приложения.
3. @larsmans, я включил
git grep
тайминги.git grep
невероятно работает с теплым кэшем.
Ответ №2:
На случай, если кому-то это понадобится, я создал Whoosh Store, который по сути представляет собой основанный на Whoosh чистый Python-клон утилит GNU id utils, предоставляющий инкрементные обновления, разбивку на страницы и Python API.
Клиент командной строки работает следующим образом:
ws-update -b --index my.idx datadir # build the index
ws-update -b --append --index my.idx datadir # incremental update
ws --index my.idx hello world # query the index
( -b
предназначен для пакетного обновления, которое выполняется быстрее, но требует больше памяти. Для полного использования синтаксиса CLI --help
.)
Это и близко не соответствует скорости утилит GNU id, но за счет обновления индекса с помощью нескольких инкрементных пакетных обновлений (в памяти) для нас это достаточно быстро.