2. Одноклассники в цифрах
• Что у нас есть:
– 185 млн аккаунтов;
– 7 млн групп;
– .....
• 5.5 млн пользователей онлайн;
• В секунду:
– 250 000 страниц, 260 000 фото, 150 Гбит;
– 8 000 сообщений и комментариев;
– 3 000 поисковых запросов.
1
3. Задачи поисковой системы
Видео
Музыка
Группы
Пользователи Подарки
групп
Пользователи
Помощь Сообщества
Интересы Мероприятия
Города
2
4. Выбор нового решения
• У нас уже работал поиск пользователей на MS SQL,
что упростило определение технических
требований.
• Нужен был OpenSource-проект, написанный на
Java.
• Тестировали Solr, но он нас совсем не устроил.
• Используя Solr, провели необходимые
эксперименты с Lucene.
• Прототип на Lucene превзошел ожидания.
3
5. Как устроен Lucene?
The bright Term DocId DocId Values
blue blue 1,2 1 333, Author A
Index Reader & Searcher & Query parser
butterfly bright 1,2 2 777, Author C
hangs on
Tokenizers & Filters & IndexWriter
butterfly 1
the breeze
breeze 1
hangs 1
Under blue
sky, in bright need 2
sunlight, search 2
one need sky 2
not search
around
Term DocId DocId Values
It’s best to best 1 1 555, Author C
forget the forget 1
great sky
great 1
and to retire
from every retire 1
wind sky 1
wind 1
4
6. Что мы реализовали в Lucene за 3 года:
• собственную репликацию;
• хранение индексов в памяти;
• выполнение поиска на индексах;
• загрузку хранимых полей;
• новые виды запросов.
5
7. От MS SQL к Lucene
• На Indexer хранится база с данными для индекса.
• Indexer готовит индекс и рассылает изменения.
• Query-сервера исполняют запросы на индексе.
Search Presentation
Event
Cache Search processing Get Entity cache Services
Update
Query
Query service Replication Indexer service + DB
6
8. Эксплуатация первой версии
• Если вам что-то не нравится при нагрузочном
тестировании, лучше найдите причину
• Если что-то нужно, сделайте это регулярным
Search Presentation
Event
Cache Search processing Get Entity cache Services
Update
Query
Query service Replication Indexer service + DB
7
9. Мгновенный поиск и социальный граф
• Одновременный поиск по трём
большим индексам.
• Временные персональные
индексы, разделенные на: друзья,
друзья друзей, мои группы,
группы друзей и т.д.
• Первые выдачи из тулбара
полностью идут из персонального
индекса.
• Во многих разделах сайта есть
подсказки по друзьям,
работающие на персональном
индексе.
8
10. Семеро одного не ждут
• В персональный индекс
дольше всего собираются Get session for Schema
группы и сообщества.
• Быстрее всего собираются Schedule queries
друзья и друзья друзей. Execute queries
• Дольше всего идет поиск waitAll ()
waitFor (queries complete)
по пользователям. waitAtLeast (result items)
• Быстрее всего – по Reduce results
сообществам. Load results
11. Эффективность кэширования
• Кэшируются только 5% запросов.
• Попадание в кэш доходит до 60%.
• На топ 1000 запросов приходится < 2%.
Presentation
Search
Event
Cache Search processing
Get Get Entity cache
Services Services
Update
Query
Query service Replication Indexer service + DB
10
12. Кэширование и нагрузка
Cache Cache
*2 *2 *2
Service Service Service
Service 0-19 Service 20-39 Service 40-59 Service 60-79 Service 80-99
37
11
13. Разделять или совмещать?
• Пока систем и опыта мало, лучше разделять:
+ системы не влияют друг на друга;
+ проще тестировать и выкладывать.
• Когда однотипных систем становится много,
лучше начать их объединение:
+ проще следить за работой;
+ везде одна версия и настройки;
- каждый раз нужно тестировать все;
- сложнее решать возникающие проблемы.
12
14. Поиск пользователей группы
• Пользователи и состав групп находятся в разных сервисах.
• Размеры групп варируются от нескольких человек до
миллионов.
• Для заиндексированых групп применяются обновления.
• Маленькие группы «забываются» через час.
Сервисы
Пользователи портала
Основная
память
Поисковая Внешняя
Группы система Маленькие память
группы
13
15. Поиск пользователей онлайн
• В первой версии искали в индексе пользователей
+ легко запустить;
+ надежно работает;
– медленно работает;
– сложная логика.
• Сейчас ищем по отдельному индексу, в котором
только пользователи онлайн
+ быстро работает;
+ простая логика;
– более 200.000 изменений в минуту;
– система зависит от индексирующего сервера.
14
За 3 с половиной года мы прошли путь от поиска пользователей в MSSQL до поиска по дюжене индексов, и использования соц графа при выдаче результатов. Так же поиск неявно используется в работе некоторых разделов портала.Это основные виды данных которые мы индексируем. Кроме непосредственно поиска, ПС обеспечивает множество фич сайта, в том числе: всевозможные подсказки друзей, поиск среди сейчас на сайте, витрина мероприятий, расчет топов в музыке.
Поиск поMSSQL занимал более 10 секунд. НедавноSQLперестал искать по именам даже в админкеСмотерли только OpenSource Java чтобы была возможность вмешиваться в работу технологииСперва тестировали Solr,но остались недовольны всем, чем можно: ботелнэки, репликация на bash-скриптах, качество кода... Поняв, что Solrнегоден для продакшена, сосредоточились именно на поведении Luceneи остались довольны результатами. Тестов было много: нас интересовало изменение производительности с размером индекса, как меняется производительность с изменением числа апдейтов в индекс, как меняется время запросов. В тестах на 37млн аккаунтов и 1.5 GB с одного сервера на ?8ядер? смогли снять 370 запросов в секунду. На нашем прототипе Luceneпоказала производительность уже в 800 з/c.
Инвертированый индекс - это индексная структура, которая хранит не отдельные поля документов, а данные из этих полей: слова, цифры и т.д. Через такую структуру можно быстро находить документы, содержащие необходимые слова, все или часть из них.
Luceneактивно читает файлы во время поиска.Пробовали: HDD,RamDrive,LuceneRamDirectory, OffHeapDirectory. Выбрали HeapDirectоry,файлы как byte[] в хипеМедленная работа с хранимыми полямиПричина: при считывании хранимого поля создается много мусора и производятся ненужные операцииРешение: считывать значение в нужный тип сразу из byte[]Результат: на порядок быстрее стали операции с хранимыми полями; время GC упало в 2 раза
Первым запускали поиск пользователей. Данные которые мы хотели в него положить, нужно было собирать из разных баз – притом без возможности быстро собрать инфу на пользователя. Поэтому появилась промежуточная база рядом с Indexer. В случае с новыми пользователями, основная информация индексировалась сразу, и ставилась задача в очередь на доиндексацию. У Search DB есть Slave для резервирования.По результатам тестов решили сразу запускать 2 партиции.Почему выбрали Master + Slave такую схему, а не Preprocessor + (indexer + index) сервера? Так проще поддерживать ноды консистентными. Так же исключается проблема с разной производительностью нод, из-за разной структуры индексов. Кроме того Index ноды не занимаются постороенней работой. На первый взгляд у нашей схемы появляется ботелнек, но на самом деле в схеме с Preprocessor тоже есть сервер поломка которого ломает индексацию. Опять же, при Master+Slaveпроще сделать восстоновление после проблем и реиндексацию – она 99% времени аффектит только индексер. Хотя за Mster+slaveприходится платить чуть большим трафиком, но пока это проблем не создавало.
Мы провели тщательное нагрузочное тестирование сиситемы на реальных запросах в фоновом режиме. В нагрузочных тестах было видно что GC работает неравномерно, не то чтобы критично, но как то странно. Через пол года эта странность начала быстро прогрессировать, и разобраться на продакшен системе было уже гораздо тяжелее. В целом даже многократная нагрузка в НТ, не гарантирует отсутствие поблем в первые часы после запуска.Мы сделали регулярную реиндексацию, что быть уверенными что в любой момент сможем пересоздать индекс. Это может понадобиться к примеру чтобы помеять схему индексации. И не раз это нас предупреждало о том что мы начинаем терять эту возможность. При этом мы посчитали что регулярная синхронизация нам не нужна – у нас все надежно и ничего не будет теряться Еще была проблема в том, что выбраный механизм для наполнения индекса, не подходил для регулярного выполнения.Search Dbбыл нужен только для пользователей???Большой хип может работать быстро, если в нем мало объектов. Поэтому делая навороченное кэширование, легко проиграть в производительности – всю экономию съест GC.
Сессии хранятся offheap
Бытсро расло как количество данных, так и количество форматов в которых могут приходить запросы. Так же все больше было запросов использующих только часть сессии. Переход на схемы, чтобы снизить лишний трафик. Асинхроноое наполнение индекса – пока он наполняется, уже исполняются запросы в большие индексы. Запросы допускающие ответ раньше чем отработают все подзапросы.
Кэш результатов, да и промежуточный сервис, были лишними. Вполне можно было обойтись без них и сэкономить уйму времени. По крайней мере, в случае с поиском наша статистика указывает на низкую эффективность кэширования запросов. Особенно с ростом интерактивности интерфейса. Сделать кэширование в такой системе не просто, и, скорее всего, это потребует отдельных серверов, что усложняет систему и снижает ее надежность. Кроме того, время, потраченое нами на реализацию кэширования, можно было не тратить или потратить на оптимизацию исполняющей части.Мы применили ту же схему и для поиска групп и поиска сообществ, там лишними была еще и Search DB. В тот момент это было сделано из соображений приемствености и унификации, но это принесло больше проблем, чем пользы.
Вариант с ремоут-кэшами еще плох и тем, что он очень чувствителен к сбоям и перегрузке сети. Трафик запросов и загрузки данных гораздо ниже и рассредоточение.
Риски vsудобство. Разделенное тяжелее сломать целиком, но когда «похожих» систем много, возникает путаница, проблемы с деплойментом и поддержкой в рабочем состоянии на всех средах. Еще становится тяжело за всем уследить, и возникшие проблемы для не центральных сервисов могут быть пропущены. Объединение позволяет все централизировать, сэкономить время на поддержку сред и оптимизировать железо не создавая сложных деплойментов на серверах. Так же унифицированая система более устойчива к изменеению нагрузки, т.к. ее легче перебалансировать.