elasticsearch (семь) --- глубокая фрагментация

задняя часть база данных Elasticsearch Lucene

В распределенных кластерах мы ввели сегментирование, описав его как базовую единицу работы. Но что такое шардинг и как он работает? В этой главе мы ответим на следующие вопросы:

为什么搜索是近实时的?
为什么文档的CRUD操作是实时的?
ES怎样保证更新持久化,即使断电也不会丢失?
为什么删除文档不会立即释放空间?
什么是refresh,flush, optimize API,以及什么时候你该使用它们?

Самый простой способ понять, как работает шардинг, — начать с урока истории. Мы рассмотрим, какие проблемы необходимо решить, чтобы обеспечить распределенную, постоянную поисковую систему с возможностями поиска и анализа в режиме, близком к реальному времени.

сделать текст доступным для поиска

Первая проблема, которую нужно было решить, заключалась в том, как сделать текст доступным для поиска. В традиционных базах данных одно поле содержит одно значение, но этого недостаточно для полнотекстового поиска. Сделать каждое слово в тексте доступным для поиска означает, что база данных должна хранить несколько значений.

Лучшая структура данных для поддержки нескольких значений поля — инвертированный индекс. Инвертированный индекс содержит упорядоченный список уникальных значений или слов, встречающихся во всех документах, а также список документов, к которым относится каждое слово.

 Term  | Doc 1 | Doc 2 | Doc 3 | ...
 ------------------------------------
 brown |   X   |       |  X    | ...
 fox   |   X   |   X   |  X    | ...
 quick |   X   |   X   |       | ...
 the   |   X   |       |  X    | ...

Инвертированный индекс хранит больше информации, чем список документов, содержащих тот или иной термин. В нем может храниться количество документов, содержащих каждый термин, частота появления термина в данном документе, порядок терминов в каждом документе, длина каждого документа, средняя длина всех документов и т. д. Эта статистика позволяет Elasticsearch узнать, какие термины важнее и какие документы важнее, т. е. релевантность.

Имейте в виду, что для того, чтобы инвертированный индекс функционировал должным образом, он должен знать все документы в коллекции.

На заре полнотекстового поиска для всей коллекции документов создавался большой индекс, который записывался на диск. Только когда новый индекс будет готов, он заменит старый и можно будет получить самые последние изменения.

неизменность

Записанный на диск инвертированный индекс является неизменяемым и имеет следующие преимущества:

1.不需要锁。如果从来不需要更新一个索引,就不必担心多个程序同时尝试修改。
2.一旦索引被读入文件系统的缓存(译者:在内存),它就一直在那儿,因为不会改变。
只要文件系统缓存有足够的空间,大部分的读会直接访问内存而不是磁盘。这有助于性能提升。
3.在索引的声明周期内,所有的其他缓存都可用。它们不需要在每次数据变化了都重建,因为数据不会变。
4.写入单个大的倒排索引,可以压缩数据,较少磁盘IO和需要缓存索引的内存大小。

Конечно, у неизменяемых индексов есть свои недостатки, первый из которых заключается в том, что они неизменяемы! Вы не можете изменить это. Если вы хотите найти новый документ, вы должны повторно просмотреть весь индекс. Это серьезно ограничивает не только данные, которые может содержать индекс, но и частоту обновления индекса.

динамический индекс

Следующая проблема, которую необходимо решить, заключается в том, как обновить инвертированный индекс, сохранив при этом преимущества неизменности. ответ,Используйте несколько индексов.

Вместо перезаписи всего инвертированного индекса добавляются дополнительные индексы, отражающие последние изменения. Каждый инвертированный индекс можно запрашивать по порядку, начиная с самого старого, и результаты агрегируются последними.

Lucene, базовая зависимость Elasticsearch, вводитper-segment searchКонцепция чего-либо. сегмент (segment) является полностью функциональным инвертированным индексом, но теперь индекс в Lucene относится к набору сегментов плюс точка фиксации (commit point, включая все файлы сегментов), как показано на рисунке 1. Новый документ сначала записывается в индексный кэш области памяти, а затем в сегмент диска, как показано на рисунках 2 и 3.

Рисунок 1: Lucene с одной точкой фиксации и тремя индексами

索引vs分片
为了避免混淆,需要说明,Lucene索引是Elasticsearch中的分片,Elasticsearch中的索引是分片的集合。
当Elasticsearch搜索索引时,它发送查询请求给该索引下的所有分片,然后过滤这些结果,聚合成全局的结果。

Одинper-segment searchРаботает следующим образом:

1.新的文档首先写入内存区的索引缓存。
2.不时,这些buffer被提交:
    一个新的段——额外的倒排索引——写入磁盘。
    新的提交点写入磁盘,包括新段的名称。
    磁盘是fsync(文件同步)——所有写操作等待文件系统缓存同步到磁盘,确保它们可以被物理写入。
3.新段被打开,它包含的文档可以被检索
4.内存的缓存被清除,等待接受新的文档。

Рис. 2. Буфер в памяти содержит индекс предстоящих документов Lucene.

