В области поисковых систем с открытым исходным кодом появилось несколько новых интересных игроков. Мы решили внимательно изучить некоторые из них, чтобы узнать, насколько они сравнимы с Elasticsearch - как по набору функций, так и по производительности.

Кандидаты

  • Elasticsearch - "взрослая" полнотекстовая поисковая система, основанная на Lucene.

  • RediSearch - полнотекстовый поиск поверх Redis от RedisLabs

  • Postgres FTS - полнотекстовые индексы для Postgres

  • TypeSense - альтернатива Algolia с открытым исходным кодом

  • MeiliSearch - альтернатива Algolia с открытым исходным кодом

Функциональное сравнение

Функция

Elastic

RediSearch

PostgreSQL

TypeSense

MeiliSearch

Хранилище

Диск

RAM + снэпшоты

Диск

RAM + снэпшоты

RAM + снэпшоты

Распределенная работа

Мастер-реплика

RAFT

Мастер-реплика

RAFT

НЕТ

Репликация

+

НЕТ

+

НЕТ

НЕТ

Поддерживаемые языки

латиница + cjk + кириллица + арабский + еще 10

латиница + арабский, русский, китайский

латиница + арабский, русский

с пробелами

с пробелами + кандзи

Исправление опечаток

да, но медленно

+

НЕТ

+

+

Бустинг

+

+

+

+

НЕТ

Точный поиск

+

+

+

НЕТ

НЕТ

Синонимы

+

+

+

+

+

Известные ограничения

Elasticsearch: Нестабильность, если в кластере больше ~1000 индексов (или 20 тыс. шардов)

TypeSense: Размер хранилища ограничен доступной оперативной памятью. Источник

Meilisearch: Максимальное количество слов, учитываемых для каждого поискового запроса - 10. Максимальный размер базы данных - 100 ГБ (может быть изменен в конфиге). Максимум 200 индексов. Максимум 1000 слов в одном поле. Источник

Тест

Набор данных: дамп выжимок из английской Википедии enwiki-20210720-abstract.xml
Дата: 20 июля 2021 г.
Документов: 6,3 млн
Размер XML: 6,0 ГБ

Слова запроса выбираются случайным образом из 1000 самых распространенных английских слов.

Конфигурация системы

2 General Purpose / 32 ГБ / 8 vCPU инстанса DigitalOcean (один для генерации нагрузки + один для тестируемой системы).

Полученные результаты

Время индексации

Для индексации мы учитывали только время, которое индексатор потратил на запросы к серверу.

Elasticsearch, PostgreSQL и Typesense показывают здесь очень похожую производительность, в то время как RediSearch примерно в 2 раза медленнее; этот результат странным образом противоречит результатам тестов RedisLabs, поэтому настройка может быть здесь неоптимальной.

В то же время, Meilisearch блещет, будучи почти в 7 раз быстрее остальных.

Тайминги запросов

Опять же, RediSearch оказался в среднем довольно медленным, и снова RedisLabs получили другие результаты. Еще один неожиданно медленный результат показал запрос из трех слов в Typesense.

Meilisearch показал довольно хорошую производительность, особенно для запросов с префиксами и опечатками.

Мы использовали нули для отображения неподдерживаемых типов запросов на графиках, но если приглядеться, видно, что RediSearch показал результат менее 1 мс (!) для запросов «точная фраза» и «три слова И».

Результаты в числах

Тест

Elasticsearch

RediSearch

PostgreSQL

TypeSense

MeiliSearch

Индексирование

- время

268

516

290

272

42 (асинхр.)

- пропускная способность

23644

12267

21827

23258

150284

Запрос: 1 слово

16,14

16,81

69,89

16,04

6,73

Запрос: 3 слова

4,07

0,95

2,61

224,36

11,57

Запрос: 3 слова "ИЛИ"

20,69

45,86

2,48

-

-

Запрос: точная фраза

3,16

0,64

9,85

-

-

Запрос: 1 слово, автокомплит

7,76

36,98

9,22

6,75

6,18

Запрос с опечаткой

19,81

58,17

-

14,61

