Обзор модуляЧасть II · ~10 ч · Сложность: (продвинутый) · Пререквизиты: Модуль 1, Модуль 3
К этому моменту в сквозном конвейере «обход → индекс → факторы → ранжирование → выдача → постобработка → измерение» планировщик обхода (Модуль 2) уже добыл документы, а каноникализатор (Модуль 3) свёл дубли к одному представителю и присвоил каждому документу устойчивый идентификатор. Теперь наступает звено индексирования: как из миллиардов разобранных документов построить структуру данных, по которой за единицы миллисекунд можно найти все документы, содержащие слова запроса, и тут же достать признаки для их оценки.
Этот модуль — про офлайн-сторону шва между офлайн и онлайн. Индексатор работает в пакетном (batch) и потоковом (realtime) режимах: он строит обратный индекс, материализует в него предвычисленные признаки документов и хостов и раскладывает всё по сегментам, ярусам и шардам так, чтобы рантайм мог читать это с предсказуемой латентностью. Ключевая идея, к которой мы будем возвращаться: робот материализует статику в индекс, а рантайм читает её как факторы. Граница между «посчитано заранее» и «посчитано на запросе» — это не философия, а инженерное решение про латентность, стоимость и свежесть.
После модуля вы будете понимать устройство обратного индекса до уровня байтов постинг-листа, уметь оценивать стоимость сжатия и декодирования, проектировать разбиение документа на зоны и аннотации, объяснять, какие признаки имеет смысл предвычислять и хранить в индексе, а какие считать на лету, и проектировать топологию индекса (сегменты, ярусы, шарды) под заданный бюджет латентности и качества.
Модуль продолжает Часть II и опирается на Модуль 1 (модель IR, термин, документ, токенизация) и Модуль 3 (идентичность документа, docID). Дальше всё это будет читаться: текстовая релевантность (Модуль 6) обращается к постинг-листам и позициям, каскад ранжирования (Модуль 12) забирает статику как факторы, свежесть (Модуль 17) опирается на реалтайм-контур, а инфраструктура (Модуль 13) — на шардирование и сегменты.
Как читать по трекам
- Студент CS. Главы 4.1 и 4.4 — обязательно и подробно (структуры данных, сжатие, отбор кандидатов — основа всего, что дальше). Главы 4.2 и 4.3 — обязательно для понимания факторов. Глава 4.5 — обязательно концептуально (модель индекса как append-only-сегментов). Все лабы желательно сделать руками.
- Инженер поиска/ML. Весь модуль — это ваш «домашний» материал. Особое внимание: инженерные заметки про сжатие и латентность (4.1), квоты отбора и тиры (4.4), реалтайм-контур и видимость записей (4.5). Лабы делайте на реальных объёмах данных.
- SEO-специалист. Обязательно: SEO-врезки во всех главах, особенно про зоны документа (4.2: что весит больше — title, заголовки, якоря), про индексируемость и попадание в высокий ярус (4.4), про задержку индексации realtime vs batch (4.5). Вывод форматов сжатия и алгоритмов — обзорно.
- Смешанный/руководитель. Прочитайте Обзор, конспекты 4.1 и 4.4 по диагонали, целиком — раздел про шов офлайн/онлайн (4.3) и про realtime vs batch (4.5). Это объясняет, почему «новое» индексируется не мгновенно и почему изменение фактора в индексе стоит дорого.
- 4.1. Обратный индекс: постинг-листы, хиты, позиции, частоты, сжатие (varint, delta, PForDelta). (продвинутый)
- 4.2. Аннотации и зоны документа: title, тело, якорный текст, заголовки. (средний)
- 4.3. Документная и хостовая статика: предвычисленные признаки и шов офлайн/онлайн. (средний)
- 4.4. Сегментация, ярусы (тиры) индекса, квоты отбора, шардирование. (продвинутый)
- 4.5. Реалтайм-контур индекса vs батч-построение. (продвинутый)
Цели обучения
После главы студент сможет:
- объяснить, чем обратный индекс (inverted index) отличается от прямого (forward index) и почему поиск строится на обратном;
- описать строение постинг-листа (posting list): docID, частота, позиции, хиты;
- реализовать кодирование разностей (delta/gap encoding) поверх отсортированного списка docID;
- закодировать целые переменной длины (varint) и оценить число байт на постинг;
- объяснить идею пакетного кодирования (PForDelta) и компромисс «степень сжатия ↔ скорость декодирования»;
- оценить, как выбор кодека влияет на латентность чтения постинг-листа.
Зачем «обратный» индекс
Прямой индекс отвечает на вопрос «какие слова в документе d?» — это просто список термов документа. Поиск задаёт обратный вопрос: «в каких документах встречается слово t?». Структуру, которая по терму возвращает список документов, и называют обратным индексом.
Обратный индекс состоит из двух частей:Интуиция. Это предметный указатель в конце учебника. Вы не листаете все страницы в поисках слова «энтропия» — вы открываете указатель, находите строку энтропия → 17, 142, 143 и идёте сразу на нужные страницы. Обратный индекс — это такой указатель для всего корпуса.
- Словарь (lexicon / term dictionary). Отображение term → указатель на постинг-лист (плюс агрегаты: число документов с термом df, суммарная частота). Словарь должен быть компактным и быстро искаемым (хеш-таблица, отсортированный массив с бинарным поиском, либо префиксное дерево / FST для экономии памяти на общих префиксах).
- Постинги (postings). Для каждого терма — список вхождений в документы.
Код: Выделить всё
СЛОВАРЬ ПОСТИНГИ (постинг-листы)
┌────────────┬────────┐
│ term │ ptr,df │
├────────────┼────────┤
│ "альфа" │ •──────────► [d3:2 d7:1 d11:5 d12:1 ...]
│ "бета" │ •──────────► [d1:1 d3:1 d12:3 ...]
│ "гамма" │ •──────────► [d7:4 d20:2 ...]
└────────────┴────────┘
Минимальный постинг — это docID. Но для ранжирования (Модуль 6) и фразового поиска нужно больше. Различают три типичных уровня детализации:
Код: Выделить всё
Уровень | Содержимое постинга | Что позволяет
----------------+-----------------------------+-------------------------------------
doc-level | docID | булев поиск, проверка наличия терма
freq-level | docID, частота tf | скоринг BM25/TF-IDF без фраз
position-level | docID, tf, список позиций | фразовый поиск, близость, зоны
Хит (hit) — это обобщённая запись об одном вхождении терма. Помимо позиции, хит часто упаковывает зону (где встретилось слово — в title, теле, якоре; см. главу 4.2) и иногда признаки оформления. Компактный хит — это битовое поле: несколько бит на тип зоны, остальное — на позицию.
Почему постинг-лист отсортирован по docIDПример. Постинг терма «альфа» в документе d11 уровня position может выглядеть так: docID=11, tf=5, positions=[2, 17, 18, 90, 240], где позиция 2 помечена зоной title. То, что два вхождения (17, 18) стоят рядом, позволит обработать фразу «альфа-альфа» как точное совпадение.
Внутри постинг-листа docID хранятся в возрастающем порядке. Это даёт два больших выигрыша:
- Пересечение списков (AND-запрос) делается слиянием за линейное время без сортировки: идём двумя указателями по двум отсортированным спискам.
- Сжатие разностями (см. ниже) становится эффективным.
Сжатие 1: кодирование разностей (delta / gap encoding)Интуиция. Запрос «альфа И бета» — это пересечение двух множеств документов. Если оба списка отсортированы, пересечение находится «застёжкой-молнией»: сравниваем головы двух списков, меньшую двигаем вперёд, совпадения выписываем.
docID могут быть большими числами (миллиарды документов → ~31–34 бита на число). Но список отсортирован, значит вместо самих чисел можно хранить разности между соседними (gaps):
Код: Выделить всё
docID: [824, 831, 833, 900, 905, 1207]
gaps: [824, 7, 2, 67, 5, 302]
Сжатие 2: целые переменной длины (varint)Интуиция. Хранить «адреса домов» дорого. Хранить «сколько шагов до следующего дома» — дёшево, потому что шаги маленькие. Восстановить адреса легко: накопительная сумма.
Varint (variable-length integer, он же VByte) кодирует целое побайтно: в каждом байте 7 бит данных и 1 бит-продолжение (continuation bit). Если бит продолжения = 1 — есть ещё байт; = 0 — это последний байт числа.
Код: Выделить всё
число 5 → 0000_0101 (1 байт)
число 300 → 1010_1100 0000_0010 (2 байта)
старший бит=1 (есть продолжение) │старший бит=0 (конец)
Сжатие 3: пакетные кодеки и PForDeltaИнженерная заметка. Главная боль varint — ветвление на каждом байте (проверка бита продолжения), что плохо ложится на конвейер процессора и предсказатель переходов. На горячем пути декодирования постинг-листов это заметно. Поэтому в высоконагруженных индексах varint часто уступает место пакетным (block-based) кодекам, декодирующим сразу группу чисел без побайтового ветвления, нередко с использованием SIMD.
Идея блочных кодеков: разбить поток gaps на блоки фиксированного размера (например, 128 значений) и кодировать каждый блок одинаковой битовой шириной.
Bit-packing (frame of reference). Находим максимальное число в блоке, берём b = ceil(log2(max+1)) бит и упаковываем все 128 чисел по b бит подряд. Декодирование — массовое и без ветвлений.
Проблема bit-packing: одно большое число в блоке («выброс», outlier) поднимает b для всех 128 значений и убивает сжатие.
PForDelta (Patched Frame Of Reference). Решает проблему выбросов так:
- Выбираем b так, чтобы под него помещалось, скажем, 90% значений блока.
- Эти значения упаковываем по b бит.
- Редкие выбросы (10%) кодируем отдельно как исключения (patches): храним их полные значения и позиции в отдельной области, а в основном потоке на их месте оставляем «заглушку».
- При декодировании сначала распаковываем основной поток bit-packing'ом, затем «накладываем заплатки» — подставляем исключения на их позиции.
Код: Выделить всё
Блок gaps: [3, 5, 2, 7, 9001, 4, 6, 2, ...]
▲ выброс
b выбран под 90% значений (например, b=4 → значения 0..15)
основной поток: [3,5,2,7, ?,4,6,2,...] упаковано по 4 бита
исключения: позиция 4 → значение 9001 (хранится отдельно, полной шириной)
Интуиция. Не порть сжатие всего блока из-за одного редкого «гиганта». Упакуй массу плотно, а гигантов вынеси в отдельный список и «залатай» на выходе.
Позиции и частоты тоже сжимают, причём отдельными потоками: частоты — своим кодеком, позиции внутри документа — теми же gap+varint/bit-packing (позиции внутри документа тоже монотонно растут). Разделение потоков («docID отдельно, частоты отдельно, позиции отдельно») позволяет читать только то, что нужно: булев AND может вообще не трогать позиции.Пример (порядок величин). Постинг-лист частого слова на 10 млн документов: «сырьём» это ~40 МБ (4 байта × 10 млн). После delta + varint — единицы МБ. После блочного PForDelta — сопоставимый размер, но декодируется в разы быстрее за счёт отсутствия побайтового ветвления.
Skip-списки: быстрое продвижение по постинг-листу
При пересечении длинного и короткого списков нет смысла декодировать длинный список целиком. Skip-указатели (skip pointers) — это разрежённый «оглавление» постинг-листа: через каждые k постингов хранится (docID, смещение в байтах), позволяя «перепрыгнуть» вперёд к нужному docID, не декодируя промежуток.
Где это «шьётся» с рантаймомИнтуиция. Метод advance(target) («доведи указатель до первого docID ≥ target») — рабочая лошадь пересечений. Skip-список превращает его из линейного прохода в почти логарифмический прыжок.
Обратный индекс — это офлайн-артефакт: его строит индексатор, а на запросе движок только читает. Латентность чтения постинг-листов — нижняя граница времени отбора кандидатов в каскаде (Модуль 12). Поэтому выбор кодека — это прямой инженерный рычаг: сильнее сжал → меньше дискового/сетевого трафика и больше влезает в память, но медленнее декодируешь.
Частые заблужденияИнженерная заметка (латентность). Правило большого пальца: на горячем пути отбора кандидатов скорость декодирования важнее предельной степени сжатия. Лишний мегабайт памяти дешевле лишней миллисекунды на запросе, умноженной на миллиарды запросов. Поэтому: блочные кодеки на горячих списках; агрессивное сжатие — на холодных данных и редких термах; позиции — отдельным потоком, чтобы не платить за них на запросах, где они не нужны.
Заблуждение. «Сжатие индекса нужно, чтобы сэкономить диск.» Не только и не столько. Главный выигрыш — меньше байт читать и держать в кеше/памяти: сжатый постинг-лист и читается с диска быстрее, и целиком помещается в RAM, что радикально снижает латентность. Диск сегодня дёшев — дорога латентность.
Заблуждение. «Чем сильнее сжатие, тем лучше.» Нет: за пределами некоторой точки рост степени сжатия оплачивается ростом времени декодирования. На горячем пути это чистый проигрыш. Кодек выбирают под нагрузку, а не «самый плотный».
Лаба / практикаЗаблуждение. «Позиции — это то же, что частота.» Частота — сколько раз слово встретилось; позиции — где именно. Частоты достаточно для BM25; для фраз и близости нужны позиции, и они стоят отдельных байт.
Задача. Реализовать кодирование/декодирование постинг-листа.
Вход. Отсортированный список docID одного терма, например: [3, 5, 6, 9, 17, 18, 19, 40, 312, 7001].
Шаги.
- Постройте gaps (разности соседних, первый элемент — как есть).
- Реализуйте varint_encode(n) и varint_decode(bytes) (7 бит данных + бит продолжения).
- Закодируйте gaps в байтовый поток, посчитайте суммарный размер в байтах и сравните с «сырым» хранением (4 байта на число).
- Реализуйте обратное декодирование: varint → gaps → накопительная сумма → исходные docID. Проверьте, что восстановили исходный список.
- (Со звёздочкой) Реализуйте advance(target) со skip-указателями через каждые 4 постинга и сравните число декодированных постингов с линейным проходом.
Критерий «сделано». Round-trip без потерь; вы можете назвать число байт на ваш список и объяснить, на каком gap'е varint переходит на 2 байта (на gap ≥ 128). Время ~60 мин.
Контрольные вопросы
- Почему docID в постинг-листе хранят отсортированными, а не в порядке появления?
- Что произойдёт со степенью сжатия gap-кодирования, если терм встречается почти в каждом документе? А если в очень редких?
- Сколько байт займёт число 200 в varint и почему?
- Какую проблему bit-packing решает PForDelta и какой ценой?
- Почему частоты и позиции выгодно хранить отдельными потоками от docID?
- В каком случае агрессивное сжатие постинг-листа ухудшает латентность запроса?
- Зачем нужны skip-указатели и как они меняют сложность операции advance?
Цели обучения
После главы студент сможет:
- объяснить, что такое зоны (zones / fields) документа и зачем хит несёт метку зоны;
- перечислить ключевые зоны: title, тело, заголовки, якорный текст (anchor text), метаданные, URL-токены;
- объяснить, почему якорный текст хранится в индексе документа-цели, а не документа-источника;
- описать модель взвешенных зон (weighted-zone / fielded scoring) на уровне принципа;
- оценить, как разбиение на зоны влияет на формирование сниппета и на ранжирование.
Документ — это не плоский мешок слов
В Модуле 1 документ моделировался как «мешок слов» (bag of words). Для реального индекса этого мало: слово в title и слово в подвале страницы — это разный сигнал. Поэтому документ разбивают на зоны (zones) — именованные области с разным семантическим весом, и каждый хит помечается зоной, в которой он встретился.
Ключевые зоныИнтуиция. Одно и то же слово «договор» в заголовке страницы кричит «страница про договоры», а в куске юридического дисклеймера внизу — почти шум. Индекс обязан различать эти два вхождения, иначе он не отличит документ «про X» от документа, где «X» упомянут вскользь.
Код: Выделить всё
Зона | Что это | Почему важна
---------------+--------------------------------------+--------------------------------------------------------------------------
title | заголовок документа (<title>) | сильнейший внутренний текстовый сигнал темы; источник заголовка сниппета
Заголовки | H1…H6, выделенные заголовки секций | структура и подтемы документа
Тело | основной текст | масса контента, база для BM25
Якорный текст | текст ссылок, ведущих на документ | внешнее описание документа чужими словами
URL-токены | слова, разобранные из URL/пути | слабый, но дешёвый тематический сигнал
Метаданные | meta-описания, атрибуты, подписи | вспомогательные сигналы и сниппет
Якорный текст (anchor text) — это видимый текст гиперссылки. Если множество страниц ссылается на документ D словами «годовой отчёт компании», то «годовой отчёт» — сильное описание D, данное со стороны.
Принципиальный момент хранения: якорный текст индексируется в зоне документа-цели, а не источника. То есть при разборе страницы A с ссылкой A → D[текст: "годовой отчёт"] строка «годовой отчёт» отправляется в индексную запись документа D как его аннотация.
Интуиция. Якорный текст — это «как тебя называют другие». Часто люди описывают страницу лаконичнее и точнее, чем её собственный title. Поэтому внешние якоря — мощный сигнал, и собирают их именно в карточку цели.
Модель взвешенных зонВнимание. Якорный текст — двусторонний меч. Он легко накручивается (массовая простановка ссылок с одинаковым текстом). Поэтому сырой якорный сигнал агрегируют, нормируют по числу источников и доменов и фильтруют (связь с антиспамом — Модуль 16). В индексе хранят не «сколько раз», а взвешенно-агрегированную аннотацию.
Простейшая схема скоринга по зонам — взвешенная сумма: итоговый текстовый вклад терма есть сумма его вкладов по зонам с весами зон:
Код: Выделить всё
score_text(d, t) = w_title · s_title(d,t)
+ w_body · s_body(d,t)
+ w_anchor· s_anchor(d,t)
+ w_head · s_head(d,t) + ...
Зоны и сниппетЗаблуждение. «Веса зон — это константы, прошитые навсегда.» На практике это входные признаки модели ранжирования и предмет обучения и A/B-проверок (Модуль 19). Зоны — это разметка; как их взвешивать, решает ранжирование.
Разбиение на зоны нужно не только ранжированию, но и формированию сниппета (Модуль 14): заголовок сниппета берут обычно из title (или из якоря/заголовка, если они точнее отвечают запросу), а тело сниппета — из фрагментов тела с подсветкой вхождений. Без хранения зон и позиций сниппет построить нечем.
Как это материализуется в индекс
Индексатор при разборе документа: токенизирует каждую зону, для каждого вхождения пишет хит с меткой зоны и позицией, отдельно накапливает входящие якоря (из обхода ссылочного графа). На выходе — постинги, где зона закодирована в хите (несколько бит), и отдельная зональная статистика (длины зон для нормировки). Рантайм затем читает зоны как признаки.
Частые заблужденияSEO-врезка. Практические следствия. (1) title и H1 — самые «дешёвые» места, где попадание слов запроса даёт сильный текстовый сигнал; они же формируют заголовок сниппета — пишите их под человека и под запрос одновременно. (2) Якорный текст входящих ссылок описывает вашу страницу чужими словами — релевантные, естественные анкоры полезны, однотипные накрученные — обнуляются или штрафуются. (3) Текст, спрятанный в подвал/«простыни» внизу, имеет малый вес зоны — ключевые формулировки выносите в тело и заголовки. (4) Слова в URL — слабый бонус, не самоцель.
Заблуждение. «Якорный текст хранится у страницы, которая ставит ссылку.» Наоборот: он приписывается цели ссылки — это её описание глазами внешних авторов.
Заблуждение. «Чем больше повторить ключевое слово в title, тем лучше.» Насыщение (Модуль 6) гасит выгоду повторов, а аномальная плотность ловится антиспамом (Модуль 16). Зона title сильна, но не безнаказанна.
Лаба / практикаЗаблуждение. «Зоны — это только про ранжирование.» Зоны и позиции одновременно обслуживают и ранжирование, и сниппеты, и фразовый/зональный поиск (intitle:-подобные операторы).
Задача. Построить зональный индекс на мини-корпусе и сравнить ранжирование с/без весов зон.
Вход. 5 коротких HTML-документов с заполненными <title>, H1, телом; и таблица входящих якорей вида target_docID → anchor_text (3–4 якоря).
Шаги.
- Токенизируйте каждую зону отдельно; постройте постинги с меткой зоны в хите.
- Сложите входящие якоря в зону anchor целевых документов.
- Для запроса из 2 слов посчитайте: (a) скор только по телу; (b) взвешенную сумму зон с весами title=3, head=2, body=1, anchor=2.
- Сравните два ранжирования, объясните перестановки.
Критерий «сделано». Вы показываете, какой документ и почему сместился, и какой вес зоны это вызвал. Время ~50 мин.
Контрольные вопросы
- Что такое зона документа и зачем хит несёт её метку?
- В чьей индексной записи хранится якорный текст ссылки и почему?
- Приведите две роли разбиения на зоны помимо ранжирования.
- Почему якорный сигнал нельзя брать «сырым»?
- Откуда берётся заголовок сниппета и почему он не всегда равен title?
- Являются ли веса зон константами? Что определяет их значения?
Цели обучения
После главы студент сможет:
- определить понятие «статика» (static / query-independent features) и отличить её от запросозависимых признаков;
- привести примеры документной и хостовой статики;
- объяснить главный тезис модуля: робот материализует статику в индекс, а рантайм читает её как факторы — и почему это «шов» офлайн/онлайн;
- сформулировать критерий «считать офлайн или онлайн» (латентность, стоимость, свежесть, потребность в данных запроса);
- описать, как статика хранится в индексе (колонки/прямой индекс) и читается каскадом.
Что такое статика
Статика (static features) — это признаки документа или хоста, не зависящие от запроса. Их можно вычислить один раз заранее и положить в индекс. Противоположность — запросозависимые (dynamic / query-dependent) признаки: BM25, близость терминов, совпадение зон — их нельзя посчитать без запроса.
Документная статика (примеры)Интуиция. Статика — это «паспорт документа», заполненный до того, как кто-то что-то спросил: насколько он авторитетный, свежий, длинный, на каком языке, не спам ли. Когда придёт запрос, рантайму останется только прочитать паспорт — не пересчитывать.
- Ссылочный авторитет документа (PageRank-подобная мера, Модуль 7).
- Длина документа и длины зон (для нормировки BM25).
- Язык и регион документа, кодировка.
- Возраст/дата обновления, частота изменений (для свежести, Модуль 17).
- Качество/читабельность контента, доля «boilerplate», флаги спама.
- Тип/жанр документа (форум, новость, товар), наличие структурированных данных.
- Поведенческая статика, агрегированная офлайн (Модуль 11) — например, сглаженная популярность.
- Авторитет хоста/домена, его «надёжность» и история спама.
- Агрегированные характеристики хоста: средняя свежесть, доля качественного контента, гео-принадлежность.
- Технические сигналы хоста: доступность, скорость, поддержка безопасного соединения.
Главный тезис: материализация статики — это шов офлайн/онлайнИнтуиция. Хостовая статика — это «общая репутация издателя», которую наследует каждая его страница, особенно пока у самой страницы мало собственных сигналов (новая страница на авторитетном хосте стартует не с нуля).
Код: Выделить всё
ОФЛАЙН (индексатор, робот) ОНЛАЙН (рантайм)
┌──────────────────────────────────────┐ шов ┌──────────────────────────┐
│ обход → каноник. → разбор → │ ═════► │ читает постинги + │
│ ВЫЧИСЛЯЕТ статику (PageRank, язык, │ ИНДЕКС │ читает статику как ФАКТОРЫ │
│ свежесть, качество, хостовые меры) → │ (запись)│ (без пересчёта) → │
│ МАТЕРИАЛИЗУЕТ её в индекс │ (чтение)│ каскад L0–L3 ранжирует │
└──────────────────────────────────────┘ └──────────────────────────┘
Критерий «офлайн или онлайн»Пример. PageRank документа считается офлайн на всём ссылочном графе (это глобальная итеративная процедура, Модуль 7) — на запросе её посчитать невозможно (нет времени и нет смысла, она от запроса не зависит). Поэтому её значение материализуется в индекс. На запросе «купить велосипед» рантайм для каждого кандидата просто читает его сохранённый авторитет и складывает с BM25 — секунды офлайн-счёта превращаются в наносекунды чтения.
Считать признак заранее (материализовать) выгодно, если:
- он не зависит от запроса (иначе нельзя предвычислить в принципе);
- его дорого считать на запросе (глобальные графовые меры, тяжёлые агрегаты);
- он меняется медленнее, чем мы готовы терпеть устаревание (иначе придётся часто перестраивать — конфликт со свежестью, Модуль 17).
Как статика хранится и читаетсяВнимание. Материализация — это компромисс со свежестью. Любая статика в индексе — это «снимок» на момент построения сегмента. PageRank в индексе может отставать от реального графа на часы/дни. Поэтому статику пересчитывают и переливают в индекс с тем темпом, который оправдан её скоростью изменения и стоимостью пересборки (см. 4.4 про сегменты и 4.5 про realtime).
Статика — это, по сути, прямой индекс признаков: «по docID → вектор/набор признаков». Хранят её обычно по колонкам (columnar): отдельная колонка на признак, выровненная по docID. Колоночное хранение даёт две вещи: (1) дешёвую дозагрузку только нужных факторов; (2) хорошее сжатие однородных колонок (числа близких порядков). На запросе ранний ярус каскада (L0/L1) читает дешёвую статику для грубой отсечки, поздние ярусы (L2/L3) дочитывают дорогие факторы только для выживших кандидатов (Модуль 12).
Инженерная заметка (латентность). Статику кладут «близко» к постингам, чтобы после отбора кандидатов её чтение было локальным (тот же шард/сегмент, желательно тот же узел и горячий кеш). Разъезд постингов и статики по разным хранилищам = дополнительный сетевой round-trip на каждый кандидат = смерть для латентности. Принцип: факторы живут там же, где документы, которые они описывают.
Частые заблужденияSEO-врезка. «Статика» — это бо́льшая часть того, что в SEO называют «факторами уровня страницы/сайта»: авторитет, качество, скорость, свежесть, язык/гео, репутация домена. Ключевое следствие: эти сигналы вычисляются офлайн и обновляются с задержкой. Поэтому эффект от работ над авторитетом/качеством виден не мгновенно — он проявляется после очередной материализации статики в индекс. Новой странице помогает хостовая статика (репутация домена), пока не накопятся собственные сигналы.
Заблуждение. «Авторитет считается на запросе.» Нет: запросонезависимая статика (авторитет, качество, язык) считается офлайн и читается на запросе. На запросе считается только то, что без запроса посчитать нельзя.
Заблуждение. «Раз признак в индексе — значит, он всегда актуален.» Любая статика — это снимок на момент построения сегмента; между пересборками она устаревает. Свежесть статики — отдельная инженерная забота.
Лаба / практикаЗаблуждение. «Чем больше факторов материализовать, тем лучше.» Каждый материализованный фактор — это место в индексе, трафик чтения и обязательство его пересчитывать. Материализуют то, что дорого считать и стабильно во времени; остальное считают на лету.
Задача. Спроектировать раскладку статики и смоделировать шов офлайн/онлайн.
Вход. Список из 8 признаков (например: BM25, близость, авторитет документа, авторитет хоста, длина, язык, возраст, флаг спама).
Шаги.
- Разметьте каждый признак как статический (материализуется) или запросозависимый (считается онлайн); обоснуйте.
- Для статических укажите ожидаемый темп обновления (минуты/часы/сутки) и почему.
- Спроектируйте колоночную раскладку статики по docID; прикиньте, какие колонки читает дешёвый ярус (L0/L1), а какие — дорогой (L2/L3).
- Напишите псевдокод обработки запроса: отбор кандидатов → чтение дешёвой статики → отсечка → дочитывание дорогой статики для выживших.
Критерий «сделано». Для каждого признака сказано, где он считается и почему; псевдокод не читает дорогие факторы для отсечённых кандидатов. Время ~45 мин.
Контрольные вопросы
- Дайте определение статики. Почему BM25 не статика, а авторитет — статика?
- Сформулируйте тезис «робот материализует, рантайм читает». Где проходит шов офлайн/онлайн?
- Назовите три условия, при которых признак выгодно предвычислять офлайн.
- Чем материализация статики «платит» за скорость на запросе?
- Почему статику хранят колоночно и рядом с постингами?
- Что наследует новая страница от хоста и зачем это нужно?
Цели обучения
После главы студент сможет:
- объяснить, почему индекс строят из неизменяемых сегментов (immutable segments) и как работают слияния (merges) и удаления;
- описать ярусную (tiered) организацию индекса и зачем разделять «хороший» и «хвостовой» контент;
- объяснить квоты отбора (selection quotas) и каскадный обход ярусов до набора достаточного числа кандидатов;
- сравнить шардирование по документам (document partitioning) и по термам (term partitioning);
- оценить влияние топологии индекса на латентность, полноту и стоимость.
Сегменты: индекс строят из неизменяемых кусков
Гигантский индекс нельзя строить и обновлять как единый монолит. Его собирают из сегментов (segments) — самодостаточных мини-индексов (свой словарь + постинги + статика по подмножеству документов). Ключевое свойство сегмента — неизменяемость (immutability): записанный сегмент не редактируют.
Почему immutable:
- запись «один раз, последовательно» (append-only) дёшева и не требует блокировок;
- неизменяемый сегмент тривиально кешируется и реплицируется (он никогда не «протухает» по содержимому);
- параллельное чтение не конфликтует с записью.
- Добавление документа — новый маленький сегмент.
- Удаление/обновление — не правка на месте, а запись docID в список удалённых (delete list / tombstones); старая версия логически «погашена», физически исчезает при слиянии.
- Слияние (merge / compaction) — фоновый процесс: несколько мелких сегментов переписываются в один крупный, при этом погашенные документы выбрасываются, постинг-листы пересжимаются.
Код: Выделить всё
поток новых документов
│ append-only
▼
[seg1][seg2][seg3][seg4][seg5] ... + delete-list (tombstones)
│ слияние (merge)
▼
[ крупный seg ] ← мусор (удалённые) физически выкинут
Интуиция. Это как журнал бухгалтерии: вы не затираете старую запись, вы пишете новую («сторно»). Раз в период журнал «сводят» (merge): итог переписывают начисто, отменённые записи выкидывают.
Ярусы (тиры) индексаИнженерная заметка. Слияния — это компромисс. Мелких сегментов много → быстрая индексация, но медленный поиск (на запросе надо опросить много сегментов и объединить результаты) и много места под мусор. Крупных сегментов мало → быстрый поиск, но дорогие слияния и большая задержка появления изменений. Политика слияний (когда и что сливать) — отдельный объект тюнинга.
Не весь контент равноценен. Разумно разложить документы по ярусам / тирам (tiers) по ожидаемой ценности:
Код: Выделить всё
Ярус | Что внутри | Свойства
----------------+----------------------------------------------------------+-----------------------------------------------
Верхний (head) | авторитетный, качественный, частозапрашиваемый контент | малый, горячий, в памяти, опрашивается первым
Средний | основная масса нормального контента | крупнее, частично на диске
Нижний (tail) | редкий, низкоавторитетный, «хвостовой» контент | огромный, холодный, опрашивается редко
Квоты отбора и каскадный обход ярусовИнтуиция. Сначала ищем в «отделе бестселлеров» — там почти всегда есть, что показать. В «архив редкостей» спускаемся, только если в бестселлерах не набралось нужного.
Квота отбора (selection quota) — целевое число кандидатов, которое отбор должен набрать, прежде чем передать их выше по каскаду ранжирования (Модуль 12). Обход ярусов идёт каскадно:
Код: Выделить всё
нужно набрать ≥ N кандидатов:
спросить ВЕРХНИЙ ярус → набралось ≥ N и качество достаточно? → СТОП
иначе → спросить СРЕДНИЙ → добрать
иначе → спросить НИЖНИЙ → добрать до N (или сколько есть)
Внутри яруса и внутри постинг-листа тоже действуют ограничения: длинные постинг-листы частых слов усекают (берут не весь список, а самые перспективные по статике начала) — это связано с упорядочиванием постингов и ранним завершением (early termination) обхода. Грубо: «не читай миллион документов с частым словом, прочти лучшие десятки тысяч и остановись, если выживших уже хватает».Интуиция. Квота — это «сколько кандидатов достаточно, чтобы дальше было из чего выбирать». Набрали из верхнего яруса — отлично, дешёвый запрос. Не набрали (редкий запрос, узкая тема) — спускаемся ниже, платя латентностью за полноту.
Шардирование: как резать индекс между машинамиВнимание. Квоты и усечение — это управляемый компромисс между полнотой (recall) и латентностью/стоимостью. Слишком жёсткие квоты → можно не дотянуться до редкого, но точного ответа (потеря recall на хвостовых запросах). Слишком щедрые → лишняя латентность и вычисления на каждом запросе. Это настраивают и проверяют на A/B (Модуль 19).
Один сервер весь индекс не вместит. Его шардируют (shard) — режут на части по машинам. Две базовые стратегии:
1. По документам (document partitioning, локальный индекс). Каждый шард держит полный обратный индекс по своему подмножеству документов. Запрос рассылается на все шарды (scatter), каждый ищет у себя свой топ-k, координатор сливает (gather).
Код: Выделить всё
запрос ──► [шард 1: docs 0..N] ─┐
──► [шард 2: docs N..2N] ─┼─► координатор: merge top-k
──► [шард 3: docs 2N..3N] ─┘
- Плюсы: простое добавление документов (новый документ → в свой шард), естественная балансировка, отказ шарда = частичная потеря, а не полная.
- Минусы: каждый запрос идёт на все шарды → латентность определяется самым медленным («хвост латентности», tail latency); глобальные статистики (df, idf) надо согласовывать между шардами.
- Плюсы: запрос трогает мало шардов; целые постинг-листы локальны.
- Минусы: «горячие» частые слова создают перекос нагрузки; многословные запросы требуют пересылать длинные списки между шардами для пересечения; обновление документа задевает много шардов.
Инженерная заметка. На практике для веб-поиска доминирует шардирование по документам (с репликацией каждого шарда ради пропускной способности и отказоустойчивости) — именно потому, что добавление документов локально, а отказ одного шарда не валит всю систему. Партиционирование по термам встречается реже и в нишах. Подробнее распределённое обслуживание — в Модуле 13.
Как всё связаноВнимание (хвост латентности). При scatter-gather на тысячи шардов время ответа диктует самый медленный шард, а не средний. Поэтому реальная борьба идёт не за средний, а за 99-й перцентиль латентности: реплики, хеджированные запросы (послать дубль и взять первый ответ), бюджеты времени с досрочной отсечкой отстающих.
Сегменты (immutability) дают дешёвую индексацию и кеширование; ярусы и квоты режут стоимость отбора; шардирование даёт масштаб и отказоустойчивость. Вместе они образуют топологию, которую читает рантайм на каждом запросе. Это прямое продолжение шва из 4.3: статика, материализованная офлайн, помогает раскладывать документы по ярусам и упорядочивать постинги для раннего завершения.
Частые заблужденияSEO-врезка. Практические следствия. (1) Попадание в верхний ярус (авторитетный/качественный контент) означает, что вашу страницу находят дёшево и на широких запросах; слабый «хвостовой» контент достаётся реже и медленнее. (2) Усечение длинных постинг-листов и квоты означают: по очень частым словам в выборку попадают «лучшие по статике» — без авторитета/качества в гипер-конкурентной выдаче можно не дойти до этапа честного ранжирования. (3) Низкая видимость может быть следствием не «плохого текста», а попадания в холодный ярус из-за хостовой/документной статики.
Заблуждение. «Документы в индексе обновляются на месте.» Нет: сегменты неизменяемы; обновление = новый сегмент + tombstone старого; физическая чистка — при слиянии.
Заблуждение. «Индекс ищет по всем документам на каждом запросе.» Обычно нет: ярусы и квоты позволяют остановиться, набрав достаточно кандидатов из верхних ярусов, не трогая хвост.
Заблуждение. «При шардировании по документам запрос быстр, потому что идёт в один шард.» Наоборот — он идёт во все шарды (scatter-gather), и латентность определяет самый медленный из них.
Лаба / практикаЗаблуждение. «Усечение постинг-листа теряет релевантные документы случайно.» Усечение опирается на упорядочивание по статике: отсекается «менее перспективный хвост», а не случайные документы — но риск потери recall на узких запросах реален и его измеряют.
Задача. Смоделировать сегменты, слияние и каскадный отбор по ярусам.
Вход. Поток операций: add(docID, terms), delete(docID); и набор документов с проставленной «ценностью» (имитация статики), запрос из 1–2 слов, квота N=20.
Шаги.
- Реализуйте добавление как новый сегмент (список постинг-листов) и удаление как tombstone.
- Реализуйте merge(): слить 2+ сегмента в один, выкинув документы из delete-list.
- Разложите документы по 2–3 ярусам по «ценности».
- Реализуйте отбор с квотой: опрашивайте ярусы сверху вниз, пока не набрали N кандидатов; считайте, сколько ярусов реально пришлось тронуть.
- Сравните «дешёвый» запрос (хватило верхнего яруса) и «дорогой» (пришлось спуститься в хвост).
Критерий «сделано». Tombstone'нутые документы исчезают после merge; журнал показывает, сколько ярусов опрошено на каждом запросе и почему остановились. Время ~70 мин.
Контрольные вопросы
- Почему сегменты делают неизменяемыми? Как тогда удаляют документ?
- Что делает слияние (merge) и какой компромисс задаёт политика слияний?
- Зачем нужны ярусы индекса и в каком порядке их опрашивают?
- Что такое квота отбора и что регулирует её величина?
- Сравните шардирование по документам и по термам: плюсы, минусы, когда что.
- Почему при scatter-gather важен 99-й перцентиль латентности, а не средний?
- Какой риск несёт усечение длинных постинг-листов и как его контролируют?
Цели обучения
После главы студент сможет:
- различать пакетное (batch) построение индекса и реалтайм-контур (realtime / near-real-time indexing);
- объяснить архитектуру «горячий писчий сегмент + холодные неизменяемые сегменты» и роль журнала (write-ahead log);
- объяснить понятие задержки индексации (indexing latency) и видимости документа (visibility) в поиске;
- описать схему, где быстрый realtime-контур и медленный, но качественный batch-контур работают вместе;
- оценить компромисс «свежесть ↔ полнота статики/качество ранжирования».
Два контура построения индекса
Индекс строят в двух режимах одновременно:
Батч-построение (batch). Большие порции документов перерабатываются периодически: пересчитывается дорогая статика (PageRank на всём графе, агрегаты качества), пересжимаются крупные сегменты, выполняются глубокие слияния. Это медленно, но качественно и дёшево на документ (амортизация на огромных объёмах).
Реалтайм-контур (realtime / near-real-time, NRT). Новый или изменившийся документ должен стать находимым за секунды-минуты, а не за часы следующего батча. Для этого есть отдельный быстрый путь, добавляющий документ в индекс почти сразу.
Архитектура: горячий писчий сегмент + журналИнтуиция. Батч — это «переиздание энциклопедии раз в период»: тщательно, выверенно, но редко. Реалтайм — это «вклейка свежих листов» между переизданиями: быстро и находимо сейчас, пусть и без полной выверки. Новость о событии должна быть находимой через минуты, а не ждать ночной пересборки.
Реалтайм-контур обычно устроен так:
Код: Выделить всё
запись документа
│
▼
[Write-Ahead Log] ──(durability: переживём перезапуск)
│
▼
[ГОРЯЧИЙ in-memory сегмент] ← сюда мгновенно добавляются постинги, документ становится находимым
│ периодический flush
▼
[маленький неизменяемый on-disk сегмент] ─┐
[другие холодные сегменты] ───────────────┼─► фоновые слияния → крупные сегменты
┘
- Журнал (write-ahead log, WAL). Документ сперва пишется в журнал (для надёжности: если узел упадёт, горячий сегмент восстановят из журнала).
- Горячий сегмент (in-memory). Изменяемая буферная структура в памяти, куда мгновенно попадают новые постинги. Поиск опрашивает её вместе с холодными сегментами, поэтому новый документ находится почти сразу.
- Flush. Периодически горячий сегмент «застывает» в маленький неизменяемый on-disk сегмент (см. 4.4) и обнуляется.
- Merge. Маленькие сегменты фоном сливаются в крупные (compaction), там же документу досчитывается дорогая статика.
Задержка индексации и видимостьИнженерная заметка. Поиск по горячему in-memory сегменту и по сжатым холодным сегментам — это два разных пути чтения, которые надо объединить в одном ответе. Часто горячий сегмент хранит постинги в менее сжатом, но быстро-вставляемом виде; холодные — в плотном PForDelta (глава 4.1). Цена realtime — поддержка двух представлений и постоянные flush/merge в фоне.
Задержка индексации (indexing latency) — время от «документ добыт/изменён» до «документ находится по запросу». Видимость (visibility) — момент, с которого документ участвует в поиске.
Тонкий момент — момент видимости относительно flush/commit: документ должен стать видимым атомарно (либо находится со всеми своими термами, либо ещё нет — не «половина термов»). Поэтому видимость привязывают к точке коммита горячего сегмента, а удаление — к применению tombstone (глава 4.4): пока tombstone не виден, старая версия ещё находится.
Как два контура работают вместеВнимание. Реалтайм-контур ускоряет видимость, но жертвует качеством сигналов. Только что добавленный документ ещё не имеет полной статики: его ссылочный авторитет не пересчитан на свежем графе, поведенческие сигналы не накоплены, агрегаты качества грубые. То есть он находится, но ранжируется по неполному набору факторов, пока следующий батч/merge не «дообогатит» его статикой. Это прямое следствие шва из 4.3: realtime материализует то, что можно посчитать быстро, а дорогую статику доливает батч.
Код: Выделить всё
БЫСТРО, неполные сигналы МЕДЛЕННО, полная статика
┌───────────────────────────────┐ ┌──────────────────────────────┐
│ REALTIME: документ виден за │ │ BATCH: пересчёт PageRank, │
│ секунды-минуты, базовая статика│ ──► │ качества, поведения; глубокие │
│ (язык, длина, дешёвые флаги) │ │ слияния; полная материализация│
└───────────────────────────────┘ └──────────────────────────────┘
видимость СЕЙЧАС качество ПОТОМ
Заблуждение. «Realtime-индекс заменяет батч.» Нет: они дополняют друг друга. Realtime даёт свежесть и видимость; батч даёт качество статики, плотное сжатие и дешёвую амортизированную стоимость. Без батча realtime-сегменты не сольются и не получат полную статику; без realtime свежее ждало бы следующей пересборки.
Частые заблужденияSEO-врезка. Почему «проиндексировалось» и «хорошо ранжируется» — это разные события, разнесённые во времени. Новая страница может стать находимой быстро (realtime-контур), но её сильные сигналы — авторитет, качество, поведение — материализуются позже, в батч-цикле. Отсюда типичная картина: страница «появилась в индексе», но позиции «дозревают» дни/недели по мере дообогащения статикой и пересчёта ссылочного графа. Частые правки страницы перед каждым flush не ускоряют «дозревание» дорогих сигналов.
Заблуждение. «Документ индексируется мгновенно и сразу ранжируется в полную силу.» Видимость может быть быстрой (realtime), но полнота сигналов приходит с батч-циклом — это разные моменты времени.
Заблуждение. «Если документ виден, значит, все его термы уже в индексе.» Видимость атомарна по коммиту: либо документ со всеми термами виден, либо ещё нет; «полудокументов» в корректной системе быть не должно.
Лаба / практикаЗаблуждение. «Realtime-контур не нуждается в надёжном хранении — это же временный буфер.» Нет: до flush'а данные горячего сегмента защищает журнал (WAL), иначе перезапуск узла терял бы свежие документы.
Задача. Смоделировать realtime-контур поверх сегментной модели из 4.4.
Вход. Поток событий: add(doc), update(doc), delete(docID), flush(), merge(), search(query); у каждого документа — «дешёвая статика» (есть сразу) и «дорогая статика» (появляется только после merge).
Шаги.
- Реализуйте горячий in-memory сегмент: add/update/delete меняют его мгновенно; search опрашивает горячий + холодные сегменты.
- По flush() застывайте горячий сегмент в неизменяемый и заводите новый пустой; фиксируйте видимость по коммиту.
- По merge() сливайте сегменты, применяйте tombstones и досчитывайте дорогую статику.
- Измерьте задержку индексации: сколько событий между add(doc) и первым search, который его находит.
- Покажите документ, который находится сразу после add, но получает дорогую статику (и поднимается в ранжировании) только после merge.
Критерий «сделано». Вы демонстрируете разрыв во времени между «стал виден» и «получил полную статику» и объясняете его через шов офлайн/онлайн. Время ~70 мин.
Контрольные вопросы
- Чем отличаются batch- и realtime-контуры по скорости и качеству сигналов?
- Зачем нужен горячий in-memory сегмент и почему поиск опрашивает его вместе с холодными?
- Какова роль журнала (WAL) в реалтайм-контуре?
- Что такое задержка индексации и видимость? Почему видимость должна быть атомарной?
- Почему свежий документ «находится», но ранжируется по неполным факторам?
- Почему realtime не отменяет batch, а дополняет его?
- Обратный индекс — это словарь term → постинг-лист; постинги хранятся отсортированными по docID и содержат частоты, позиции и хиты (с меткой зоны), что и даёт булев поиск, скоринг и фразы.
- Сжатие (delta-кодирование, varint, блочный PForDelta) экономит не столько диск, сколько память и латентность чтения; на горячем пути скорость декодирования важнее предельной степени сжатия.
- Зоны документа (title, заголовки, тело, якорный текст, URL) несут разный вес; якорный текст приписывается документу-цели и описывает его «чужими словами». Зоны обслуживают и ранжирование, и сниппеты.
- Статика — это запросонезависимые признаки документа и хоста (авторитет, качество, свежесть, язык). Их вычисляют офлайн и материализуют в индекс.
- Шов офлайн/онлайн: робот материализует статику в индекс, рантайм читает её как готовые факторы. Материализуют то, что дорого считать и стабильно во времени; запросозависимое (BM25, близость) считают на лету.
- Индекс строится из неизменяемых сегментов; обновление = новый сегмент + tombstone, физическая чистка — при слиянии (merge/compaction).
- Ярусы и квоты отбора позволяют не опрашивать весь индекс: сначала верхний ярус, спуск в хвост — только при нехватке кандидатов; длинные постинг-листы усекают по статике.
- Шардирование по документам (scatter-gather) доминирует в вебе ради локального добавления и отказоустойчивости; цена — хвост латентности (важен 99-й перцентиль).
- Realtime-контур (горячий in-memory сегмент + WAL) даёт видимость за секунды; batch-контур даёт полную статику, плотное сжатие и амортизированную стоимость. Видимость и полнота сигналов — разнесённые во времени события.
- Обратный индекс (inverted index) — структура term → список документов с термом.
- Прямой индекс (forward index) — документ → его термы/признаки; так же хранят и статику по docID.
- Постинг-лист (posting list) — список вхождений терма (отсортирован по docID).
- Постинг (posting) — одна запись: docID (+ частота, позиции, хиты).
- Хит (hit) — упакованная запись об одном вхождении терма (позиция + зона + признаки).
- Зона (zone / field) — именованная область документа (title, тело, заголовки, якорь, URL).
- Якорный текст (anchor text) — текст входящей ссылки; индексируется у документа-цели.
- Delta/gap-кодирование — хранение разностей соседних отсортированных значений вместо самих значений.
- Varint (VByte) — кодирование целого байтами по 7 бит данных + бит продолжения.
- Bit-packing (frame of reference) — упаковка блока чисел одинаковой битовой шириной.
- PForDelta — bit-packing с выносом редких выбросов в исключения (patches).
- Skip-указатели (skip pointers) — разрежённый индекс внутри постинг-листа для быстрого advance.
- Статика (static / query-independent features) — запросонезависимые признаки (авторитет, качество, свежесть, язык).
- Материализация статики — запись предвычисленных признаков в индекс на офлайн-стороне.
- Шов офлайн/онлайн — граница, где предвычисленное офлайн читается рантаймом как факторы.
- Сегмент (segment) — неизменяемый самодостаточный мини-индекс.
- Tombstone (delete list) — пометка docID как удалённого без физической правки.
- Слияние (merge / compaction) — переписывание мелких сегментов в крупный с выбросом мусора.
- Ярус / тир (tier) — уровень индекса по ценности контента (head / средний / tail).
- Квота отбора (selection quota) — целевое число кандидатов для передачи в каскад ранжирования.
- Раннее завершение (early termination) — остановка обхода постингов при достаточном числе кандидатов.
- Шардирование (sharding) — разрезание индекса между машинами (по документам или по термам).
- Scatter-gather — рассылка запроса на все шарды и слияние их топ-k.
- Хвост латентности (tail latency) — медленные перцентили (например, 99-й) времени ответа.
- Batch-построение — периодическая пакетная пересборка индекса и статики.
- Realtime/NRT-контур — быстрый путь, делающий документ находимым за секунды-минуты.
- Журнал (write-ahead log, WAL) — лог для надёжности горячего сегмента.
- Задержка индексации (indexing latency) — время от добычи/изменения документа до его находимости.
- Видимость (visibility) — момент, с которого документ участвует в поиске (атомарно по коммиту).
- Опирается на: Модуль 1 (термин, документ, токенизация, модель IR), Модуль 3 (идентичность документа, docID, дедупликация — что вообще попадает в индекс).
- Питает: Модуль 6 (текстовая релевантность читает постинги, частоты, позиции, зоны — основа BM25/BM25F), Модуль 7 (ссылочный авторитет — пример дорогой статики, считаемой офлайн), Модуль 8 (таксономия факторов: статика vs динамика), Модуль 12 (каскад ранжирования читает кандидатов и статику; квоты и ярусы определяют отбор), Модуль 13 (распределённое обслуживание: шарды, реплики, scatter-gather, хвост латентности), Модуль 17 (свежесть и реалтайм: realtime-контур — её механизм).
- Перекликается с: Модуль 16 (антиспам фильтрует якорный сигнал и аномалии), Модуль 18 (персонализация — пример того, что нельзя материализовать заранее), Модуль 19 (A/B-проверка квот, политик слияний, кодеков).
- Классические тексты по архитектуре обратного индекса и обработке постинг-листов (булев и ранжированный поиск, пересечение списков, skip-списки).
- Обзоры и сравнения кодеков сжатия целых для IR (variable-byte, frame-of-reference, PForDelta, современные SIMD-ориентированные схемы) — с замерами «степень сжатия ↔ скорость декодирования».
- Литература по взвешенным зонам и fielded-ранжированию (модели вида BM25F) — как вход для главы 4.2 и Модуля 6.
- Материалы по запросонезависимым (static) признакам и упорядочиванию документов для раннего завершения обхода.
- Работы по сегментным append-only индексам, политикам слияний (compaction) и near-real-time индексации (горячий сегмент + WAL).
- Обзоры по распределённому поиску: партиционирование по документам vs по термам, scatter-gather, борьба с хвостом латентности (репликация, хеджированные запросы).