aea0ddb9d478e4c7342074cbea8ef897.ppt
- Количество слайдов: 44
Загадки Sphinx-а Анатомический атлас поискового движка
Кто вы такие? • Sphinx – полнотекстовый поисковик
Кто вы такие? • Sphinx – полнотекстовый поисковик • Умеет копать
Кто вы такие? • Sphinx – полнотекстовый поисковик • Умеет копать • Умеет не копать
Кто вы такие? • • Sphinx – полнотекстовый поисковик Умеет копать Умеет не копать Умеет передать лопату соседу
Кто вы такие? • • • Sphinx – полнотекстовый поисковик Умеет копать Умеет не копать Умеет передать лопату соседу Умеет много гитик – «фасеточный» поиск, геопоиск, создание сниппетов, мультизапросы, IO throttling, и еще 10 -20 других интересных директив
Зачем вас позвали? • Чего не будет в докладе? – Вводного обзора «что это и зачем мне» – Длинных цитат из документации – Подробного разбора C++ архитектуры
Зачем вас позвали? • Чего не будет в докладе? – Вводного обзора «что это и зачем мне» – Длинных цитат из документации – Подробного разбора C++ архитектуры • Что будет? – Как оно в целом устроено внутри – Как можно оптимизировать разное – Как можно распараллеливать разное
Глава 1. Потроха движка
Вспомнить workflow • Сначала индексируем • Потом ищем
Вспомнить workflow • Сначала индексируем • Потом ищем • Есть источники данных (откуда и что брать) • Есть индексы – Какие источники брать – Как обрабатывать текст – Куда складывать результат
Как работает индексация • В двух актах, с антрактом • Фаза 1 – сбор документов – Получаем документы (цикл по источникам) – Разбиваем каждый документ на слова – Обрабатываем слова (морфология, *фиксы) – Заменяем слова на wordid (CRC 32/64) – Пишем ряд временных файлов
Как работает индексация • Фаза 2 – сортировка хитов – – Хит (hit, вхождение) – запись (docid, wordpos) На входе частично сортированные (по wordid) данные Сортируем слиянием списки хитов На выходе полностью сортированные данные • Интермеццо – Собираем и сортируем значения MVA – Сортируем ordinals – Сортируем extern атрибуты
Тупой и еще тупее • Формат индекса на выходе… несложен • Несколько сортированных массивов – Словарь (список wordid) – Атрибуты (только если docinfo=extern) – Список документов (для каждого слова) – Список позиций (для каждого слова) • Всё уложено линейно, хорошо для IO
Как работает поиск • Для каждого локального индекса – Строим список кандидатов (документов, удовлетворяющих запросу) – Фильтруем (аналог – WHERE) – Ранжируем (считаем веса документов) – Сортируем (аналог – ORDER BY) – Группируем (аналог – GROUP BY) • Склеиваем результаты по всем индексам
1. Цена поиска • Построение списка кандидатов – 1 ключевое слово = 1+ IO (список документов) – Булевы операции над списками документов – Стоимость пропорциональна (~) длине списков – То есть, сумме частот всех ключевых слов – При поиске фраз итп, еще и операции над списками позиций слов – примерно 2 x IO/CPU
1. Цена поиска • Построение списка кандидатов – 1 ключевое слово = 1+ IO (список документов) – Булевы операции над списками документов – Стоимость пропорциональна (~) длине списков – То есть, сумме частот всех ключевых слов – При поиске фраз итп, еще и операции над списками позиций слов – примерно 2 x IO/CPU • Мораль – “The Who” – очень плохая музыка
2. Цена фильтрации • docinfo=inline – Атрибуты хранятся в списке документов – ВСЕ значения дублируются МНОГО раз! – Доступны сразу после чтения с диска • docinfo=extern – Атрибуты хранятся в отдельном списке (файле) – Полностью кэшируются в RAM – Хэш по docid + бинарный поиск • Перебор фильтров • Cтоимость ~ числу кандидатов и фильтров
3. Цена ранжирования • Прямая – зависит от ranker-а – Учитывать позиции слов – • Полезно для релевантности • Но стоит ресурсов – двойной удар! • Стоимость ~ числу результатов • Самый дорогой – phrase proximity + BM 25 • Самый дешевый – none (weight=1) • Косвенная – может наводиться в сортировке
4. Цена сортировки • Стоимость ~ числу результатов • Еще зависит от критерия сортировки (документы придут в порядке @id asc) • Еще зависит от max_matches • Чем больше max, тем хуже серверу • 1 -10 K приемлемо, 100 K перебор • 10 -20 недобор
5. Цена группировки • • Группировка внутри – подвид сортировки Тоже число результатов Тоже max_matches Вдобавок, от max_matches зависит точность @count и @distinct
Глава 2. Оптимизируем разное
Как оптимизировать запросы • Разбиение данных (partitioning) • • Режимы ранжирования и сортировки Фильтры против ключевых слов Фильтры и MTF вручную Мультизапросы (multi queries)
Как оптимизировать запросы • Разбиение данных (partitioning) • • Режимы ранжирования и сортировки Фильтры против ключевых слов Фильтры и MTF вручную Мультизапросы (multi queries) • Последняя линия защиты – Три Большие Кнопки
1. Разбиение данных • Чисто швейцарский нож, для разных задач • Уперлось в переиндексацию? – Разбиваем, переиндексируем только изменения • Уперлось в фильтрацию? – Разбиваем, ищем только по нужным индексам • Уперлось в CPU/HDD? – Разбиваем, разносим по разным cores/HDDs/boxes
1 a. Разбиение под индексацию • • • Необходимо держать баланс Не добьешь – будет тормозить индексация Перебьешь – будет тормозить поиск 1 -10 индексов – работают разумно Некоторых устраивает и 50+ (30+24. . . ) Некоторых устраивает и 2000+ (!!!)
1 b. Разбиение под фильтрацию • Полностью, на 100% зависит от статистики боевых запросов – Анализируйте свои личные боевые логи – Добавляйте комментарии (3 й параметр Query()) • Оправдано только при существенном уменьшении обрабатываемых данных – Для документов за последнюю неделю – да – Для англоязычных запросов – нет (!)
1 c. Разбиение под CPU/HDD • Распределенный индекс, куски явно дробим по физическим устройствам • Прицеливаем searchd “сам на себя” – index dist 1 { type = distributed local = chunk 01 agent = localhost: 3312: chunk 02 agent = localhost: 3312: chunk 03 agent = localhost: 3312: chunk 04 }
1 c. Как искать CPU/HDD заторы • Три стандартных средства – vmstat – чем и насколько занят CPU? – oprofile – кем конкретно занят CPU? – iostat – насколько занят HDD? • • Плюс логи, плюс опция searchd --iostats Обычно все наглядно (us/sy/bi/bo…), но! Ловушка – HDD может упираться в iops Ловушка – CPU может прятаться в sy
2. Ранжирование • Теперь бывает разное (т. н. ранкеры в режиме поиска extended 2) • Ранкер по умолчанию – phrase+BM 25, анализирует позиции слов – не бесплатно • Иногда достаточно более простого • Иногда @weight игнорируется совсем (ищем ipod, сортируем по цене) • Иногда можно сэкономить
3. Фильтры против спецслов • Известный трюк – При индексации, добавляем специальное ключевое слово в документ (_authorid 123) – При поиске, добавляем его в запрос • Понятный вопрос – Что быстрее, как лучше? • Нехитрый ответ – Считайте ценник, не отходя от кассы
3. Фильтры против спецслов • Цена булева поиска ~ частотам слов • Цена фильтрации ~ числу кандидатов • Поиск – CPU+IO, фильтр – только CPU • Частота спец-слова = селективности значения фильтра • Частое значение + мало кандидатов → плохо! • Редкое значение + много кандидатов → хорошо!
4. Фильтры и MTF вручную • Фильтры перебираются последовательно • В порядке, указанном приложением! • Самый жесткий – лучше ставить в начале • Самый нестрогий – лучше ставить в конце • При замене спец-словами – не важно • Домашняя работа – а почему?
5. Мультизапросы • Любые запросы можно передать пачкой • Всегда экономит network roundtrip • Иногда может сработать оптимизатор • Особо важный и нужный случай – разные режимы сортировки-группировки • 2 x+ оптимизация “фасеточного” поиска
5. Мультизапросы $client = new Sphinx. Client (); $q = “laptop”; // coming from website user $client->Set. Sort. Mode ( SPH_SORT_EXTENDED, “@weight desc”); $client->Add. Query ( $q, “products” ); $client->Set. Group. By ( SPH_GROUPBY_ATTR, “vendor_id” ); $client->Add. Query ( $q, “products” ); $client->Reset. Group. By (); $client->Set. Sort. Mode ( SPH_SORT_EXTENDED, “price asc” ); $client->Set. Limit ( 0, 10 ); $result = $client->Run. Queries ();
6. Три Большие Кнопки • Если ничто другое не помогает… • Cutoff (см. Set. Limits()) – Останов поиска после N первых совпадений – В каждом индексе, не суммарно • Max. Query. Time (см. Set. Max. Query. Time()) – Останов поиска после M миллисекунд – В каждом индексе, не суммарно
6. Три Большие Кнопки • Если ничто другое не помогает… • Consulting – Можем заметить незамеченное – Можем дописать недописанное
Глава 3. Пример распараллеливания
Боевая задача • Есть ~160 M кросс-ссылок • Нужен ряд отчетов (по доменам→groupby) ************** 1. row ************** domain_id: 440682 link_id: 15 url_from: http: //www. insidegamer. nl/forum/viewtopic. php? t=40750 url_to: http: //xbox 360 achievements. org/content/view/101/114/ anchor: NULL from_site_id: 9835 from_forum_id: 1818 from_author_id: 282 from_message_id: 2586 message_published: 2006 -09 -30 00: 00. . .
Решаем – раз • Дробим данные • 8 машин, 4 x CPU, ~5 M ссылок на CPU • Используем Sphinx • Теоретически можно My. SQL • Практически сложно – Выйдет 15 -20 M+ rows/CPU – Агрегацию придется делать вручную
Решаем – два • Выделяем “интересные части” URL при индексации UDF-кой • Выборку подменяем текстовым запросом ************** 1. row ************** url_from: http: //www. insidegamer. nl/forum/viewtopic. php? t=40750 urlize(url_from, 0): www$insidegamer$nl$forum insidegamer$nl$forum$viewtopic. php$t=40750 urlize(url_from, 1): www$insidegamer$nl$forum insidegamer$nl$forum$viewtopic. php
Решаем – три • 64 индекса – 4 копии searchd на сервер, по числу CPU/HDD – 2 индекса (main+delta) на CPU • Обыскиваются параллельно – Web box спрашивает главную копию searchd – Главная спрашивает себя и остальные 3 копии – Используем 4 копии, потому что startup/update – Используем plain HDD, потому что IO stepping
Результаты • Точность приемлема • “Редкие” домены – без ошибок • “Частые” домены – в пределах 0. 5% • • Среднее время запроса – 0. 125 sec 90% запросов – менее 0. 227 sec 95% запросов – менее 0. 352 sec 99% запросов – менее 2. 888 sec
Конец