Рисунок 3: После фиксации новый сегмент добавляется в точку фиксации, а кеш очищается

Когда запрос принят, все сегменты опрашиваются по очереди. Статистика терминов по всем сегментам агрегируется, что обеспечивает корректный расчет релевантности каждого термина и документа. Таким образом, новые документы добавляются в индекс по низкой цене.

удалить и обновить

Сегменты неизменяемы, поэтому документы нельзя ни удалить из старых сегментов, ни обновить старые сегменты, чтобы отразить последнюю версию документа. Вместо этого каждая точка фиксации включает файл .del, содержащий удаленные документы в сегменте.

Когда документ удаляется, он просто помечается для удаления в файле .del и по-прежнему соответствует запросу, но удаляется из результатов до того, как будет окончательно возвращен.

Операции обновления документа аналогичны: при обновлении документа старая версия документа помечается для удаления, а новая версия документа индексируется в новом сегменте. Возможно, разные версии документа будут соответствовать запросу, но более старые версии будут удалены из результатов.

поиск почти в реальном времени

потому чтоper-segment searchМеханизм, существует задержка между индексированием и поиском документа. Новые документы будут доступны для поиска в течение нескольких минут, но это все еще недостаточно быстро.

Диск является узким местом. Требуется зафиксировать новый сегмент на дискеfsyncоперации, гарантируя, что сегмент физически записывается на диск без потери данных в случае сбоя питания. ноfsyncстоит дорого, его нельзя запускать каждый раз, когда документ индексируется.

Таким образом, существует потребность в более легком способе сделать новые документы доступными для поиска, что означает удаление этогоfsync.

Между Elasticsearch и диском находится кеш файловой системы. Как упоминалось ранее, документы в кеше индексов в памяти (рис. 1) записываются в новые сегменты (рис. 2), но новые сегменты сначала записываются в кеш файловой системы, что является дешевым, а затем синхронизируются с диском. отличная цена. Но как только файл кэшируется, его также можно открыть и прочитать, как и любой другой файл.

Рисунок 1: Индекс Lucene с новыми документами в буфере памяти

Lucene позволяет открывать новые сегменты, чтобы содержащиеся в них документы были доступны для поиска без выполнения полной фиксации. Это более легкий процесс, чем фиксация, и его можно использовать часто, не влияя на производительность.

Рисунок 2. Кэшированное содержимое было записано в сегмент, но еще не зафиксировано

refeash API

В Elasticsearch этот легкий процесс записи для открытия нового сегмента называется обновлением. По умолчанию каждый сегмент автоматически обновляется каждую секунду. Вот почему Elasticsearch — это поиск почти в реальном времени: поиск изменений в документах происходит не сразу, а становится видимым в течение секунды.

Это смущает новых пользователей: они индексируют документ, пытаются его найти, но не могут найти. Решение состоит в том, чтобы выполнить ручное обновление через API:

POST /_refresh <1>
POST /blogs/_refresh <2>

обновить все индексы

Обновлять только индексированные блоги

Не всем пользователям нужно обновлять каждую секунду. Возможно, вы используете ES для индексации миллионов файлов журналов и хотите оптимизировать скорость индексации, а не поиск в реальном времени. Вы можете уменьшить частоту обновления, изменив элемент конфигурации refresh_interval:

  PUT /my_logs
{
  "settings": {
    "refresh_interval": "30s" <1>
  }
}

обновлять my_logs каждые 30 секунд

refresh_intervalМожет динамически обновляться в существующих индексах. Вы можете отключить автоматическое обновление при создании большого индекса и снова включить его, когда захотите использовать индекс.

PUT /my_logs/_settings
{ "refresh_interval": -1 } <1>

PUT /my_logs/_settings
{ "refresh_interval": "1s" } <2>

Отключить все автоматические обновления

Автоматическое обновление каждую секунду

постоянные изменения

бесполезныйfsyncСинхронизируя кеш файловой системы с диском, мы не можем обеспечить сохранность данных даже после сбоя питания или даже обычного выхода из приложения. Для надежности ES необходимо убедиться, что изменения сохраняются на диск.

Мы говорили, что при полной фиксации сегмента синхронизации на диск записывается точка фиксации, в которой будут перечислены все известные сегменты. При перезапуске или повторном открытии индекса ES использует эту точку фиксации, чтобы определить, какие сегменты принадлежат текущему сегменту.

Несмотря на то, что мы получаем поиск почти в реальном времени с обновлениями каждую секунду, нам по-прежнему необходимо периодически выполнять полные фиксации, чтобы обеспечить восстановление после сбоев. Но как насчет документации между коммитами? Мы тоже не хотим их терять.

ES добавляет ведение журнала транзакций (translog) для записи каждой операции. Теперь с журналом транзакций процесс выглядит следующим образом:

1. Когда документ индексируется, он добавляется в кэш в памяти и добавляется в журнал транзакций.

Рисунок 1: Новые документы добавляются в кэш в памяти и записываются в журнал транзакций