5,84

Выводы

Elasticsearch по-прежнему остается королем поиска, он выдает стабильную производительность и для индексирования, и для всех типов запросов.

RediSearch имеет посредственную производительность индексации; также RedisLabs изо всех сил стараются продать свое облачное решение, поэтому документация не на высоте. Но эта система показывает минимальную задержку (менее миллисекунды) для некоторых типов запросов.

PostgreSQL показал странную "яму" производительности для простых запросов из одного слова, и интерфейс поиска довольно сложен. Хотя, если у вас уже есть база данных Postgres, встроенный поиск может быть неплохим решением для простых случаев.

У TypeSense неплохой набор функций, и производительность в целом тоже на высоте, за исключением странного провала при запросах из нескольких слов.

Кажущаяся высокая производительность MeiliSearch была вызвана ориентацией теста только на время ответа при индексировании, но Meili больше других полагается на асинхронную обработку. Мы не смогли провести тест с ожиданием индексации каждой порции данных, т.к. в таком режиме система оказалась мучительно медленной.

Дополнение: результаты Meilisearch и Typesense

Джейсон Боско из Typesense обратился к нам по поводу странных медленных выбросов с запросами из 3 слов и рекомендовал повторно запустить этот тест с параметром drop_tokens_threshold = 1, но мы получили в этом режиме похожие результаты (200+ мс). Мы также попробовали drop_tokens_threshold = 0 (по сути, превратив запрос в "ИЛИ"), это дало более высокую производительность.

То есть, по-видимому, замедление вызвано тем, что мы выбираем 3 случайных английских слова для запроса, и при отсутствии в индексе документов, содержащих все три слова Typesense начинает отбрасывать слова, пока не получает результат, и этот процесс не очень быстр.

Джейсон также отметил, что кажущаяся быстрая индексация Meilisearch на самом деле была вызвана асинхронностью запросов индексации. Мы обновили тест, чтобы он дожидался завершения индексирования каждой порции, но в таком режиме индексация занимает как-то невероятно много времени, так что, видимо, нам нужно повнимательнее изучить, как Meilisearch работает под капотом.

Дополнение для Хабра :)

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

Sonic

Ужасно медленная индексация. Не дождался окончания теста, хоть он и работал всю ночь.

Toshi

Индексация: 273 док/сек (настройки по умолчанию), 37631 док/сек с использованием недокументированного эндпоинта _bulk для пакетной работы

Запрос 1 слово: 372 з/сек, 2.68 мс
Запрос 3 слова: 357 з/сек, 2.79 мс
Запрос 3 слова с опечатками: 325 з/сек, 3.07 мс

Неплохие в целом показатели, но для индексации обязательно нужно использовать _bulk.

Quickwit

Индексация (из отдельно подготовленного файла с ndjson): 37085 док/сек

Запрос 1 слово: 324 з/сек, 3.08 мс
Запрос 3 слова: 324 з/сек, 3.08 мс ¯_(ツ)_/¯

Неплохие показатели, но для индексации нужно заранее готовить данные.

Groonga

Индексация: 77372 док/сек

Запрос 1 слово: 0.56 з/сек, 1782 мс
Запрос 3 слова: 0.50 з/сек, 1982 мс

Очень быстрая индексация, но ОЧЕНЬ медленный поиск.

Дополнение 2

Reindexer:

Индексация - 30664 док/сек (правда, асинхронно)

Запрос

QPS

avg

50%%

90%%

99%%

100%%

1 слово

59.08

16.93 ms

14.04 ms

31.24 ms

63.49 ms

246.68 ms

3 слова

41.74

23.96 ms

21.51 ms

38.55 ms

73.75 ms

152.33 ms

Документация выстроена неочевидно для человека со стороны и сильно "заточена" под использование как библиотеки для golang. Также в процессе "поймал" баг, когда невалидный utf-8 на входе прекрасно индексируется, но валится при поиске.

Также, это in-memory storage, т.е. объем данный жестко ограничен размером памяти (на моем тесте процесс потреблял около 9 Гб).

Manticore:

Индексация - 4481 док/сек

