⚡️Ускорение индексации денормализованных документов
В посте про подходы к поиску иерархичных данных был небольшой рассказ про денормализацию — это когда есть родительский документ { ticket } и есть дочерние { article_1 }, { article_2 }, { article_3 }. И далее мы уплощаем эту структуру в три документа, где child-ы обогащены данными от родителя
Псевдокод:
Такой подход позволяет добиться хорошего перфоманса при поиске (поскольку все необходимые данные находятся в рамках одного документа), но теперь нужно на каждое изменение родительского документа переиндексировать всех его детей
Какие есть способы ускорить такую индексацию в elasticsearch:
0. Наивный неэффективный способ
Получили эвент обновления ticket => запрашиваем все артиклы => строим n документов для обновления артиклов => делаем n запросов в эластик
Если у тикета 500 артиклов, то к примеру при изменении статуса тикета, нам нужно будет сделать 500 запросов на переиндексацию — звучит крайне неэффективно
Псевдокод:
1. Использование bulk update + partial update
Нам приходится делать кучу запросов, мы это можем решить с помощью использования bulk update, передав в одном запросе список того, что хотим переиндексировать
Вторая проблема — что приходится передавать полный документ, хотя у него изменился маленький кусочек. Эластик поддерживает partial update, и поэтому можно передать только тот кусок документа, который изменился
Псевдокод:
Важное НО: эластик делает shallow partial update, т.е. вложенные объекты обновляются целиком сразу, а не частично. К примеру, если бы мы захотели обновить вложенное поле
2. Использование update_by_query + script
Нивелируем проблемы предыдущего решения, а именно: shallow апдейты и то, что мы можем заранее не знать id-шники всех дочерних документов
Для этой цели есть update_by_query, который позволяет обновить документы, которые соответствуют некоторому условию. И обновить их с помощью скрипта
Псевдокод:
Такой подход идеально подходит для переиндексации дочерних документов, и позволяет делать максимально компактные запросы и обеспечивать гибкость с помощью кастомных скриптов (по дефолту на внутреннем языке эластика — painless)
В посте про подходы к поиску иерархичных данных был небольшой рассказ про денормализацию — это когда есть родительский документ { ticket } и есть дочерние { article_1 }, { article_2 }, { article_3 }. И далее мы уплощаем эту структуру в три документа, где child-ы обогащены данными от родителя
Псевдокод:
{ ticket, article_1 },
{ ticket, article_2 },
{ ticket, article_3 }
Такой подход позволяет добиться хорошего перфоманса при поиске (поскольку все необходимые данные находятся в рамках одного документа), но теперь нужно на каждое изменение родительского документа переиндексировать всех его детей
Какие есть способы ускорить такую индексацию в elasticsearch:
0. Наивный неэффективный способ
Получили эвент обновления ticket => запрашиваем все артиклы => строим n документов для обновления артиклов => делаем n запросов в эластик
Если у тикета 500 артиклов, то к примеру при изменении статуса тикета, нам нужно будет сделать 500 запросов на переиндексацию — звучит крайне неэффективно
Псевдокод:
query 1: { ticket: { all fields }, article { all fields }
query 2: { ticket: { all fields }, article { all fields }
query 3: { ticket: { all fields }, article { all fields }
...
1. Использование bulk update + partial update
Нам приходится делать кучу запросов, мы это можем решить с помощью использования bulk update, передав в одном запросе список того, что хотим переиндексировать
Вторая проблема — что приходится передавать полный документ, хотя у него изменился маленький кусочек. Эластик поддерживает partial update, и поэтому можно передать только тот кусок документа, который изменился
Псевдокод:
query: [
{ article_id: 1, ticket: { changed fields } }
{ article_id: 2, ticket: { changed fields } }
{ article_id: 3, ticket: { changed fields } }
...
]
Важное НО: эластик делает shallow partial update, т.е. вложенные объекты обновляются целиком сразу, а не частично. К примеру, если бы мы захотели обновить вложенное поле
ticket.custom_fields.some_field
, то нам мы пришлось полностью передать объект ticket. В противном случае мы бы потеряли данные2. Использование update_by_query + script
Нивелируем проблемы предыдущего решения, а именно: shallow апдейты и то, что мы можем заранее не знать id-шники всех дочерних документов
Для этой цели есть update_by_query, который позволяет обновить документы, которые соответствуют некоторому условию. И обновить их с помощью скрипта
Псевдокод:
query: {
condition: ticket_id = 123,
script: ticket.custom_fields.some_field = "abc"
}
Такой подход идеально подходит для переиндексации дочерних документов, и позволяет делать максимально компактные запросы и обеспечивать гибкость с помощью кастомных скриптов (по дефолту на внутреннем языке эластика — painless)