2. При обновлении осколок переходит в состояние, описанное на следующем рисунке. Обновляйте каждый второй осколок:

  • Документ буфера памяти записывается в сегмент, но fsync нет.
  • Сегмент открывается, что делает новые документы доступными для поиска.
  • кэш очищен

Рис. 2. После обновления кэш очищается, но журнал транзакций не очищается.

3. Этот процесс будет продолжаться по мере добавления новых документов в кэш и записи в журнал.

Рисунок 3. В журнале транзакций регистрируется растущий объем документов

4. Время от времени, например, когда журнал очень большой, будет создаваться новый журнал и выполняться полная фиксация:

  • Все документы в буфере памяти записываются в новый сегмент.
  • очистить кэш
  • Точка фиксации записывается на диск
  • Кэш файловой системы сбрасывается на жесткий диск с помощью операции fsync.
  • Журнал транзакций очищен

В журнал транзакций записываются все операции, которые не сбрасываются на диск. Когда сбой повторится, ES восстановит все известные сегменты жесткого диска с самой последней точкой фиксации и восстановит все операции из журнала.

Журнал транзакций также используется для обеспечения операций CRUD в реальном времени. Когда вы пробуете CRUD с идентификаторами, он сначала проверяет журнал на наличие последних изменений, прежде чем извлекать документы в соответствующем сегменте. Это означает, что ES может получить последнюю версию документа в режиме реального времени.

Рисунок 4. После сброса сегмент полностью фиксируется, а журнал транзакций очищается.

flush API

В ES операция совершения коммита и удаления журнала транзакций называетсяflush. Фрагментация происходит каждые 30 минут или если журнал транзакций слишком велик.flushработать.

API сброса можно использовать для выполнения сброса вручную:

POST /blogs/_flush <1> 
POST /_flush?wait_for_ongoing  <2>

очистить индекс блогов

очистить все индексы, дождаться завершения операции перед возвратом

Вам редко нужно промывать вручную, обычно достаточно автоматического.

Когда вы хотите перезапустить или закрыть индекс, полезно сбросить индекс. Когда ES пытается восстановить или повторно открыть индекс, он должен воспроизвести все операции в журнале транзакций, поэтому чем меньше журнал, тем быстрее восстановление.

Объединить сегменты

Создавая новые сегменты с автоматическим обновлением каждую секунду, быстро увеличивается количество сегментов. Наличие слишком большого количества сегментов является проблемой. Каждый сегмент потребляет дескрипторы файлов, память и ресурсы процессора. Что еще более важно, каждый поисковый запрос должен проверять каждый сегмент по очереди. Чем больше сегментов, тем медленнее выполняется запрос.

ES решает эту проблему, объединяя сегменты в фоновом режиме. Маленькие сегменты объединяются в более крупные сегменты, которые затем объединяются в более крупные сегменты.

Это когда старые документы удаляются из файловой системы. Старые сегменты не копируются в новые сегменты большего размера.

Вам не нужно ничего делать с этим процессом. ES обрабатывает это автоматически при индексировании и поиске. Этот процесс показан на рисунке: два зафиксированных сегмента и один незафиксированный сегмент объединяются в один больший сегмент:

1. В процессе индексации обновление создаст новый сегмент и откроет его.

2. Процесс слияния выберет несколько небольших сегментов для объединения в большие сегменты в фоновом режиме, и этот процесс не будет прерывать индексацию и поиск.

Рис. 1. Два зафиксированных сегмента и один незафиксированный сегмент объединены в один больший сегмент

3. На следующем рисунке показана комбинированная операция:

  • Новый сегмент сбрасывается на жесткий диск.
  • Новая точка фиксации записывает новый сегмент, исключая старый сегмент.
  • Откроется новый сегмент для поиска.
  • Старые сегменты удаляются.

Рис. 2. После объединения сегментов старые сегменты удаляются.

Объединение больших сегментов потребляет много ресурсов ввода-вывода и ресурсов ЦП, и если этот флажок не установлен, это повлияет на производительность поиска. По умолчанию ES ограничивает процесс слияния, чтобы поиск мог иметь достаточно ресурсов для продолжения.

optimize API

Оптимизировать API лучше всего можно описать как API принудительного слияния сегментов. Это заставляет осколки объединять сегменты, чтобы достичь указанногоmax_num_segmentsпараметр. Это делается для уменьшения количества сегментов (обычно 1) для повышения эффективности поиска.

警告
不要在动态的索引(正在活跃更新)上使用optimize API。
后台的合并处理已经做的很好了,优化命令会阻碍它的工作。不要干涉!

В определенных обстоятельствах API оптимизации полезен. Типичным сценарием является запись журналов, и в этом случае журналы индексируются по дням, неделям и месяцам. Старые индексы, как правило, доступны только для чтения, их невозможно изменить. В этом случае эффективно уменьшить сегмент каждого индекса до 1. Процесс поиска будет использовать меньше ресурсов и работать лучше:

POST /logstash-2014-10/_optimize?max_num_segments=1 <1>

Объединить каждый сегмент в индексе в сегмент

Ссылаться на:es Полное руководство