Моделирование данных в Elasticsearch не так очевидно, как при работе с реляционными базами данных. В отличие от традиционных реляционных баз данных, которые полагаются на нормализацию данных и соединения SQL, Elasticsearch требует альтернативных подходов к управлению отношениями.
Существует четыре распространенных обходных пути управления связями в Elasticsearch:
Объединения на стороне приложения
Денормализация данных
Вложенные типы полей и вложенные запросы
Отношения между родителями и детьми
В этом блоге мы обсудим, как можно разработать модель данных для обработки отношений с использованием вложенного типа поля и отношений «родитель-потомок». Мы рассмотрим архитектуру, влияние на производительность и варианты использования этих двух методов.
Elasticsearch поддерживает вложенные структуры, в которых объекты могут содержать другие объекты. Вложенные типы полей — это объекты JSON в основном документе, которые могут иметь свои собственные поля и типы. Эти вложенные объекты рассматриваются как отдельные скрытые документы, доступ к которым можно получить только с помощью вложенного запроса.
Вложенные типы полей хорошо подходят для отношений, где важны целостность данных, тесная связь и иерархическая структура. К ним относятся отношения «один-к-одному» и «один-ко-многим», где имеется одна основная сущность. Например, представление человека и его нескольких адресов и номеров телефонов в одном документе.
При использовании вложенных типов полей Elasticsearch хранит весь документ, а также родительские и вложенные объекты в одном блоке и сегменте Lucene. Это может привести к увеличению скорости выполнения запросов, поскольку связь содержится в документе.
Давайте рассмотрим пример записи в блоге с комментариями. Мы хотим разместить комментарии под публикацией в блоге, чтобы их можно было легко запрашивать вместе в одном документе.
{ "post_id": "1", "title": "Introduction to Elasticsearch Data Modeling", "content": "Exploring various data modeling options in Elasticsearch.", "comments": [ { "comment_id": "101", "text": "Great overview of data modeling!" }, { "comment_id": "102", "text": "Looking forward to more content." } ] }
Преимущества отношений вложенных объектов включают в себя:
Неэффективность обновления . Обновления, вставки и удаления в любой части документа с вложенными объектами требуют переиндексации всего документа, что может занимать много памяти, особенно если документы большие или обновления происходят часто.
Производительность запросов с большими вложенными полями . Если у вас есть документы с особенно большими вложенными полями, это может повлиять на производительность. Это связано с тем, что поисковый запрос извлекает весь документ.
Несколько уровней вложенности могут стать сложными . Выполнение запросов к вложенным структурам с несколькими уровнями по-прежнему может оказаться сложным. Это связано с тем, что запросы могут включать вложенные запросы внутри вложенных запросов, что приводит к ухудшению читаемости кода.
При сопоставлении родитель-потомок документы делятся на родительские и дочерние типы. Каждый дочерний документ имеет прямую связь с родительским документом. Эта связь устанавливается через определенное значение поля в дочернем документе, которое соответствует идентификатору родительского документа. Модель «родитель-потомок» использует децентрализованный подход, при котором родительские и дочерние документы существуют независимо.
Соединения родитель-потомок подходят для отношений «один ко многим» или «многие ко многим» между сущностями. Представьте себе приложение, в котором вы хотите создать связи между компаниями и контактами и хотите искать компании и контакты, а также контакты в конкретных компаниях.
Elasticsearch обеспечивает производительность соединений родитель-потомок, отслеживая, какие родители связаны с какими дочерними объектами, и располагая оба объекта в одном сегменте. Локализуя операцию соединения, Elasticsearch устраняет необходимость в обширной связи между сегментами, которая может стать узким местом в производительности.
Давайте возьмем пример отношений родитель-потомок для сообщений и комментариев в блоге. Каждое сообщение в блоге, то есть родительское, может иметь несколько комментариев, то есть дочерних. Чтобы создать связь родитель-потомок, давайте проиндексируем данные следующим образом:
PUT my-index-000001 { "mappings": { "properties": { "post_id": { "type": "keyword" }, "post_id": { "type": "join", "relations": { "post": "comment" } } } } }
Родительским документом будет сообщение, которое выглядит следующим образом:
{ "post_id": "1", "title": "Introduction to Elasticsearch Data Modeling", "content": "Exploring various data modeling options in Elasticsearch." }
Дочерний документ тогда будет комментарием, содержащим post_id, связывающий его с родительским документом.
{ "comment_id": "101", "text": "Great overview of data modeling!", "post_id": "1" }
Преимущества детско-родительского моделирования включают в себя:
Напоминает реляционную модель данных . В отношениях «родитель-потомок» родительский и дочерний документы разделены и связаны уникальным родительским идентификатором. Эта настройка ближе к модели реляционной базы данных и может быть более интуитивно понятной для тех, кто знаком с такими концепциями.
Эффективность обновления . Дочерние документы можно добавлять, изменять или удалять, не затрагивая родительский документ или другие дочерние документы. Это особенно полезно при работе с большим количеством дочерних документов, которые требуют частых обновлений. Обратите внимание, что связывание дочернего документа с другим родительским документом — более сложный процесс, поскольку новый родительский документ может находиться в другом сегменте.
Лучше подходит для разнородных дочерних документов : поскольку дочерние документы хранятся отдельно, они могут быть более эффективными в использовании памяти и хранилища, особенно в случаях, когда имеется много дочерних документов со значительными различиями в размерах.
К недостаткам детско-родительских отношений можно отнести:
Дорогие и медленные запросы . Объединение документов по разным индексам увеличивает вычислительную работу во время выполнения запроса, что снова влияет на производительность. Elasticsearch отмечает, что запросы родитель-потомок могут быть в 5-10 раз медленнее, чем запросы вложенных объектов.
Накладные расходы на сопоставление . Отношения «родитель-потомок» могут потреблять больше ресурсов памяти и кэша. Elasticsearch поддерживает карту отношений родитель-потомок, которая может разрастаться и занимать значительный объем памяти, особенно при большом объеме документов.
Управление размером сегментов . Поскольку родительские и дочерние документы находятся в одном сегменте, существует потенциальный риск неравномерного распределения данных по кластеру. Некоторые сегменты могут стать значительно больше других, особенно если есть родительские документы со многими дочерними элементами. Это может привести к проблемам в управлении и масштабировании кластера Elasticsearch .
Переиндексация и обслуживание кластера . Если вам необходимо переиндексировать данные или изменить стратегию сегментирования , отношения «родитель-потомок» могут усложнить этот процесс. Вам необходимо убедиться, что целостность отношений сохраняется во время таких операций. Регулярные задачи обслуживания кластера, такие как перебалансировка сегментов или обновление узлов, могут стать более сложными. Особое внимание необходимо уделять тому, чтобы во время этих процессов не нарушались отношения между родителями и детьми.
Elastic , компания, стоящая за Elasticsearch, всегда будет рекомендовать вам выполнять соединения на стороне приложения, денормализацию данных и/или вложенные объекты, прежде чем идти по пути отношений родитель-потомок.
В таблице ниже представлены характеристики вложенных типов полей и запросов, а также отношений «родитель-потомок» для параллельного сравнения подходов к моделированию данных.
| Вложенные типы полей и вложенные запросы | Отношения между родителями и детьми |
---|---|---|
Определение | Вкладывает объект в другой объект | Связывает родительские и дочерние документы вместе. |
Отношения | Один к одному, один ко многим | Один ко многим, многие ко многим |
Скорость запроса | Обычно быстрее, чем отношения родитель-потомок, поскольку данные хранятся в одном и том же блоке и сегменте. | Обычно в 5–10 раз медленнее, чем вложенные объекты, поскольку родительские и дочерние документы соединяются во время запроса. |
Гибкость запросов | Менее гибкие, чем запросы «родитель-потомок», поскольку ограничивают область запроса границами каждого вложенного объекта. | Обеспечивает большую гибкость при запросе, поскольку родительские или дочерние документы можно запрашивать вместе или по отдельности. |
Обновления данных | Обновление вложенных объектов потребовало переиндексации всего документа. | Обновлять дочерние документы проще, поскольку не требуется переиндексация всех документов. |
Управление | Более простое управление, поскольку все содержится в одном документе. | Более сложное управление из-за раздельной индексации и поддержания связей между родительскими и дочерними документами. |
Случаи использования | Храните и запрашивайте сложные данные с несколькими уровнями иерархии. | Отношения, в которых мало родителей и много детей, например продукты и обзоры продуктов. |
Хотя Elasticsearch предоставляет несколько обходных путей для соединений в стиле SQL , включая вложенные запросы и отношения родитель-потомок, установлено, что эти модели плохо масштабируются. При разработке масштабных приложений может иметь смысл рассмотреть альтернативный подход с собственными возможностями соединения SQL — Rockset .
Rockset — это база данных поиска и аналитики, предназначенная для поиска SQL, агрегирования и объединения любых данных, включая глубоко вложенные данные JSON. Когда данные передаются в Rockset, они кодируются в основных структурах данных базы данных, используемых для хранения и индексации данных для быстрого поиска. Rockset индексирует данные таким образом, чтобы можно было выполнять быстрые запросы, включая соединения, используя свой оптимизатор запросов на основе SQL. В результате для поддержки SQL-соединений не требуется предварительное моделирование данных.
Одна из проблем Elasticsearch заключается в том, как эффективно сохранить взаимосвязь при обновлении данных. Одна из причин заключается в том, что Elasticsearch построен на Apache Lucene, который хранит данные в неизменяемых сегментах, в результате чего все документы необходимо переиндексировать. Rockset использует RocksDB, хранилище ключей-значений с открытым исходным кодом Meta и созданное для изменения данных, чтобы иметь возможность эффективно поддерживать обновления на уровне полей без необходимости переиндексации целых документов.
Давайте сравним подход к отношениям родитель-потомок в Elasticsearch с запросом SQL в Rockset.
В приведенном выше примере отношений родитель-потомок мы смоделировали сообщения с несколькими комментариями, создав два типа документов:
сообщения или тип родительского документа
комментарии или дочерние типы документов
Мы использовали уникальный идентификатор, родительский идентификатор, чтобы установить связь между родительским и дочерним документами. Во время запроса мы используем Elasticsearch DSL для получения комментариев к определенному сообщению.
В Rockset данные, содержащие сообщения, будут храниться в одной коллекции, таблице в реляционном мире, а данные, содержащие комментарии, будут храниться в отдельной коллекции. Во время запроса мы объединяли данные вместе с помощью SQL-запроса.
Вот два подхода бок о бок:
POST /blog/posts/1 { "title": "Elasticsearch Modeling", "content": "A post about data modeling in Elasticsearch" } POST /blog/comments/2?parent=1 { "text": "Great post!" } POST /blog/comments/3?parent=1 { "text": "I learned a lot from this." }
Чтобы получить сообщение по его заголовку и всем комментариям, вам необходимо создать запрос следующим образом.
GET /posts/_search { "query": { "bool": { "must": [ { "match": { "title": "Exploring Elasticsearch Models" } } ] } }, "inner_hits": { "_source": ["text"], "name": "comments", "path": "comments" } }
Чтобы затем запросить эти данные, вам просто нужно написать простой запрос SQL.
SELECT p.title, p.content, c.text FROM posts p JOIN comments c ON p.post_id = c.post_id WHERE p.post_id = 1;
Если у вас есть несколько наборов данных, которые необходимо объединить для вашего приложения, Rockset является более простым и масштабируемым, чем Elasticsearch. Это также упрощает операции, поскольку вам не нужно переделывать данные, управлять обновлениями или выполнять операции переиндексации.
В этом блоге представлен обзор типов вложенных полей и вложенных запросов, а также отношений «родитель-потомок» в Elasticsearch с целью помочь вам определить лучший подход к моделированию данных для вашей рабочей нагрузки.
Типы вложенных полей и запросы полезны для отношений «один к одному» или «один ко многим», когда отношения поддерживаются в одном документе. Это считается более простым и масштабируемым подходом к управлению взаимоотношениями.
Модель отношений «родитель-потомок» лучше подходит для отношений «один-ко-многим» и «многие-ко-многим», но она отличается повышенной сложностью, особенно потому, что отношения должны ограничиваться конкретным сегментом.
Если одним из основных требований вашего приложения является моделирование отношений, возможно, имеет смысл рассмотреть Rockset. Rockset упрощает моделирование данных и предлагает более масштабируемый подход к управлению отношениями с использованием SQL-соединений. Вы можете сравнить и сравнить производительность Elasticsearch и Rockset , запустив бесплатную пробную версию с кредитами в размере 300 долларов США сегодня.