Прямой ответ
Поисковый индекс — производная структура. Источник данных остаётся системой записи, а индекс восстанавливается из источника, поэтому задача сводится к поддержанию соответствия между ними.
Три способа поддержания соответствия.
Полная переиндексация. Индекс перестраивается целиком из источника. Применяется при изменении маппинга и анализатора, а также как восстановление после сбоя.
Инкрементальное обновление. В индекс пишутся только изменённые документы. Требует определения изменения: по отметке времени обновления либо по журналу изменений базы.
Журнал изменений (CDC). Изменения читаются из журнала транзакций базы и применяются к индексу; о механизме — в статье про change data capture.
| Способ | Отставание | Стоимость | Когда применять |
|---|---|---|---|
| Полная переиндексация | Часы | Высокая | Изменение маппинга, восстановление |
| По отметке обновления | Минуты | Низкая | Простая схема, допустимое отставание |
| CDC по журналу | Секунды | Средняя | Низкое отставание, большой объём |
| Запись при изменении | Секунды | Низкая | Один сервис-владелец данных |
Запись в индекс при изменении данных в том же сервисе проста, но не атомарна: сбой между записью в базу и записью в индекс оставляет расхождение. Устранение требует исходящей очереди либо периодической сверки.
Переиндексация без простоя выполняется через алиас:
import httpx
ES = ""
httpx.put(f"{ES}/products-v2", json={
"mappings": {"properties": {"title": {"type": "text"}, "brand": {"type": "keyword"}}}
})
# перенос данных в products-v2 ...
httpx.post(f"{ES}/_aliases", json={"actions": [
{"remove": {"index": "products-v1", "alias": "products"}},
{"add": {"index": "products-v2", "alias": "products"}},
]})
Приложение обращается к алиасу, поэтому переключение индекса не требует изменения кода.
Отставание индекса и его последствия
Индекс не обновляется одновременно с источником, поэтому между ними существует окно расхождения. Величина окна определяет допустимые сценарии использования.
Поиск по каталогу. Отставание в минуты обычно незаметно, поскольку пользователь не ожидает немедленного появления изменённой позиции.
Поиск по наличию и цене. Отставание приводит к выдаче неактуальной цены, поэтому такие поля читаются из источника после нахождения документа, а не из индекса.
Поиск по правам доступа. Отставание означает доступ к документу, который пользователь не должен видеть. Поле прав проверяется при выдаче, а не только при индексации.
Практический приём: индекс используется для нахождения идентификаторов, а актуальные значения читаются из источника. Такой подход ограничивает последствия отставания, но добавляет запрос к источнику на каждую выдачу.
Отдельная задача — обнаружение расхождения. Периодическая сверка числа документов в источнике и индексе выявляет пропущенные обновления; о методике — в статье про отслеживание прогресса переноса.
При использовании журнала транзакций отставание определяется задержкой чтения журнала и скоростью применения изменений; о логической репликации — в статье про репликацию и миграцию PostgreSQL.
Практика: организация синхронизации индекса
1. Определяйте источник записи явно. Индекс не используется как источник истины, поэтому восстановление выполняется из базы.
2. Разделяйте полный и инкрементальный перенос. Полная переиндексация выполняется по расписанию либо при изменении схемы, инкрементальная — постоянно.
3. Используйте алиас вместо имени индекса. Переключение индекса выполняется одной операцией и не требует изменения кода приложения.
4. Проверяйте статус индексации. Ответ с идентификатором задачи означает принятие, а не завершение записи.
5. Обрабатывайте ошибки индексации отдельно от ошибок записи. Сбой записи в индекс не должен откатывать транзакцию в базе.
6. Ведите учёт отставания. Разница между временем последнего изменения в источнике и временем последней индексации — ключевая метрика.
7. Сверяйте объёмы периодически. Совпадение числа документов не гарантирует совпадения содержимого, но выявляет пропущенные записи.
8. Планируйте переиндексацию по времени. Перестройка индекса создаёт нагрузку, поэтому выполняется в период низкой активности.
Рекомендации:
- Не храните в индексе данные, которых нет в источнике. Восстановление индекса из источника теряет такие данные.
- Проверяйте актуальные значения при выдаче. Цена, наличие и права доступа читаются из источника, а не из индекса.
- Учитывайте стоимость полного переноса. Переиндексация миллионов документов занимает часы и создаёт нагрузку на источник.
- Используйте исходящую очередь для надёжности. Запись события об изменении в той же транзакции, что и изменение данных, устраняет потерю обновлений.
- Документируйте допустимое отставание. Значение определяется сценарием использования и фиксируется в требованиях.
Практический сценарий: пропавшие из поиска документы после сбоя
Контекст: часть товаров перестала находиться в поиске, хотя в базе присутствовала.
- Диагностика. Сверка числа документов показала расхождение: в источнике на несколько тысяч записей больше, чем в индексе. Расхождение накопилось за период сбоя сервиса индексации.
- Причина. Обновление индекса выполнялось в том же процессе, что и запись в базу, без очереди и повторных попыток. Сбой сервиса приводил к потере обновлений, а механизм восстановления отсутствовал.
- Действия. (а) Создали новый индекс с актуальным маппингом и выполнили полную переиндексацию из источника; (б) переключили алиас на новый индекс после проверки объёма; (в) добавили исходящую очередь: событие об изменении записывается в той же транзакции, что и изменение данных, а отдельный процесс применяет события к индексу; (г) включили повторные попытки при сбое индексации и очередь недоставленных сообщений для повторяющихся ошибок; (д) добавили метрику отставания и периодическую сверку объёмов.
- Результат. Потеря обновлений исключена записью события в транзакции, а расхождение обнаруживается метрикой до обращения пользователей.
Вывод: синхронизация индекса требует надёжного механизма доставки изменений, а не записи в том же процессе. Запись события в транзакции с изменением данных устраняет потерю обновлений, а полная переиндексация с переключением алиаса восстанавливает индекс без простоя поиска.
FAQ
Нужно ли останавливать поиск при переиндексации? Нет, новый индекс заполняется рядом с действующим, а переключение выполняется через алиас.
Как обнаружить расхождение между базой и индексом? Периодической сверкой числа документов и выборочной сверкой содержимого по идентификаторам.
Что делать при сбое записи в индекс? Повторять попытку, а при повторяющейся ошибке помещать событие в очередь недоставленных сообщений для разбора.
Можно ли использовать индекс как единственный источник данных? Нет. Индекс восстанавливается из источника, поэтому данные, существующие только в индексе, теряются при перестройке.
Источники: Elasticsearch index aliases, PostgreSQL logical decoding, Debezium documentation — ссылки в шапке.