Слайд 1 - Sphinx

реклама
Sphinx в примерах и задачах
Андрей Аксенов
Sphinx Technologies
Что за… Sphinx?
• Sphinx – это полнотекстовый поисковик
Что за… Sphinx?
• Sphinx – это полнотекстовый поисковик
• Может копать
– Активный, ответственный работник
• Может не копать
– Внимателен к инициативам начальства
• Может лопату спрятать
– Отличный командный игрок
Что за… Sphinx?
• Может то, чего по слухам не может!
– «Полуживые» обновления индекса
– Фасеточный поиск
– Особенные, уличные SQL запросы
– Геопоиск
– Создание сниппетов
– Мультизапросы
– И еще 10-20-30 других интересных фичей
Где выход и когда обед?
• О чем вы НЕ узнаете из доклада?
– Зачем всё это
– Что написано в документации
– Что написано в исходниках
• О чем поговорим
– Как Sphinx устроен внутри
– Как оптимизировать разное
– Как куют highload
Общее устройство
• Нет… никакого… Сфинкса.
Общее устройство
• Есть две программы
– Indexer – строит индексы
– Searchd – отвечает на запросы
• Еще есть API
– Клиент, который умеет говорить с searchd
по сети
– PHP, PECL, Python, Perl, Java, Ruby, C99,
C++, Haskell, C#, MySQL SE…
Как работает indexer
• Есть источники данных
– Откуда брать (MySQL, PgSQL, xmlpipe)
– Что брать (sql_query, sql_attr_xxx)
– Как брать (sql_query_pre, sql_query_post)
• Есть физические индексы
– Как индексировать (токенизация, стемминг,
словоформы, HTML stripper)
– Куда класть файлы
Что хранится в индексе
• Индекс для полнотекстовых запросов
– Словарь
– Списки документов по ключевым словам
– Списки позиций по документам
• Привязанные атрибуты документов
– Integer (от 1 до 32 или 64 бит)
– Float
– MVA (сортированный список 32-битных целых)
• НЕ хранятся исходные текстовые данные
Как работает searchd
• Получает запрос, вычисляет ответ
• Умеет агрегировать ответы
– По нескольким физически индексам
– С ответами от удаленных searchd
• Еще умеет строить сниппеты
• Еще умеет обновлять атрибуты (иногда)
Как все перестает работать
Нагрузка ударяет в спину
• Виды проблем
– Bandwidth – слишком много запросов
– Latency – слишком долгий отклик
– Availability – слишком мало (работающих)
серверов
• Как бороться?
– Локально – как оптимизировать запросы
– Глобально – как обустраивать кластера
Оптимизируем запросы
• Как работает поиск
Как работает поиск
• Для каждого локального индекса
– Строим список кандидатов (документов,
удовлетворяющих запросу)
– Фильтруем (аналог – WHERE)
– Ранжируем (считаем веса документов)
– Сортируем (аналог – ORDER BY)
– Группируем (аналог – GROUP BY)
• Склеиваем результаты по всем
индексам
Цена булева поиска
• Построение списка кандидатов
–
–
–
–
–
1 ключевое слово = 1+ IO (список документов)
Булевы операции над списками документов
Стоимость пропорциональна (~) длине списков
То есть, сумме частот всех ключевых слов
При поиске фраз итп, еще и операции над
списками позиций слов – примерно 2x IO/CPU
• Мораль
– “The Who” – очень плохая музыка
– Гамлет – очень плохая литература
Цена фильтрации
• docinfo=inline
– Атрибуты хранятся в списке документов
– ВСЕ значения дублируются МНОГО раз!
– Доступны сразу после чтения с диска
• docinfo=extern
– Атрибуты хранятся в отдельном списке (файле)
– Полностью кэшируются в RAM
– Хэш по docid + бинарный поиск
• Перебор фильтров
• Cтоимость ~ числу кандидатов и фильтров
Цена ранжирования
• Прямая - зависит от ranker-а
– Учитывать позиции слов -
• Полезно для релевантности
• Но стоит ресурсов - двойной удар!
• Стоимость ~ числу результатов
• Самый дорогой - phrase proximity + BM25
• Самый дешевый - none (weight=1)
• Косвенная - может наводиться в сортировке
Цена сортировки
• Стоимость ~ числу результатов
• Еще зависит от критерия сортировки
(документы придут в порядке @id asc)
• Еще зависит от max_matches
• Чем больше max, тем хуже серверу
• 1-10K приемлемо, 100K перебор
• 10-20 недобор
Цена группировки
• Группировка внутри – особый подвид
сортировки
• Тоже число результатов
• Тоже max_matches
• Вдобавок, от max_matches зависит
точность @count и @distinct
(Некоторые) оптимизации
(Некоторые) оптимизации
• Режимы ранжирования и сортировки
• Фильтры против ключевых слов
• Мультизапросы (multi queries)
• Разбиение данных (partitioning)
• Последняя линия защиты –
•
Три Большие Кнопки
Ранжирование…
• Бывает разное, см. SetRankingMode()
• По умолчанию – phrase+BM25
– Анализирует позиции слов
– Что не бесплатно!
• Иногда достаточно более простого
• Иногда достаточно тривиального
– ищем ipod, сортируем по цене…
…и сортировка
• Можно упростить ранжирование
– Когда сортируем по цене, вес не интересен
• Можно вкомпилировать
– См. src/sphinxcustomsort.inl + @custom
• Можно оптимизировать
– Документы приходят в порядке @id asc
– @id asc => date asc – оптимально
– @id asc => date desc – можно поменять id
Фильтры против спецслов
• Известный трюк
– При индексации, добавляем специальное
ключевое слово в документ (_authorid123)
– При поиске, добавляем его в запрос
• Понятный вопрос
– Что быстрее, как лучше?
• Нехитрый ответ
– Считайте ценник, не отходя от кассы
Фильтры против спецслов
• Цена булева поиска ~ частотам слов
• Цена фильтрации ~ числу кандидатов
• Поиск – CPU+IO, фильтр – только CPU
• Частота спецслова
= селективности значения фильтра
• Частое знач-е + мало кандидатов → плохо!
• Редкое знач-е + много кандидатов → хорошо!
Мультизапросы
• Любые запросы можно передать пачкой
• Всегда экономит network roundtrip
• Иногда может сработать оптимизатор
• Особо важный и нужный случай –
разные режимы сортировки группировки
• 2x+ оптимизация “фасеточного” поиска
Мультизапросы
$client = new SphinxClient ();
$q = “laptop”; // coming from website user
$client->SetSortMode ( SPH_SORT_EXTENDED, “@weight desc”);
$client->AddQuery ( $q, “products” );
$client->SetGroupBy ( SPH_GROUPBY_ATTR, “vendor_id” );
$client->AddQuery ( $q, “products” );
$client->ResetGroupBy ();
$client->SetSortMode ( SPH_SORT_EXTENDED, “price asc” );
$client->SetLimit ( 0, 10 );
$result = $client->RunQueries ();
(Некоторые) оптимизации
• Режимы ранжирования и сортировки
• Фильтры против ключевых слов
• Мультизапросы (multi queries)
• Разбиение данных (partitioning)
• Последняя линия защиты –
•
Три Большие Кнопки
Partitioning
Partitioning
• Алгоритм решения боевых задач им. тов.
Цезаря
• Уперлось в переиндексацию?
– Разбиваем, переиндексируем только изменения
• Уперлось в фильтрацию?
– Разбиваем, ищем только по нужным индексам
• Уперлось в CPU/HDD?
– Разбиваем, разносим по разным cores/HDDs/boxes
Разбиение под индексацию
• Необходимо держать баланс
• Не добьешь – будет тормозить
индексация
• Перебьешь – будет тормозить поиск
• 1-10 индексов – работают разумно
• Некоторых устраивает и 50+ (30+24...)
• Некоторых устраивает и 2000+ (!!!)
Разбиение под фильтрацию
• Полностью, на 100% зависит от
статистики боевых запросов
– Анализируйте свои личные боевые логи
– Добавляйте комментарии (Query(), 3rd arg)
• Оправдано только при существенном
уменьшении обрабатываемых данных
– Для документов за последнюю неделю – да
– Для англоязычных запросов – нет (!)
Разбиение под CPU/HDD
• Распределенный индекс, куски явно
дробим по физическим устройствам
• Прицеливаем searchd “сам на себя” –
index dist1
{
type = distributed
local = chunk01
agent = localhost:3312:chunk02
agent = localhost:3312:chunk03
agent = localhost:3312:chunk04
}
Куем highload
Куем highload
•
•
•
•
Или «как нам обустроить кластер»
Всегда будет рост bandwidth
Всегда будет no-SPoF (те. «какой-то» HA)
Варианты строго зависят от требований
–
–
–
–
Допустимая задержка индексации?
Допустимая скорость запроса?
Время восстановления после hard crash?
Приемлема ли деградация результатов?
Методы борьбы
• Борем задержку индексации
– Дельта-индекс
– Каскады дельта-индексов
– Merge
– Partitioning на несколько машин
• Борем скорость запроса
– Оптимизируем локальные запросы
– Partitioning на несколько машин
Методы partitioning
• Линейная репликация
– Клонируем весь индекс много раз
– Опционально выносим индексацию
– Pro – легко поддерживать/наращивать
– Pro – автоматом HA (сплошные hotspare!)
– Pro – деградация невозможна
– Contra – растет только bandwidth
Методы partitioning
• Линейный или “деревянный” partitioning
– Разбиваем индекс на независимые куски
– Линейный – star topology
– Деревянный – star-of-stars topology
– Pro – легко поддерживать
– Pro – почти линейно падает latency
– Contra – переделки при наращивании
– Contra – нету HA, деградация при сбоях
Методы partitioning
• Комбинированные схемы
– Partitioning + репликация кусков
– Балансировка между конечными кусками –
внешним LB (пока?), на уровне TCP port
– Pro – решает “все” задачи
– Contra – сложнее всего разворачивать и
поддерживать
Военные хитрости
• Несколько копий searchd на сервер
– Улучшает время запуска
– Нужно, когда МНОГО атрибутов
• Raw HDD, а не RAID
– Явное разделение – лучше неявного
– Иначе – IO stepping для одного (!) запроса
• Вечный indexer
– Бесконечный цикл вместо crontab
Выбираем железо
• Большие коллекции упираются в IO
– Добивать HDD, причем числом!
– Добивать RAM
• Маленькие коллекции в CPU
• Три стандартных средства
– vmstat – чем и насколько занят CPU?
– oprofile – кем конкретно занят CPU?
– iostat – насколько занят HDD?
• Плюс логи, плюс опция searchd --iostats
Выбираем железо
• Анализируем результаты
– Обычно все наглядно (us/sy/bi/bo…), но!
– Ловушка – HDD может упираться в iops
– Ловушка – CPU может прятаться в sy
– Ловушка – «незаметные» проблемы в us
(Некоторые) оптимизации
• Режимы ранжирования и сортировки
• Фильтры против ключевых слов
• Мультизапросы (multi queries)
• Разбиение данных (partitioning)
• Последняя линия защиты –
•
Три Большие Кнопки
Три Большие Кнопки
• Если ничто другое не помогает…
• Cutoff (см. SetLimits())
– Останов поиска после N первых
совпадений
– В каждом индексе, не суммарно
• MaxQueryTime (см. SetMaxQueryTime())
– Останов поиска после M миллисекунд
– В каждом индексе, не суммарно
Три Большие Кнопки
• Если ничто другое не помогает…
• Consulting 
– Можем заметить незамеченное
– Можем дописать недописанное
Вопросы?
Скачать