Pull to refresh

А вот про Sphinx 3.0

Reading time 6 min
Views 37K
Вот вы там все сидите и ничего не знаете, а мы, тем временем, пилим помаленьку мега-релиз поискового движка Sphinx за номером 3.0. Грядет ряд больших переделок. Часть из них, как полагается, ещё даже как следует не начата. Однако большая часть уже скорее готова, чем нет. А отдельно взятые изменения даже протекли в публичную ветку 2.3. Так что, пожалуй, настало время вкратце начинать рассказывать, чего ожидать в светлом будущем: надеюсь, не столь отдалённом. Кому интересно почитать, все под кат; кому послушать, приходите на meetup в эту субботу. Если совсем вкратце, то: прощай, концепция дополняющего основную базу движка; привет, хранилище документов, тотальный RT, репликация, REST и ряд других известных ключевых слов.

Начнем собственно с главного, короткий список запланированных больших изменений:
  • новый формат FT индекса: быстрее индексация, быстрее поиск, меньше размер;
  • новый формат атрибутов: прощайте, все дурацкие исторические ограничения;
  • полный переход на треды: прощай, директива workers, привет, thread pool;
  • полный переход на RT индексы: причем indexer остается, а вот директива type не очень;
  • онлайн репликация RT индексов: постепенно заменяем адский ручной кластер на автоматический;
  • хранилка оригиналов документов: прощай, концепция auxiliary index, привет, мир, теперь мы тоже как бы база;
  • нативные индексы атрибутов: эмуляция ключевиками кое-как работает, а вот индексами возможно пользоваться;
  • REST интерфейс: API для ботов, SQL для себя, REST для людей.


Два больших внутренних изменения это, конечно, со всех сторон (и в полнотекстовой и атрибутивной части) новый формат индекса, плюс форсированный переход на строго тредовую модель работы и строго RT индексы. Отчего и зачем это все?

Старый формат FT индекса концептуально тянется, страшно подумать, аж с 2001 года. Не, в нём, разумеется, регулярно делались некие изменения и оптимизации, поэтому старичок ещё бодрячком. Однако радикальных, концептуальных изменений не было, натурально, вот прямо с 2001 года. Как хранили пачку varint закодированных дельт внешних docid, так и храним. Причем, будучи довольно низкоуровневым кирпичиком, формат индекса влияет не только лишь на собственно хранение данных, а затрагивает и ещё ряд штук во внутренней архитектуре, которые потом протекают и наружу: требование обязательно предоставлять внешний DocID, головные боли с дубликатами документов, ряд странных моментов в производительности, вот это вот всё.

Сегодня мы можем сделать и получше.

В новом формате нету «магического» внешнего уникального атрибута DocID, внутренняя нумерация документов отвязана от внешней. Одно это изменение делает возможным кучу штук: внезапно, в индекс можно заливать дубликаты ID, или вообще не требовать никакого числового ID; внезапно, при необходимости для подмножества документов индекса можно строить компакетные битмаски, а не здоровенные списки ID; внезапно, собственно данные полнотекстового индекса возможно куда эффективнее сжимать всякими групповыми кодеками типа PFD, Group Varint и прочая; внезапно, при индексации не нужен занудный этап сортировки, индекс можно строить практически инкрементально; и так далее. Индекс становится до 1.5 раз меньше, строится в 2-3 раза быстрее и, теоретически, искать по нему тоже можно до 2-3 раз быстрее, по меньшей мере, на отдельных запросах. Профит со всех сторон.

Последнее провоцирует следующую мелкую революцию. Раз новый инкрементальный алгоритм индексации (который, грубо говоря, «дописывает» по 1 записи к существующему недоиндексу в памяти) без затей оказался в 2-3 раза быстрее существующей сейчас пакетной индексации дисковых индексов, то — зачем вообще различать дисковые индексы и RT индексы? Отлично, долой дисковые индексы, долой директиву type. Раз теперь мы можем все индексы сделать RT, причем без потери производительности, скорее даже с ускорением, значит, надо это сделать. (Внутри при этом все равно остаются disk/RAM based варианты реализации отдельных сегментов индекса, конечно. Диск и память таки отличаются по ТТХ и не помнить об этом нельзя. Однако это уже вчистую головная боль «как сделать эффективно» для разработчиков, а заметных снаружи функциональных отличий для пользователей быть не должно.)

Что интересно, при этом утилита indexer, в принципе, никуда не пропадает. ETL инструмент для выгрузки данных из базы и загрузки их в поиск все равно иметь полезно. Однако раньше оно строило дисковый индекс, а теперь будет строить RT индекс (либо для прямой загрузки в демона, либо для «приклеивания» свежепроиндексированного пакета данных к существующему индексу, это уже не суть важно).

