ALTER

ALTER

Запрос ALTER поддерживается только для таблиц типа *MergeTree, а также Merge и Distributed. Запрос имеет несколько вариантов.

Манипуляции со столбцами

Изменение структуры таблицы.

ALTER TABLE [db].name [ON CLUSTER cluster] ADD|DROP|MODIFY COLUMN ...

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

Существуют следующие действия:

ADD COLUMN name [type] [default_expr] [AFTER name_after]

Добавляет в таблицу новый столбец с именем name, типом type и выражением для умолчания default_expr (смотрите раздел "Значения по умолчанию"). Если указано AFTER name_after (имя другого столбца), то столбец добавляется (в список столбцов таблицы) после указанного. Иначе, столбец добавляется в конец таблицы. Внимательный читатель может заметить, что отсутствует возможность добавить столбец в начало таблицы. Для цепочки действий, name_after может быть именем столбца, который добавляется в одном из предыдущих действий.

Добавление столбца всего лишь меняет структуру таблицы, и не производит никаких действий с данными - соответствующие данные не появляются на диске после ALTER-а. При чтении из таблицы, если для какого-либо столбца отсутствуют данные, то он заполняется значениями по умолчанию (выполняя выражение по умолчанию, если такое есть, или нулями, пустыми строками). Также, столбец появляется на диске при слиянии кусков данных (см. MergeTree).

Такая схема позволяет добиться мгновенной работы запроса ALTER и отсутствия необходимости увеличивать объём старых данных.

DROP COLUMN name

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

MODIFY COLUMN name [type] [default_expr]

Изменяет тип столбца name на type и/или выражение для умолчания на default_expr. При изменении типа, значения преобразуются так, как если бы к ним была применена функция toType.

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

Изменение типа столбца - это единственное действие, которое выполняет сложную работу - меняет содержимое файлов с данными. Для больших таблиц, выполнение может занять длительное время.

Выполнение производится в несколько стадий: - подготовка временных (новых) файлов с изменёнными данными; - переименование старых файлов; - переименование временных (новых) файлов в старые; - удаление старых файлов.

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

Не поддерживается изменение типа столбца у массивов и вложенных структур данных.

Запрос ALTER позволяет создавать и удалять отдельные элементы (столбцы) вложенных структур данных, но не вложенные структуры данных целиком. Для добавления вложенной структуры данных, вы можете добавить столбцы с именем вида name.nested_name и типом Array(T) - вложенная структура данных полностью эквивалентна нескольким столбцам-массивам с именем, имеющим одинаковый префикс до точки.

Отсутствует возможность удалять столбцы, входящие в первичный ключ или ключ для сэмплирования (в общем, входящие в выражение ENGINE). Изменение типа у столбцов, входящих в первичный ключ возможно только в том случае, если это изменение не приводит к изменению данных (например, разрешено добавление значения в Enum или изменение типа с DateTime на UInt32).

Если возможностей запроса ALTER не хватает для нужного изменения таблицы, вы можете создать новую таблицу, скопировать туда данные с помощью запроса INSERT SELECT, затем поменять таблицы местами с помощью запроса RENAME, и удалить старую таблицу.

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

Для таблиц, которые не хранят данные самостоятельно (типа Merge и Distributed), ALTER всего лишь меняет структуру таблицы, но не меняет структуру подчинённых таблиц. Для примера, при ALTER-е таблицы типа Distributed, вам также потребуется выполнить запрос ALTER для таблиц на всех удалённых серверах.

Запрос ALTER на изменение столбцов реплицируется. Соответствующие инструкции сохраняются в ZooKeeper, и затем каждая реплика их применяет. Все запросы ALTER выполняются в одном и том же порядке. Запрос ждёт выполнения соответствующих действий на всех репликах. Но при этом, запрос на изменение столбцов в реплицируемой таблице можно прервать, и все действия будут осуществлены асинхронно.

Манипуляции с партициями и кусками

