Перейти к основному содержимому
Перейти к основному содержимому

Легковесное UPDATE 语句

Beta feature. Learn more.
примечание

Легковесные обновления в настоящее время находятся в стадии бета-тестирования. Если вы столкнетесь с проблемами, пожалуйста, откройте issue в репозитории ClickHouse.

Легковесная команда UPDATE обновляет строки в таблице [db.]table, которые соответствуют выражению filter_expr. Она называется "легковесным обновлением", чтобы отличать ее от запроса ALTER TABLE ... UPDATE, который является тяжелым процессом, переписывающим целые колонки в частях данных. Она доступна только для семейства движков таблиц MergeTree.

UPDATE [db.]table [ON CLUSTER cluster] SET column1 = expr1 [, ...] [IN PARTITION partition_expr] WHERE filter_expr;

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

Примеры

UPDATE hits SET Title = 'Updated Title' WHERE EventDate = today();

UPDATE wikistat SET hits = hits + 1, time = now() WHERE path = 'ClickHouse';

Легковесные обновления не обновляют данные немедленно

Легковесный UPDATE реализован с помощью патч-частей - специального типа части данных, который содержит только обновленные колонки и строки. Легковесный UPDATE создает патч-части, но не производит немедленное физическое изменение оригинальных данных в хранилище. Процесс обновления похож на запрос INSERT ... SELECT ..., но запрос UPDATE ждет, пока создание патч-части не будет завершено, прежде чем вернуть результат.

Обновленные значения:

  • Немедленно видны в запросах SELECT через применение патчей
  • Физически материализуются только во время последующих слияний и мутаций
  • Автоматически очищаются после того, как все активные части были материализованы

Требования к легковесным обновлениям

Легковесные обновления поддерживаются для движков MergeTree, ReplacingMergeTree, CollapsingMergeTree и их версий Replicated и Shared.

Чтобы использовать легковесные обновления, материализация колонок _block_number и _block_offset должна быть включена с помощью настроек таблицы enable_block_number_column и enable_block_offset_column.

Легковесные удаления

Запрос легковесного DELETE можно выполнить как легковесный UPDATE вместо мутации ALTER UPDATE. Реализация легковесного DELETE контролируется с помощью настройки lightweight_delete_mode.

Соображения по производительности

Преимущества легковесных обновлений:

  • Задержка обновления сопоставима с задержкой запроса INSERT ... SELECT ...
  • Пишутся только обновленные колонки и значения, а не целые колонки в частях данных
  • Нет необходимости ждать завершения текущих слияний/мутаций, поэтому задержка обновления предсказуема
  • Параллельное выполнение легковесных обновлений возможно

Потенциальные влияния на производительность:

  • Добавляет накладные расходы к запросам SELECT, которые должны применять патчи
  • Индексы пропуска не будут использоваться для колонок в частях данных, для которых необходимо применять патчи. Проекции не будут использоваться, если для таблицы есть патч-части, включая части данных, которые не имеют патчей для применения.
  • Ч频 обновления, которые происходят слишком часто, могут привести к ошибке "слишком много частей". Рекомендуется объединять несколько обновлений в один запрос, например, поместив идентификаторы для обновлений в один оператор IN в условии WHERE
  • Легковесные обновления предназначены для обновления небольшого количества строк (до примерно 10% таблицы). Если вам нужно обновить большее количество, рекомендуется использовать мутацию ALTER TABLE ... UPDATE

Параллельные операции

Легковесные обновления не ждут завершения текущих слияний и мутаций в отличие от тяжелых мутаций. Согласованность параллельных легковесных обновлений контролируется настройками update_sequential_consistency и update_parallel_mode.

Разрешения на обновление

UPDATE требует привилегии ALTER UPDATE. Чтобы разрешить операторы UPDATE на конкретной таблице для данного пользователя, выполните:

GRANT ALTER UPDATE ON db.table TO username;

Подробности реализации

Патч-части такие же, как и обычные части, но содержат только обновленные колонки и несколько системных колонок:

  • _part - имя оригинальной части
  • _part_offset - номер строки в оригинальной части
  • _block_number - номер блока строки в оригинальной части
  • _block_offset - смещение блока строки в оригинальной части
  • _data_version - версия данных обновленных данных (номер блока, выделенный для запроса UPDATE)

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

Патч-части принадлежат к разным партициям, чем оригинальная часть. Идентификатор партиции патч-части - patch-<hash имен колонок в патч-части>-<original_partition_id>. Поэтому патч-части с разными колонками хранятся в разных партициях. Например, три обновления SET x = 1 WHERE <cond>, SET y = 1 WHERE <cond> и SET x = 1, y = 1 WHERE <cond> создадут три патч-части в трех разных партициях.

Патч-части могут сливаться между собой, чтобы уменьшить количество применяемых патчей в запросах SELECT и снизить накладные расходы. Слияние патч-частей использует алгоритм слияния replacing с _data_version в качестве версии колонки. Таким образом, патч-части всегда хранят последнюю версию для каждой обновленной строки в части.

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

Например, если мы читаем часть A, нам нужно применить патч-часть X:

  • если X содержит часть A саму по себе. Это происходит, если A не участвовала в слиянии, когда был выполнен UPDATE.
  • если X содержит часть B и C, которые охватываются частью A. Это происходит, если происходило слияние (B, C) -> A, когда был выполнен UPDATE.

Для этих двух случаев есть два способа применения патч-частей соответственно:

  • Используя слияние по сортированным колонкам _part, _part_offset.
  • Используя соединение по колонкам _block_number, _block_offset.

Режим соединения медленнее и требует больше памяти, чем режим слияния, но используется реже.