Реиндексация данных
Реиндексация - процесс переноса документов из одного индекса Smart Monitor Data Storage в другой при помощи API операции _reindex.
Данная операция используется, когда нужно изменить структуру хранения или обработки данных без ручной выгрузки и повторной загрузки документов.
Реиндексация не изменяет исходный индекс и не переносит автоматически его settings, mappings, aliases и index template. Целевой индекс рекомендуется создавать заранее, чтобы явно зафиксировать нужную схему и настройки.
Термины
| Термин | Описание |
|---|---|
| Исходный индекс | Индекс, из которого копируются документы |
| Целевой индекс | Новый индекс, в который записываются документы |
| Alias | Логическое имя индекса. Позволяет переключить приложение на новый индекс без изменения его конфигурации |
| Ingest pipeline | Набор процессоров, применяемых к документам при записи |
| Slice | Часть задачи реиндексации. Используется для параллельной обработки больших индексов |
| Task | Фоновая задача Smart Monitor Data Storage, если _reindex запущен асинхронно |
Когда требуется реиндексация
- изменение типа поля в
mapping - изменение анализаторов, нормализаторов или количества
primary shard - переименование индекса
- переименование, удаление или преобразование полей
- объединение нескольких индексов в один
- копирование части данных по фильтру
- применение
ingest pipelineк уже существующим документам
Некоторые изменения, например добавление нового поля в mapping, можно выполнить без реиндексации. Перед запуском операции необходимо проверить, действительно ли требуется перенос данных.
Предварительные требования
Перед началом работ необходимо убедиться, что:
- есть доступ к Dev Tools,
curlили другому HTTP-клиенту - у пользователя есть права на чтение исходного индекса и запись в целевой
- на диске достаточно места для временного хранения двух копий данных
- кластер находится в состоянии
greenили допустимом для обслуживанияyellow - создан snapshot или другой способ восстановления данных (при необходимости)
Общая схема без простоя
Рекомендуемый сценарий для production с использованием alias:
- Создать новый индекс с новой версией имени, например
orders-v2 - Скопировать данные из текущего индекса через
_reindex - Проверить количество документов, выборочные документы, mapping и поисковые запросы
- Остановить или ограничить запись в старый индекс на короткое время
- Досинхронизировать документы, которые изменились во время основной реиндексации
- Атомарно переключить alias со старого индекса на новый
- Проверить приложение
- Удалить старый индекс только после периода наблюдения
Переключение alias выполняется одним запросом, чтобы в любой момент времени alias указывал ровно на один индекс:
POST _aliases
{
"actions": [
{ "remove": { "index": "old-index", "alias": "index-alias" } },
{ "add": { "index": "new-index", "alias": "index-alias" } }
]
}
Переиндексация всех документов
1. Создание целевого индекса
Сначала необходимо создать целевой индекс с требуемой структурой полей (маппингом) и настройками. Их можно задать вручную или скопировать из исходного индекса.
При создании временных индексов рекомендуется устанавливать количество реплик (number_of_replicas) в 0 на время реиндексации. После завершения необходимо вернуть исходное значение.
Не стоит полагаться на автоматическое создание индекса во время _reindex - Smart Monitor Data Storage создаст его с динамическим mapping, который может не соответствовать требуемой схеме.
PUT <index-name>
{
"mappings": {
... // Укажите нужный маппинг
},
"settings": {
... // Укажите нужные настройки
}
}
2. Выполнение операции Reindex
Для небольших индексов подходит синхронный запуск:
POST _reindex
{
"source":{
"index":"source"
},
"dest":{
"index":"<index-name>"
}
}
Для больших индексов необходимо использовать асинхронный запуск:
POST _reindex?wait_for_completion=false&slices=auto&requests_per_second=1000
{
"source": {
"index": "source",
"size": 1000
},
"dest": {
"index": "<index-name>",
"op_type": "create"
},
"conflicts": "proceed"
}
Параметры запуска:
| Параметр | Назначение |
|---|---|
wait_for_completion=false | Запускает операцию в фоне и возвращает task_id |
slices=auto | Включает автоматическое параллельное выполнение |
requests_per_second | Ограничивает скорость, чтобы снизить нагрузку на кластер |
source.size | Размер batch для чтения документов |
dest.op_type=create | Не перезаписывает существующие документы в целевом индексе |
conflicts=proceed | Продолжает выполнение при конфликте версий или существующих _id |
Выборочная реиндексация документов
Операция _reindex позволяет копировать не весь индекс, а только документы, соответствующие поисковому запросу.
По условию
POST _reindex
{
"source":{
"index":"source",
"query": {
"match": {
"field_name": "text"
}
}
},
"dest":{
"index":"<index-name>"
}
}
Полный перечень доступных операций приведен в официальной документации OpenSearch.
Только отдельные поля
POST _reindex
{
"source": {
"index": "source",
"_source": [
"field_1",
"field_2",
"field_3"
]
},
"dest": {
"index": "<index-name>"
}
}
Объединение нескольких индексов
Чтобы объединить документы из нескольких индексов в один, необходимо указать индексы-источники в виде списка.
POST _reindex
{
"source":{
"index":[
"source_1",
"source_2"
]
},
"dest":{
"index":"destination"
}
}
Необходимо убедиться, что количество шардов в исходных и целевом индексах совпадает. В противном случае операция может завершиться ошибкой.
Преобразование документов во время реиндексации
Способ 1: Скрипт
Для простых преобразований используется раздел script. Рекомендуемый язык - Painless.
Пример: переименование поля client_id в customer_id
POST _reindex
{
"source": {
"index": "source"
},
"dest": {
"index": "<index-name>"
},
"script": {
"source": "ctx._source.customer_id = ctx._source.remove('client_id')"
}
}
Способ 2: Ingest pipeline
Для более сложных преобразований используется ingest pipeline.
- Сначала необходимо создать pipeline с нужными процессорами:
PUT _ingest/pipeline/pipeline-test
{
"description": "Преобразует текстовое поле в список. Вычисляет длину поля 'word' и сохраняет ее в новом поле 'word_count'. Удаляет поле 'test'",
"processors": [
{
"split": {
"field": "text",
"separator": "\\s+",
"target_field": "word"
}
},
{
"script": {
"lang": "painless",
"source": "ctx.word_count = ctx.word.length"
}
},
{
"remove": {
"field": "test"
}
}
]
}
- Затем необходимо указать pipeline в
dest:
POST _reindex
{
"source": {
"index": "source"
},
"dest": {
"index": "<index-name>",
"pipeline": "pipeline-test"
}
}
Обновление документов в текущем индексе
Для обновления данных непосредственно в текущем индексе без создания нового используется операция update_by_query.
Особенности операции:
- выполняется методом
POST - одновременно может работать только с одним индексом
POST <index_name>/_update_by_query
Если запустить эту команду без параметров, она увеличит номер версии для всех документов в указанном индексе.
Отслеживание выполнения
GET _tasks?actions=*reindex*&detailed=true
GET _tasks/<node_id>:<task_id>
POST _tasks/<node_id>:<task_id>/_cancel
Признаки, что нагрузку необходимо снизить:
- растет latency поисковых или write-запросов
- появляются rejected-запросы в thread pool
- растет JVM heap pressure
- диск приближается к flood-stage watermark
- кластер долго находится в
redили нестабильномyellow
В таком случае необходимо отменить задачу и перезапустить её с меньшим requests_per_second или количеством slices.
Rollback
Если после переключения обнаружена проблема, необходимо вернуть alias на старый индекс:
POST _aliases
{
"actions": [
{ "remove": { "index": "new-index", "alias": "index-alias" } },
{ "add": { "index": "old-index", "alias": "index-alias" } }
]
}
Если после переключения в новый индекс уже появились новые записи, перед rollback необходимо определить, нужно ли переносить их обратно. Без этого возможна потеря последних изменений на уровне приложения.
Ограничения и риски
_reindexкопирует документы, но не переносит автоматически настройки индекса, шаблоны и alias- изменения в исходном индексе во время операции не блокируются
- при активной записи требуется стратегия досинхронизации
- большая реиндексация создает нагрузку на disk I/O, CPU, heap и thread pools
- при нехватке диска индекс может перейти в read-only из-за watermark
- скрипты и pipeline могут замедлить операцию
- ошибки mapping в целевом индексе приведут к rejected-документам
conflicts=proceedпропускает конфликты, но не исправляет их причину
Параметры исходного индекса
| Параметр | Допустимые значения | Описание | Обязательный |
|---|---|---|---|
index | String | Имя исходного индекса. Можно указать несколько индексов в виде списка. | ДА |
max_docs | Integer | Максимальное количество документов для переиндексации. | НЕТ |
query | Object | Поисковый запрос для выбора документов при операции переиндексации. | НЕТ |
size | Integer | Количество документов для переиндексации. | НЕТ |
slice | String | Задает ручную или автоматическую параллелизацию (слайсинг) для ускорения процесса переиндексации. | НЕТ |
Параметры целевого индекса
| Параметр | Допустимые значения | Описание | Обязательный |
|---|---|---|---|
index | String | Имя целевого индекса. | ДА |
version_type | Enum | Тип контроля версий для операции индексации. Допустимые значения: internal, external, external_gt, external_gte. | НЕТ |
Чек-лист перед запуском
- создан snapshot или подтвержден другой способ восстановления (при необходимости)
- проверено состояние кластера
- создан целевой индекс с нужными
settingsиmappings - выполнена тестовая реиндексация
- оценено место на диске
- определена стратегия обработки записей во время миграции
- подготовлен rollback через alias
Чек-лист после запуска
- задача
_reindexзавершилась без критичных ошибок - количество документов сверено
- проверены выборочные документы
- возвращены production-настройки индекса
- alias переключен одним атомарным запросом
- приложение проверено после переключения
- старый индекс оставлен на период наблюдения