Работает только для таблиц семейства MergeTree. Существуют следующие виды операций:

  • DETACH PARTITION - перенести партицию в директорию detached и забыть про неё.
  • DROP PARTITION - удалить партицию.
  • ATTACH PART|PARTITION - добавить в таблицу новый кусок или партицию из директории detached.
  • FREEZE PARTITION - создать бэкап партиции.
  • FETCH PARTITION - скачать партицию с другого сервера.

Ниже будет рассмотрен каждый вид запроса по-отдельности.

Партицией (partition) в таблице называются данные за один календарный месяц. Это определяется значениями ключа-даты, указанной в параметрах движка таблицы. Данные за каждый месяц хранятся отдельно, чтобы упростить всевозможные манипуляции с этими данными.

Куском (part) в таблице называется часть данных одной партиции, отсортированная по первичному ключу.

Чтобы посмотреть набор кусков и партиций таблицы, можно воспользоваться системной таблицей system.parts:

SELECT * FROM system.parts WHERE active

active - учитывать только активные куски. Неактивными являются, например, исходные куски оставшиеся после слияния в более крупный кусок - такие куски удаляются приблизительно через 10 минут после слияния.

Другой способ посмотреть набор кусков и партиций - зайти в директорию с данными таблицы. Директория с данными - /var/lib/clickhouse/data/database/table/, где /var/lib/clickhouse/ - путь к данным ClickHouse, database - имя базы данных, table - имя таблицы. Пример:

$ ls -l /var/lib/clickhouse/data/test/visits/
total 48
drwxrwxrwx 2 clickhouse clickhouse 20480 May  5 02:58 20140317_20140323_2_2_0
drwxrwxrwx 2 clickhouse clickhouse 20480 May  5 02:58 20140317_20140323_4_4_0
drwxrwxrwx 2 clickhouse clickhouse  4096 May  5 02:55 detached
-rw-rw-rw- 1 clickhouse clickhouse     2 May  5 02:58 increment.txt

Здесь 20140317_20140323_2_2_0, 20140317_20140323_4_4_0 - директории кусков.

Рассмотрим по порядку имя первого куска: 20140317_20140323_2_2_0.

  • 20140317 - минимальная дата данных куска
  • 20140323 - максимальная дата данных куска
  • 2 - минимальный номер блока данных
  • 2 - максимальный номер блока данных
  • 0 - уровень куска - глубина дерева слияний, которыми он образован

Каждый кусок относится к одной партиции и содержит данные только за один месяц. 201403 - имя партиции. Партиция представляет собой набор кусков за один месяц.

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

Директория detached содержит куски, не используемые сервером - отцепленные от таблицы с помощью запроса ALTER ... DETACH. Также в эту директорию переносятся куски, признанные повреждёнными, вместо их удаления. Вы можете в любое время добавлять, удалять, модифицировать данные в директории detached - сервер не будет об этом знать, пока вы не сделаете запрос ALTER TABLE ... ATTACH.

ALTER TABLE [db.]table DETACH PARTITION 'name'

Перенести все данные для партиции с именем name в директорию detached и забыть про них. Имя партиции указывается в формате YYYYMM. Оно может быть указано в одинарных кавычках или без них.

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

Запрос реплицируется - данные будут перенесены в директорию detached и забыты на всех репликах. Запрос может быть отправлен только на реплику-лидер. Вы можете узнать, является ли реплика лидером, сделав SELECT в системную таблицу system.replicas. Или, проще, вы можете выполнить запрос на всех репликах, и на всех кроме одной, он кинет исключение.

ALTER TABLE [db.]table DROP PARTITION 'name'

Аналогично операции DETACH. Удалить данные из таблицы. Куски с данными будут помечены как неактивные и будут полностью удалены примерно через 10 минут. Запрос реплицируется - данные будут удалены на всех репликах.

ALTER TABLE [db.]table ATTACH PARTITION|PART 'name'

Добавить данные в таблицу из директории detached.

Существует возможность добавить данные для целой партиции (PARTITION) или отдельный кусок (PART). В случае PART, укажите полное имя куска в одинарных кавычках.

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

То есть, вы можете разместить данные в директории detached на одной реплике и, с помощью запроса ALTER ... ATTACH добавить их в таблицу на всех репликах.

