Обычную таблицу можно создать из внешнего источника с помощью специального инструмента под названием indexer, который читает "рецепт" из конфигурации, подключается к источникам данных, извлекает документы и строит файлы таблицы. Это длительный процесс. Если ваши данные изменяются, таблица устаревает, и её нужно перестраивать из обновлённых источников. Если данные изменяются инкрементально, например, в блоге или новостной ленте, где старые документы не меняются, а добавляются только новые, перестройка будет занимать всё больше времени, так как вам придётся снова и снова обрабатывать архивные источники при каждом проходе.
Один из способов решения этой проблемы — использовать несколько таблиц вместо одной большой. Например, можно обработать источники, созданные в предыдущие годы, и сохранить таблицу. Затем взять только источники текущего года и поместить их в отдельную таблицу, перестраивая её так часто, как необходимо. После этого обе таблицы можно объединить в распределённую таблицу и использовать её для запросов. Суть в том, что при каждой перестройке вы обрабатываете данные не старше 12 месяцев, а таблица с более старыми данными остаётся без изменений и не требует перестройки. Можно пойти дальше и разделить таблицу за последние 12 месяцев на месячные, недельные или дневные таблицы и так далее.
Этот подход работает, но вам нужно вручную поддерживать распределённую таблицу. То есть добавлять новые части, удалять старые и следить, чтобы общее количество частичных таблиц не было слишком большим (при слишком большом количестве таблиц поиск может замедлиться, а ОС обычно ограничивает число одновременно открытых файлов). Для решения этой задачи можно вручную объединять несколько таблиц, запуская indexer --merge. Однако это решает только проблему большого количества таблиц, усложняя обслуживание. И даже при переиндексации «по часам» у вас, скорее всего, будет заметный временной разрыв между появлением новых данных в источниках и перестройкой таблицы, которая делает эти данные доступными для поиска.
Таблица в реальном времени создана для решения этой проблемы. Она состоит из двух частей:
- Специальной таблицы в оперативной памяти (называемой RAM chunk), которая содержит части данных, поступающих прямо сейчас.
- Набора обычных таблиц, называемых disk chunks, которые были построены ранее.
Это очень похоже на стандартную распределённую таблицу, состоящую из нескольких локальных таблиц.
Вам не нужно строить такую таблицу, запуская indexer, который читает "рецепт" из конфигурации и источники данных таблиц. Вместо этого таблица в реальном времени предоставляет возможность «вставлять» новые документы и «заменять» существующие. При выполнении команды 'insert' вы отправляете новые документы на сервер. Он строит небольшую таблицу из добавленных документов и сразу же выводит её в онлайн. Таким образом, сразу после завершения команды 'insert' вы можете выполнять поиск во всех частях таблицы, включая только что добавленные документы.
Сервер поиска автоматически поддерживает таблицу, так что вам не нужно об этом беспокоиться. Однако вам может быть интересно узнать некоторые детали о том, «как она поддерживается».
Во-первых, поскольку индексированные данные хранятся в оперативной памяти — что будет при аварийном отключении питания? Потеряю ли я тогда таблицу? Перед завершением сервер сохраняет новые данные в специальный «binlog». Это один или несколько файлов, находящихся на вашем постоянном хранилище, которые инкрементально растут по мере добавления изменений. Вы можете настроить поведение относительно того, как часто новые запросы (или транзакции) сохраняются в binlog и как часто выполняется команда 'sync' для binlog-файла, чтобы заставить ОС действительно сохранить данные на надёжном носителе. Самый параноидальный подход — сбрасывать и синхронизировать после каждой транзакции. Это самый медленный, но и самый безопасный метод. Самый дешёвый способ — полностью отключить binlog. Это самый быстрый метод, но вы рискуете потерять индексированные данные. Также предусмотрены промежуточные варианты, например, сброс/синхронизация каждую секунду.
Binlog предназначен специально для последовательного сохранения новых транзакций; это не таблица и по нему нельзя выполнять поиск. Это всего лишь страховка, гарантирующая, что сервер не потеряет ваши данные. Если произойдёт внезапный сбой и всё упадёт из-за программной или аппаратной ошибки, сервер загрузит самый свежий доступный дамп RAM chunk и затем воспроизведёт binlog, повторяя сохранённые транзакции. В итоге он достигнет того же состояния, в котором находился в момент последнего изменения.
Во-вторых, как насчёт ограничений? Что если я хочу обработать, скажем, 10 ТБ данных, но они просто не помещаются в оперативную память! Оперативная память для таблицы в реальном времени ограничена и может быть настроена. Когда индексируется определённый объём данных, сервер управляет RAM-частью таблицы, объединяя маленькие транзакции, чтобы их количество и общий размер оставались небольшими. Однако этот процесс иногда может вызывать задержки при вставке. Когда объединение уже не помогает, и новые вставки достигают лимита RAM, сервер преобразует таблицу в оперативной памяти в обычную таблицу на диске (называемую disk chunk). Эта таблица добавляется в набор таблиц второй части RT-таблицы и становится доступной онлайн. Затем RAM очищается, и пространство освобождается.
Когда данные из RAM надёжно сохранены на диск, что происходит:
- когда сервер сохраняет собранные данные как дисковую таблицу
- или когда он сбрасывает RAM-часть при корректном завершении работы или с помощью ручного сброса
binlog для этой таблицы больше не нужен. Поэтому он удаляется. Если все таблицы сохранены, binlog будет удалён.
Третье, как насчёт сбора дисков? Если наличие множества частей диска замедляет поиск, в чём разница, если я создаю их вручную в виде распределённой таблицы, или они создаются как части диска (или «чанки») таблицей RT? В обоих случаях вы можете объединить несколько таблиц в одну. Например, можно объединить почасовые таблицы за вчерашний день и вместо этого сохранить одну «суточную» таблицу за вчера. При ручном обслуживании вам нужно самостоятельно продумывать схему и команды. С таблицей RT сервер предоставляет команду OPTIMIZE, которая делает то же самое, но избавляет вас от ненужных внутренних деталей.
Четвёртое, если мой «документ» представляет собой «мини-таблицу» и он мне больше не нужен, я могу просто выбросить его. Но если он «оптимизирован», то есть смешан с множеством других документов, как я могу отменить или удалить его? Да, индексированные документы «смешаны» вместе, и нет простого способа удалить один без перестройки всей таблицы. И если для обычных таблиц перестройка или слияние — это нормальный способ обслуживания, то для таблицы реального времени это сохраняет только простоту манипуляций, но не «реальное время». Чтобы решить эту проблему, Manticore использует хитрость: когда вы удаляете документ, идентифицированный по ID документа, сервер просто отслеживает этот номер. Вместе с другими удалёнными документами их ID сохраняются в так называемом kill-list. При поиске по таблице сервер сначала извлекает все подходящие документы, а затем исключает документы, найденные в kill-list (это самое базовое описание; на самом деле внутри всё сложнее). Суть в том, что ради «немедленного» удаления документы фактически не удаляются, а просто помечаются как «удалённые». Они всё ещё занимают место в различных структурах таблицы, по сути являясь мусором. Статистика слов, которая влияет на ранжирование, также не затрагивается, то есть работает именно так, как заявлено: мы ищем среди всех документов, а затем просто скрываем помеченные как удалённые из итогового результата. Когда документ заменяется, это означает, что он убивается в старых частях таблицы и вставляется заново в самую свежую часть. Все последствия «скрытия через killlist» также действуют в этом случае.
Когда происходит перестройка какой-то части таблицы, например, когда сливаются некоторые транзакции (сегменты) RAM-чанка, или когда RAM-чанк конвертируется в дисковый чанк, или когда два дисковых чанка объединяются, сервер выполняет комплексную итерацию по затронутым частям и физически исключает удалённые документы из всех них. То есть, если они были в списках документов некоторых слов — они вычищаются. Если это было уникальное слово — оно удаляется полностью.
В итоге: удаление работает в два этапа:
- Сначала мы помечаем документы как «удалённые» в реальном времени и подавляем их в результатах поиска.
- Во время некоторой операции с чанком таблицы RT мы окончательно физически удаляем удалённые документы.
Пятое, если таблица RT содержит обычные дисковые таблицы в своей коллекции, могу ли я просто добавить в неё свою готовую старую дисковую таблицу? Нет. Это невозможно, чтобы избежать ненужной сложности и предотвратить случайное повреждение. Однако, если ваша таблица RT только что создана и не содержит данных, вы можете ATTACH TABLE вашу дисковую таблицу к ней. Ваша старая таблица будет перемещена внутрь таблицы RT и станет её частью.
В итоге о структуре таблицы RT: это умно организованная коллекция обычных дисковых таблиц с быстрой таблицей в памяти, предназначенная для вставок в реальном времени и полуреального времени удаления документов. Таблица RT имеет общую схему, общие настройки и может легко обслуживаться без глубокого погружения в детали.
FLUSH RAMCHUNK rt_table
Команда FLUSH RAMCHUNK создает новый диск-чанк в RT-таблице.
Обычно RT-таблица автоматически сбрасывает и конвертирует содержимое RAM-чанка в новый диск-чанк, когда выполняется одно из специальных условий. Однако в некоторых случаях вы можете захотеть инициировать сброс вручную — и оператор FLUSH RAMCHUNK позволяет это сделать.
- SQL
FLUSH RAMCHUNK rt;Query OK, 0 rows affected (0.05 sec)FLUSH TABLE rt_table
FLUSH TABLE принудительно сбрасывает содержимое RAM чанка RT таблицы на диск.
Чанк RAM реального времени таблицы RT table автоматически сбрасывается на диск при корректном завершении работы или периодически каждые rt_flush_period секунд.
Выполнение команды FLUSH TABLE не только принудительно записывает содержимое RAM чанка на диск, но и запускает очистку бинарных лог-файлов.
- SQL
FLUSH TABLE rt;Query OK, 0 rows affected (0.05 sec)Со временем RT-таблицы могут фрагментироваться на множество дисковых чанков и/или загрязняться удалёнными, но ещё не очищенными данными, что влияет на производительность поиска. В таких случаях необходима оптимизация. По сути, процесс оптимизации объединяет пары дисковых чанков, удаляя документы, которые были ранее удалены с помощью операторов DELETE.
Начиная с Manticore 4, этот процесс происходит автоматически по умолчанию. Однако вы также можете использовать следующие команды для ручного запуска компактирования таблицы.
OPTIMIZE TABLE table_name [OPTION opt_name = opt_value [,...]]
Оператор OPTIMIZE добавляет RT-таблицу в очередь оптимизации, которая будет обработана в фоновом потоке.
- SQL
OPTIMIZE TABLE rt;По умолчанию OPTIMIZE объединяет дисковые чанки RT-таблицы до количества, меньшего или равного числу логических ядер процессора, умноженному на 2.
Однако, если в таблице есть атрибуты с KNN-индексами, этот порог отличается. В этом случае он устанавливается как количество физических ядер процессора, делённое на 2, для улучшения производительности KNN-поиска.
Вы также можете вручную контролировать количество оптимизируемых дисковых чанков с помощью опции cutoff.
Дополнительные опции включают:
- Настройку сервера optimize_cutoff для переопределения порога по умолчанию
- Настройку на уровне таблицы optimize_cutoff
- SQL
OPTIMIZE TABLE rt OPTION cutoff=4;При использовании OPTION sync=1 (по умолчанию 0) команда будет ждать завершения процесса оптимизации перед возвратом результата. Если соединение прервётся, оптимизация продолжит выполняться на сервере.
- SQL
OPTIMIZE TABLE rt OPTION sync=1;Оптимизация может быть длительным и интенсивным по вводу-выводу процессом. Чтобы минимизировать влияние, вся фактическая работа по слиянию выполняется последовательно в специальном фоновом потоке, а оператор OPTIMIZE просто добавляет задачу в его очередь. Фоновый поток оптимизации может быть ограничен по вводу-выводу, и вы можете контролировать максимальное количество операций ввода-вывода в секунду и максимальный размер ввода-вывода с помощью директив rt_merge_iops и rt_merge_maxiosize соответственно.
Во время оптимизации RT-таблица остаётся онлайн и доступна для поиска и обновлений почти всё время. Она блокируется на очень короткий период, когда успешно объединяется пара дисковых чанков, что позволяет переименовать старые и новые файлы и обновить заголовок таблицы.
Пока auto_optimize не отключён, таблицы оптимизируются автоматически.
Если вы сталкиваетесь с неожиданными SST или хотите, чтобы таблицы на всех узлах кластера были бинарно идентичны, необходимо:
- Отключить auto_optimize.
- Вручную оптимизировать таблицы:
На одном из узлов удалите таблицу из кластера:
‹›- SQL
SQL📋⚙ALTER CLUSTER mycluster DROP myindex;Оптимизируйте таблицу:
‹›- SQL
SQL📋⚙OPTIMIZE TABLE myindex;Добавьте таблицу обратно в кластер:
‹›- SQL
SQL📋⚙ALTER CLUSTER mycluster ADD myindex;Когда таблица будет добавлена обратно, новые файлы, созданные в процессе оптимизации, будут реплицированы на другие узлы кластера. Любые локальные изменения, сделанные в таблице на других узлах, будут потеряны.
Изменения данных таблицы (вставки, замены, удаления, обновления) должны либо:
- Быть отложены, либо
- Направляться на узел, где выполняется процесс оптимизации.
Обратите внимание, что пока таблица отсутствует в кластере, команды insert/replace/delete/update должны обращаться к ней без префикса имени кластера (для SQL-запросов или свойства cluster в случае HTTP JSON-запроса), иначе они завершатся с ошибкой. После того как таблица будет добавлена обратно в кластер, необходимо возобновить операции записи в таблицу и снова использовать префикс имени кластера, иначе они будут завершаться с ошибкой.
Операции поиска доступны как обычно на любом из узлов в процессе.