Раз уж всё равно сильно меняется формат полнотекстовой части, то заодно надо сменить и формат хранения атрибутов, гулять так гулять (ломать совместимость, так ломать)!!! Теперь все колонки переменной длины (строки, MVA, JSON и любые другие хитрые типа, если вдруг в будущем таковые будут таки добавляться) для одного документа складируются в один большой блоб, и указатель на этот блоб хранится тоже один. (А раньше на каждый тип был отдельный файл, а колонку отдельный указатель, охх.) При этом заодно изжита глупая ошибка молодости «4 GB should be enough for everybody», плюс все можно унифицированно обновлять, удалять и так далее. Итого атрибуты фиксированной длины хранятся как и раньше (вот только за счет перехода на внутренние номера доступ к ним много быстрее), атрибуты переменной длины жрут несколько меньше памяти (8 байт на все сразу, а не 4 байта на каждый), плюс теперь никак не ограничены по размеру. Плюс прочие мелкие приятные улучшения: например, формат теперь поддерживает честный NULL. Или вот внутри JSON данных теперь делается компрессия ключей.

Переход на RT в свою очередь означает, что fork/prefork становится дальше поддерживать не просто тяжело, а невозможно. Впрочем, здесь в кои веки есть решение «и быстро и хорошо», без этих вот проклятых компромиссов: thread pool (уже доступный в 2.3) работает максимально бодро с точки зрения скорости поисков (см. быстро), принципиальная модель параллельной обработки остается как бы одна, многопоточная, а не многопроцессная (см. хорошо). Если бы ещё и креши умудрялись затрагивать только один тред, было бы вообще идеально, но идеально в жизни не бывает. Поэтому для облегчения болей от креша теперь подавляющая часть нужных данных на старте таки mmap()ится, а не копируется, за счет чего запуск (и перезапуск) даже с большими индексами стал довольно резв.

Вдобавок к тредам из-за форсирования RT становится совсем-совсем нужна репликация. Раз декларируем отказ от дисковых индексов, которые хоть как-то можно было копировать в виде файлов туда-сюда, значит, надо хоть какие-то инструменты для RT. Можно, конечно, делать горячий backup/restore, но это скучно. Онлайн репликацию RT индексов сделать куда интереснее. Что же, делаем. 1 мастер, N динамических реплик, автоматический переброс начального снапшота, репликация влетающих изменений, перевыбора мастера, все эти дела. Следующим шагом всякие инструменты для построения и менеджмента кластера, но сначала пусть репликация совсем хорошо заживет.

Реплицировать «просто полнотекстовые индексы» как-то мелко, плюс давать сохранить оригиналы документов ведь регулярно просят, плюс для дополнительного ускорения сниппетов необходимо уметь сохранять блоб со всяким странным фаршем, плюс отвязанный от docid формат индекса позволяет всякое ловко реализовать. Что же, делаем ещё и docstore, те. дисковое хранилище не только атрибутов, но и полей индексируемых документов, а также всякой привязанной метаинформации (пока что это только document level индексы для сниппетов). Маленький шажок для кода, большой для функционала: теперь в Сфинкс можно складывать не только лишь полнотекстовый индекс, а таки полностью использовать как базу. Странноватую, но базу.

В базе неплохо бы иметь индексы не только по ключевым словам, поэтому заодно надо озаботится индексами по атрибутам. Ну, это чтобы WHERE MATCH('the who') AND author=1234 исполнялось «от индекса», чтобы WHERE MATCH('biggest rarity') AND gender='m' исполнялось таки «от поиска», но и то и другое автоматически исполнялось оптимально, а не так, что в вызывающем PHP скрипте надо реализовать аналог SQL оптимизатора и генератор запросов типа WHERE MATCH('the who _author1234'). Тупо проэмулировать атрибуты облачком ключевых слов, как вроде (вроде) до сих пор принято известно где, тоже, конечно, можно, но если уже делать, так по большому!!!

И, наконец, SQL это немодно, плюс не отовсюду удобно вызывать, поэтому надо добавлять HTTP/REST доступ. Совсем базовая начальная реализация уже есть в 2.3, оттуда будем расширять и углублять. Ничего интересного, даже скучно.

Вот со всем этим Sphinx 3.0 и попытается взлететь.

Ряд описанного, конечно, всё ещё в стадии разработки и в начальные альфы может и не войти. Однако общий план именно такой, плюс, скажем так, больше половины этого плана уже таки сделано. Работы осталось изрядно, но поворачивать назад уже поздно!!!

Понятно, что по каждому отдельному большому пункту можно писать отдельную изрядную статью с кучей технических деталей, но надо с чего-то начинать. Вот, я попытался сделать небольшой обзор грядущих изменений. Чуть подробнее все то же самое буду рассказывать через 3 дня, в эту субботу на встрече Sphinx-оводов (понятное дело, Москва), у кого сразу возник миллион уточняющих вопросов и нету никакой возможности писать их все в комментарии, заходите на огонёк, попробую ответить. По мере возможностей буду писать сюда (и-или в блог на сайте) ещё статей про грядущие мегафичи.

В общем и целом, грядет Sphinx 3.0, грядет куча переделок, думаю, будет интересно.

Ведь не может же в мире существовать ровно ОДНА библиотека для полнотекстового поиска, да и та на богомерзкой Java?!!! :-)
Tags:
Hubs:
+70
Comments 54
Comments Comments 54

Articles