ALTER TABLE [db.]table FREEZE PARTITION 'name'

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

Запрос делает следующее: для снэпшота данных на момент его выполнения, создаёт hardlink-и на данные таблиц в директории /var/lib/clickhouse/shadow/N/...

/var/lib/clickhouse/ - рабочая директория ClickHouse из конфига. N - инкрементальный номер бэкапа.

Структура директорий внутри бэкапа создаётся такой же, как внутри /var/lib/clickhouse/. Также делает chmod всех файлов, запрещая запись в них.

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

После создания бэкапа, данные из /var/lib/clickhouse/shadow/ можно скопировать на удалённый сервер и затем удалить на локальном сервере. Весь процесс бэкапа не требует остановки сервера.

Запрос ALTER ... FREEZE PARTITION не реплицируется. То есть, локальный бэкап создаётся только на локальном сервере.

В качестве альтернативного варианта, вы можете скопировать данные из директории /var/lib/clickhouse/data/database/table вручную. Но если это делать при запущенном сервере, то возможны race conditions при копировании директории с добавляющимися/изменяющимися файлами, и бэкап может быть неконсистентным. Этот вариант может использоваться, если сервер не запущен - тогда полученные данные будут такими же, как после запроса ALTER TABLE t FREEZE PARTITION.

ALTER TABLE ... FREEZE PARTITION копирует только данные, но не метаданные таблицы. Чтобы сделать бэкап метаданных таблицы, скопируйте файл /var/lib/clickhouse/metadata/database/table.sql

Для восстановления из бэкапа:

  • создайте таблицу, если её нет, с помощью запроса CREATE. Запрос можно взять из .sql файла (замените в нём ATTACH на CREATE);
  • скопируйте данные из директории data/database/table/ внутри бэкапа в директорию /var/lib/clickhouse/data/database/table/detached/
  • выполните запросы ALTER TABLE ... ATTACH PARTITION YYYYMM, где YYYYMM - месяц, для каждого месяца.

Таким образом, данные из бэкапа будут добавлены в таблицу. Восстановление из бэкапа, так же, не требует остановки сервера.

Бэкапы и репликация

Репликация защищает от аппаратных сбоев. В случае, если на одной из реплик у вас исчезли все данные, то восстановление делается по инструкции в разделе "Восстановление после сбоя".

Для защиты от аппаратных сбоев, обязательно используйте репликацию. Подробнее про репликацию написано в разделе "Репликация данных".

Бэкапы защищают от человеческих ошибок (случайно удалили данные, удалили не те данные или не на том кластере, испортили данные). Для баз данных большого объёма, бывает затруднительно копировать бэкапы на удалённые серверы. В этих случаях, для защиты от человеческой ошибки, можно держать бэкап на том же сервере (он будет лежать в /var/lib/clickhouse/shadow/).

ALTER TABLE [db.]table FETCH PARTITION 'name' FROM 'path-in-zookeeper'

Запрос работает только для реплицируемых таблиц.

Скачивает указанную партицию с шарда, путь в ZooKeeper к которому указан в секции FROM и помещает в директорию detached указанной таблицы.

Не смотря на то, что запрос называется ALTER TABLE, он не изменяет структуру таблицы, и не изменяет сразу доступные данные в таблице.

Данные помещаются в директорию detached, и их можно прикрепить с помощью запроса ALTER TABLE ... ATTACH.

В секции FROM указывается путь в ZooKeeper. Например, /clickhouse/tables/01-01/visits. Перед скачиванием проверяется существование партиции и совпадение структуры таблицы. Автоматически выбирается наиболее актуальная реплика среди живых реплик.

Запрос ALTER ... FETCH PARTITION не реплицируется. То есть, партиция будет скачана в директорию detached только на локальном сервере. Заметим, что если вы после этого добавите данные в таблицу с помощью запроса ALTER TABLE ... ATTACH, то данные будут добавлены на всех репликах (на одной из реплик будут добавлены из директории detached, а на других - загружены с соседних реплик).

Синхронность запросов ALTER