Запрос

QPS

avg

50%%

90%%

99%%

100%%

1 слово

128.41

7.79 ms

6.52 ms

10.12 ms

23.86 ms

334.15 ms

3 слова И

313.55

3.19 ms

2.76 ms

3.76 ms

8.36 ms

84.96 ms

3 слова ИЛИ

73.05

13.69 ms

9.94 ms

17.78 ms

106.00 ms

274.99 ms

Точная фраза

318.31

3.14 ms

2.48 ms

3.25 ms

7.15 ms

90.85 ms

Неплохие показатели по поиску, но скорость индексации оставляет желать лучшего.

Комментарии (18)


  1. Nnnnoooo
    04.10.2021 14:55
    +3

    А почему в тесте нет сфинкса/мантикоры? Довольно часто ведь используется


    1. ilvar Автор
      04.10.2021 15:28

      О, про мантикору не знал. Попробую!


      1. Nnnnoooo
        04.10.2021 17:58

        ну это форк сфинкса — во многом будет ± как сфинкс


  1. Zeiram
    04.10.2021 15:00
    -1

    ... и Яндекс.Сервера тоже нет. Пусть и не поддерживается он уже давно, но местами всё ещё используется.


    1. ilvar Автор
      04.10.2021 15:29

      Докеризованный есть где-нибудь? Не смог нагуглить


      1. Zeiram
        04.10.2021 21:22

        К сожалению он не доступен в докеризированном виде. Но я могу загрузить эти пакеты на файлообменник и сбросить вам ссылку, если пожелаете.
        Yandex_Server-2010.9.0-Linux-i686.deb
        Yandex_Server-2010.9.0-Linux-i686.rpm
        Yandex_Server-2010.9.0-Linux-x86_64.deb
        Yandex_Server-2010.9.0-Linux-x86_64.rpm
        Yandex_Server-2010.9.0-Windows-i386.exe
        Yandex_Server-2010.9.0-Windows-x86_64.exe


        1. ilvar Автор
          05.10.2021 21:29

          Yandex_Server-2010.9.0-Linux-x86_64.deb хватит. Спасибо!


    1. ilvar Автор
      12.10.2021 15:58

      Сюда из лички скопирую, вдруг кому пригодится:

      Посмотрел, в итоге решил не ковырять глубоко: софт давно не поддерживается, лицензия распространять не разрешает, и какого-то внешнего API, который бы подошел для докеризации, я не нашел.


  1. Layan
    04.10.2021 18:13

    PostgreSQL, насколько помню, позволяет делать индексацию/поиск на русском.
    Вот нашел на хабре статью, где человек с русским индексом работает.


    1. ilvar Автор
      04.10.2021 19:35

      Да, добавил русский.


  1. shybovycha
    05.10.2021 00:53
    +1

    Я всегда считал что эффективность в случае поисковых движков должна быть двух-компонентной: перформанс собственно хранения, индексирования и выборки данных (скажем "техническая" производительность) и качество результатов поиска ("софтовая" производительность - как быстро человек может найти искомый ресурс).

    И если мне не изменяет память, что Lucene, что Sphinx (который раньше на Хабре был) хорошо работают с одним поисковым термом. Но когда дело доходит до фраз - хоть выборка происходит моментально, субъективно результаты преимущественно бесполезные.

    Любопытно, почему насколько редко люди делятся как раз таки второй метрикой.


    1. ilvar Автор
      05.10.2021 21:28

      Вы правильно заметили, что вторая метрика - субъективна отчасти. Собственно, я неоднократно слышал от коллег недовольство современными поисковиками, которые "додумывают" за пользователя, что же он имел в виду, а для кейса "тупо найти максимально близкое совпадение" нужно использовать язык запросов, да и на тот иной раз система плюёт. Поэтому я, например, пользуюсь Searx с пачкой кастомных "колдунщиков", но я также понимаю, что мой кейс - очень маргинальный.

      Т.е. чтобы сделать такую метрику, нужно сначала составить несколько сот эталонных пар "запрос - результаты", причем, для нескольких для разных "персон". По-моему, это тема для немаленького исследовательского проекта.

      З.Ы. Кстати, тот же Elasticsearch, хоть и работает поверх Lucene, но даёт гораздо лучшие результаты. Если правильно составить запрос, конечно же :)


  1. beho1der
    05.10.2021 03:59

    Очень интересно было бы еще увидеть в сравнении https://github.com/Restream/reindexer


    1. ilvar Автор
      05.10.2021 21:29

      Интересное, спасибо! Попробую его запустить.


  1. galaxy
    06.10.2021 17:35

    Странная статья. Где описание методологии? Сделали непонятно что, получили какие-то результаты - толку от них кому-то?

    (ниже акцент на Postgres, т.к. с ним я хорошо знаком, но сами вопросы, думаю, актуальны для более-менее всех движков)

    1. Версии ПО? (ссылка на postgres fts в англ версии ведет на доки от 9.5, что наводит на подозрения)

    2. Как конфигурировали? Конкретно по postgres: основные параметры postgresql.conf? Какого типа индексы (gist/gin)? Конфигурация FTS?
      (вообще, некоторые утверждения ваши вызывают недоумение, например, про поддержку языков: вы точно отличаете языки от кодировок? Вы же дочитали в документации postgres до раздела про словари FTS?)

    3. Запросы - код, плз. Какие именно ключевые слова использовались и в каком количестве? Надеюсь, хотя бы одинаковые для всех движков? Запросы выдают хотя бы примерно одинаковый результат? Ранжирование было?

    4. Техническая достоверность: БД в память помещается? прогревали кеш? запросы one term / 3 term OR - с одними ключевиками (очень большие сомнения конкретно для postgres вызывают именно эти результаты - хотелось бы видеть конкретику)?

    5. Статистическая достоверность - сколько раз прогоняли? дисперсия какая?

    6. Оценка результатов поиска (выше уже про качество писали, но хотя бы количественно - если один нашел 100 документов, а другой 10000 по одному и тому же запросу - сравнивать как-то некорректно).


    1. ilvar Автор
      06.10.2021 20:13

      Postgres - 13, Эластик - 7.14, Typesense - 0.21, meilisearch - 0.21, redisearch - 2.0.11

      Настройки по умолчанию у всех, индекс у постгрес - GIN.

      Для запросов слова рандомно выбирались из списка (в т.ч. потому, что задача была оценить только производительность, а не "качество" поиска).

      Данные помещаются в память, да. Кэш не грели, слова для запросов каждый раз брали рандомом, обычно по 1000 запросов (если они работали слишком медленно - уменьшали до 100).

      Для показа юзеру что 1000, что 100000 документов это одинаковое количество - "слишком много", поэтому для оценки качества нужны таки промаркированные запросы.

      В целом, к Postgres у меня одна главная претензия в рамках этого теста: его нужно "уметь готовить", когда тот же Elastic в принципе выдаст достойные результаты без конфигурирования вовсе.


      1. galaxy
        07.10.2021 02:12

        Для показа юзеру что 1000, что 100000 документов это одинаковое количество - "слишком много", поэтому для оценки качества нужны таки промаркированные запросы.

        Лимиты делали?

        Кэш не грели, слова для запросов каждый раз брали рандомом, обычно по 1000 запросов (если они работали слишком медленно - уменьшали до 100)

        можно все-таки дисперсию или min-max время увидеть?

        И попробуйте на каком-то одном списке тестировать все движки (а то мало ли, вдруг кто-то не стал парсить xml и названия тегов стали словами, причем супер частыми, и такое слово попалось в запросе).


        1. ilvar Автор
          12.10.2021 15:43

          Достал персентили для Постгреса (с лимитом в 1000, чтобы было больше похоже на поведение Эластика):

          1 слово: среднее 10.86 ms, медиана 5.25 ms, 90% 14.61 ms, 99% 172.23 ms, макс 309.85 ms

          3 слова: среднее 1.89 ms, медиана 1.09 ms, 90% 2.99 ms, 99% 9.70 ms, макс 244.82 ms

          В целом терпимо.