Как масштабировать поиск до миллионов документов с ограниченным бюджетом
Alex Chibilyaev
5/3/2026
Масштабирование поиска от тысяч до миллионов документов — это вызов, с которым сталкивается каждый растущий продукт. Хорошая новость: вам не нужна корпоративная инфраструктура для обработки миллионов документов. При правильной архитектуре вы можете обслуживать миллионы документов на доступной инфраструктуре.
Это руководство охватывает практические стратегии масштабирования для AACsearch (и AACSearch) на каждом этапе роста.
Этап 1: 10K–100K документов (Free → Starter)
На этом этапе любой поисковый движок работает хорошо. Сосредоточьтесь на корректности и релевантности, а не на инфраструктуре.
Лучшие практики
Оптимизация документов:
- Держите каждый документ в пределах 10 КБ (исключите неиспользуемые поля)
- Ограничьте поисковые поля до 5–7 (меньше полей = быстрее запросы)
- Используйте строковые ID (AACSearch лучше обрабатывает строки, чем целые числа для ID)
// Оптимизированный документ (8 КБ → 2 КБ)
{
"id": "prod_001",
"name": "Wireless Bluetooth Headphones",
"price": 79.99,
"brand": "SoundMax",
"category": "Electronics",
"in_stock": true
}
Производительность на этом этапе
| Метрика | Ожидаемое значение | | ---------------------- | ------------------ | | Задержка запроса (P50) | < 10 мс | | Задержка запроса (P95) | < 30 мс | | Скорость индексации | 5 000 док/сек | | Одновременных запросов | 100+ QPS | | Необходимый план | Free или Starter |
Этап 2: 100K–500K документов (Starter → Scale)
Когда вы пересекаете 100K документов, паттерны запросов начинают иметь значение. Не все поля должны быть доступны для поиска.
Стратегия индексации
Используйте подмножества полей для поиска:
{
"searchable_fields": ["name", "brand", "category"],
"facet_fields": ["brand", "category", "price"],
"sortable_fields": ["price", "rating", "created_at"]
// НЕ поисковые: description, long_text, raw_data
}
Исключая description и long_text из поисковых полей, вы уменьшаете размер индекса на 40–60% с минимальным влиянием на качество поиска (пользователи в основном ищут по названию и бренду).
Оптимизация фасетов
Для фасетов с высокой кардинальностью (много уникальных значений) ограничьте возвращаемые значения:
{
"max_facet_values": 15,
"facet_query": "brand", // Разрешить поиск внутри значений фасета
"facet_limit": 50 // Никогда не возвращать более 50 значений
}
Кэширование
Внедрите серверное кэширование для популярных запросов:
const cache = new Map<string, SearchResult>();
const CACHE_TTL = 60_000; // 60 секунд
async function searchWithCache(query: string) {
const key = query.toLowerCase().trim();
if (cache.has(key)) {
const cached = cache.get(key)!;
if (Date.now() - cached.timestamp < CACHE_TTL) {
return cached.data;
}
cache.delete(key);
}
const results = await AACSearch.search({ q: query });
cache.set(key, { data: results, timestamp: Date.now() });
return results;
}
При 50% попаданий в кэш это фактически удваивает вашу пропускную способность.
Производительность на этом этапе
| Метрика | Ожидаемое значение | | ---------------------- | ------------------ | | Задержка запроса (P50) | < 15 мс | | Задержка запроса (P95) | < 50 мс | | Размер индекса | 500 МБ – 2 ГБ | | Одновременных запросов | 200–500 QPS | | Необходимый план | Starter или Scale |
Этап 3: 500K–5M документов (Scale → Pro)
За пределами 500K документов требуются продуманные архитектурные решения.
Шардирование индексов
AACsearch масштабируется горизонтально. Для больших индексов рассмотрите шардирование по:
Вариант A: Шардирование по арендатору (для мультиарендных приложений)
// Каждый арендатор получает свой шард/индекс
const shard = `products_${Math.floor(tenantId / 1000)}`;
Вариант B: Шардирование по категории документов
// Разделение по логическим категориям
const shards = ["products_electronics", "products_clothing", "products_home"];
Вариант C: Шардирование по дате (для временных рядов)
// Месячные шарды для логов/контента
const shard = `products_2025_${String(month).padStart(2, "0")}`;
Стратегия пакетной индексации
Для больших первичных импортов или переиндексации:
async function batchReindex(documents: Document[]) {
const BATCH_SIZE = 10000;
const CONCURRENCY = 4;
// Обработка пакетов параллельно
const batches = chunk(documents, BATCH_SIZE);
const results = [];
for (let i = 0; i < batches.length; i += CONCURRENCY) {
const batchGroup = batches.slice(i, i + CONCURRENCY);
const batchResults = await Promise.all(
batchGroup.map((batch) => AACSearch.importDocuments("products", batch)),
);
results.push(...batchResults);
}
return results;
}
Оптимизация запросов
Используйте курсорную пагинацию вместо смещённой:
// ХОРОШО: Курсорная (быстрая, стабильная)
const page1 = await AACSearch.search({ q: "shoes", per_page: 20 });
const page2 = await AACSearch.search({
q: "shoes",
per_page: 20,
page: page1.next_page, // курсор
});
// ПЛОХО: Со смещением (медленнее на больших смещениях)
const page100 = await AACSearch.search({ q: "shoes", per_page: 20, page: 100 });
Используйте предварительный выбор на основе фильтра для больших фасетов:
// Вместо возврата всех 5 000 брендов, позвольте пользователям искать внутри фасетов
const query = { q: "headphones", facet_by: "brand", max_facet_values: 10 };
// Пользователь нажимает «Показать больше брендов» → вызов с facet_query
const facetQuery = { q: "headphones", facet_by: "brand", facet_query: "sony" };
Производительность на этом этапе
| Метрика | Ожидаемое значение | | ---------------------- | ------------------ | | Задержка запроса (P50) | < 20 мс | | Задержка запроса (P95) | < 80 мс | | Размер индекса | 2 ГБ – 15 ГБ | | Одновременных запросов | 500–1000 QPS | | Необходимый план | Scale или Pro |
Этап 4: 5M–50M документов (Pro + Custom)
На этом этапе вы управляете значительной поисковой операцией. Ключевые стратегии:
Выделенная инфраструктура
AACsearch Pro включает опции выделенной инфраструктуры:
- Изолированные поисковые узлы (без шумных соседей)
- Индивидуальное SLA (99,95%+)
- Выделенный инженер поддержки
- Предварительно прогретые индексы (без задержки холодного старта)
Маршрутизация запросов
При очень высоком QPS интеллектуально маршрутизируйте запросы:
// Маршрутизация часто искомых запросов на «горячие» узлы
const ROUTING = {
hot: ["wireless headphones", "nike shoes", "iPhone"], // ускоренный кэш
warm: /^[a-z]{4,}$/i, // запросы средней длины → тёплые узлы
cold: /./, // всё остальное → пул по умолчанию
};
Управление жизненным циклом данных
Не все документы нуждаются в одинаковом приоритете поиска:
const lifecycle = {
active: { age: "< 90 дней", priority: "real-time", replica: 3 },
warm: { age: "90-365 дней", priority: "nearline", replica: 2 },
cold: { age: "> 365 дней", priority: "archive", replica: 1, demoted_boost: 0.5 },
};
Активные товары получают полные ресурсы. Старые товары остаются доступными для поиска, но ранжируются ниже с меньшим количеством реплик.
Сводка по масштабированию
| Этап | Документы | Стратегия | Стоимость в месяц | | ---------- | --------- | ---------------------------------------------------------------- | ----------------- | | Стартап | < 100K | Стандартная конфигурация, фокус на релевантности | $0–29 | | Рост | 100K–500K | Оптимизация полей, кэширование | $29–99 | | Масштаб | 500K–5M | Шардирование, пакетная индексация, курсорная пагинация | $99–249 | | Enterprise | 5M–50M | Выделенная инфраструктура, маршрутизация запросов, управление ЖЦ | Индивидуально |
Частые ошибки при масштабировании
| Ошибка | Симптом | Исправление | | ----------------------------- | ------------------------------------ | ------------------------------------------------------- | | Слишком много поисковых полей | Медленные запросы, большой индекс | Ограничить до 5–7 полей | | Отсутствие кэширования | Избыточная нагрузка на движок | Кэшировать топ-100 запросов на 60 сек | | Пагинация со смещением | Замедляющиеся запросы | Использовать курсорную пагинацию | | Огромные документы | Медленная индексация, большой индекс | Удалить неиспользуемые поля, уплотнить связанные данные | | Все фасеты без ограничений | Медленное вычисление фасетов | Ограничить до 10–15 значений на фасет | | Отсутствие плана шардирования | Сложно масштабировать за 5M | Спланировать стратегию шардирования до 1M документов |
Следующие шаги
- Провести бенчмарк своего индекса при текущем и прогнозируемом масштабе
- Просмотреть схему документов на предмет возможностей оптимизации
- Обсудить с нашей командой потребности в масштабировании