Для нереплицируемых таблиц, все запросы ALTER выполняются синхронно. Для реплицируемых таблиц, запрос всего лишь добавляет инструкцию по соответствующим действиям в ZooKeeper, а сами действия осуществляются при первой возможности. Но при этом, запрос может ждать завершения выполнения этих действий на всех репликах.

Для запросов ALTER ... ATTACH|DETACH|DROP можно настроить ожидание, с помощью настройки replication_alter_partitions_sync. Возможные значения: 0 - не ждать, 1 - ждать выполнения только у себя (по умолчанию), 2 - ждать всех.

Мутации

Мутации - разновидность запроса ALTER, позволяющая изменять или удалять данные в таблице. В отличие от стандартных запросов DELETE и UPDATE, рассчитанных на точечное изменение данных, область применения мутаций - достаточно тяжёлые изменения, затрагивающие много строк в таблице.

Функциональность находится в состоянии beta и доступна начиная с версии 1.1.54388. Реализована поддержка *MergeTree таблиц (с репликацией и без).

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

На данный момент доступны команды:

ALTER TABLE [db.]table DELETE WHERE filter_expr

Выражение filter_expr должно иметь тип UInt8. Запрос удаляет строки таблицы, для которых это выражение принимает ненулевое значение.

ALTER TABLE [db.]table UPDATE column1 = expr1 [, ...] WHERE filter_expr

Выражение filter_expr должно иметь тип UInt8. Запрос изменяет значение указанных столбцов на вычисленное значение соответствующих выражений в каждой строке, для которой filter_expr пронимает ненулевое значение. Вычисленные значения преобразуются к типу столбца с помощью оператора CAST. Изменение столбцов, которые используются при вычислении первичного ключа или ключа партиционирования, не поддерживается.

В одном запросе можно указать несколько команд через запятую.

Для *MergeTree-таблиц мутации выполняются, перезаписывая данные по кускам (parts). При этом атомарности нет — куски заменяются на помутированные по мере выполнения и запрос SELECT, заданный во время выполнения мутации, увидит данные как из измененных кусков, так и из кусков, которые еще не были изменены.

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

Запрос завершается немедленно после добавления информации о мутации (для реплицированных таблиц - в ZooKeeper, для нереплицированных - на файловую систему). Сама мутация выполняется асинхронно, используя настройки системного профиля. Следить за ходом её выполнения можно по таблице system.mutations. Добавленные мутации будут выполняться до конца даже в случае перезапуска серверов ClickHouse. Откатить мутацию после её добавления нельзя.

Записи о последних выполненных мутациях удаляются не сразу (количество сохраняемых мутаций определяется параметром движка таблиц finished_mutations_to_keep). Более старые записи удаляются.

Таблица system.mutations

Таблица содержит информацию о ходе выполнения мутаций MergeTree-таблиц. Каждой команде мутации соответствует одна строка. В таблице есть следующие столбцы:

database, table - имя БД и таблицы, к которой была применена мутация.

mutation_id - ID запроса. Для реплицированных таблиц эти ID соответствуют именам записей в директории <table_path_in_zookeeper>/mutations/ в ZooKeeper, для нереплицированных - именам файлов в директории с данными таблицы.

command - Команда мутации (часть запроса после ALTER TABLE [db.]table).

create_time - Время создания мутации.

block_numbers.partition_id, block_numbers.number - Nested-столбец. Для мутаций реплицированных таблиц для каждой партиции содержит номер блока, полученный этой мутацией (в каждой партиции будут изменены только куски, содержащие блоки с номерами, меньшими номера, полученного мутацией в этой партиции). Для нереплицированных таблиц нумерация блоков сквозная по партициям, поэтому столбец содержит одну запись с единственным номером блока, полученным мутацией.

parts_to_do - Количество кусков таблицы, которые ещё предстоит изменить.

is_done - Завершена ли мутация. Замечание: даже если parts_to_do = 0, для реплицированной таблицы возможна ситуация, когда мутация ещё не завершена из-за долго выполняющейся вставки, которая добавляет данные, которые нужно будет мутировать.