Резервное копирование снимков — это облегчённый режим резервного копирования для cloud-native движков таблиц. Вместо копирования данных он записывает в ClickHouse Keeper узлы блокировки для каждой части. Эти блокировки не позволяют серверу удалять части в объектном хранилище, на которые ссылается снимок, пока этот снимок хранится. Затем резервная копия сохраняет ссылки на объекты в хранилище вместо физического копирования данных, поэтому снимок создаётся быстро независимо от размера таблицы.
Облегчённый режим применяется к таблицам SharedMergeTree, SharedSet и SharedJoin. Для всех остальных типов движков — например, Log или Memory — резервное копирование автоматически возвращается к стандартному режиму с копированием данных.
Создание снимка
Для резервного копирования снимков используется стандартная команда BACKUP с параметром experimental_lightweight_snapshot = true. Параметр id обязателен — он задаёт имя снимка и используется для обращения к нему в командах разблокировки и обсервабилити:
BACKUP { TABLE [db.]table_name | DATABASE db_name | ALL [EXCEPT {TABLES | DATABASES} ...] }
TO { S3(...) | AzureBlobStorage(...) }
SETTINGS experimental_lightweight_snapshot = true, id = '<snapshot_id>'Команда возвращает id и status, а id можно использовать для отслеживания операции в system.backups.
Создайте резервную копию отдельной таблицы в S3:
BACKUP TABLE mydb.events
TO S3('https://my-bucket.s3.us-east-1.amazonaws.com/snapshots/events/', 'ACCESS_KEY_ID', 'SECRET_ACCESS_KEY')
SETTINGS experimental_lightweight_snapshot = true, id = 'events_snapshot_1'Создайте резервную копию всей базы данных:
BACKUP DATABASE mydb
TO S3('https://my-bucket.s3.us-east-1.amazonaws.com/snapshots/mydb/', 'ACCESS_KEY_ID', 'SECRET_ACCESS_KEY')
SETTINGS experimental_lightweight_snapshot = true, id = 'mydb_snapshot_1'Создайте резервную копию всех таблиц, кроме одной:
BACKUP ALL
EXCEPT TABLES mydb.staging_table
TO S3('https://my-bucket.s3.us-east-1.amazonaws.com/snapshots/full/', 'ACCESS_KEY_ID', 'SECRET_ACCESS_KEY')
SETTINGS experimental_lightweight_snapshot = true, id = 'full_snapshot_1'Те же команды подходят и для Azure Blob Storage:
BACKUP TABLE mydb.events
TO AzureBlobStorage('DefaultEndpointsProtocol=https;AccountName=myaccount;AccountKey=...', 'my-container', 'snapshots/events/')
SETTINGS experimental_lightweight_snapshot = true, id = 'events_snapshot_1'Восстановление в тот же сервис
Поскольку снимок хранит ссылки на файлы в Объектном хранилище, а не копии данных, для восстановления в новый или другой сервис ClickHouse нужен доступ к исходному Объектному хранилищу. Поэтому межсервисное восстановление через SQL не поддерживается — оно доступно только через интерфейс. Через SQL можно восстановить снимок в тот же сервис из бакета с внешней резервной копией, используя snapshot_from_current_service = 1. В этом случае объекты считываются напрямую через диск пункта назначения, а не через механизм чтения удалённого снимка:
RESTORE TABLE mydb.events AS mydb.events_restored
FROM S3('https://my-bucket.s3.us-east-1.amazonaws.com/snapshots/events/', 'ACCESS_KEY_ID', 'SECRET_ACCESS_KEY')
SETTINGS snapshot_from_current_service = 1Предложение AS выполняет восстановление в таблицу с новым именем, сохраняя исходную таблицу. Чтобы перезаписать исходную таблицу, сначала удалите её:
DROP TABLE mydb.events;
RESTORE TABLE mydb.events
FROM S3('https://my-bucket.s3.us-east-1.amazonaws.com/snapshots/events/', 'ACCESS_KEY_ID', 'SECRET_ACCESS_KEY')
SETTINGS snapshot_from_current_service = 1Разблокировка снимка
Каждый снимок удерживает блокировки в ClickHouse Keeper, из-за которых файлы объектного хранилища, на которые он ссылается, не удаляются сборщиком мусора. После завершения восстановления — или когда снимок больше не нужен — разблокируйте его, чтобы снять эти блокировки.
Есть два варианта: разблокировка на уровне системы, которая сразу снимает все блокировки снимка, и разблокировка на уровне отдельной таблицы, которая снимает блокировку только для одной таблицы, оставляя остальную часть снимка без изменений.
Разблокировка на уровне системы — снимает все блокировки снимка:
SYSTEM UNLOCK SNAPSHOT '<snapshot_id>'
FROM S3('https://my-bucket.s3.us-east-1.amazonaws.com/snapshots/events/', 'ACCESS_KEY_ID', 'SECRET_ACCESS_KEY')Разблокировка для отдельной таблицы — снимает блокировку только для одной таблицы:
ALTER TABLE mydb.events UNLOCK SNAPSHOT '<snapshot_id>'
FROM S3('https://my-bucket.s3.us-east-1.amazonaws.com/snapshots/events/', 'ACCESS_KEY_ID', 'SECRET_ACCESS_KEY')Предложение FROM необязательно, если пункт назначения снимка был сохранён в Keeper при создании (это видно в столбце info таблицы system.snapshot_locks):
SYSTEM UNLOCK SNAPSHOT '<snapshot_id>'
-- или для конкретной таблицы:
ALTER TABLE mydb.events UNLOCK SNAPSHOT '<snapshot_id>'После разблокировки соответствующая строка исчезает из system.snapshot_locks, а части, на которые больше не ссылаются другие снимки, пропадают из system.snapshot_parts.
Обсервабилити
system.backups
Все операции со снимками появляются в system.backups наряду с обычными операциями резервного копирования и восстановления. Выполните запрос к этой таблице по заданному id (или по UUID, возвращённому командой):
SELECT id, name, status, error, start_time, end_time, num_files, uncompressed_size, compressed_size
FROM system.backups
WHERE id = 'events_snapshot_1'
FORMAT VerticalRow 1:
──────
id: events_snapshot_1
name: S3('https://my-bucket.s3.us-east-1.amazonaws.com/snapshots/events/', '[HIDDEN]')
status: BACKUP_CREATED
error:
start_time: 2024-06-01 10:00:00
end_time: 2024-06-01 10:00:03
num_files: 42
uncompressed_size: 1073741824
compressed_size: 0system.snapshot_locks
system.snapshot_locks показывает зафиксированные снимки, которые в данный момент зарегистрированы в Keeper. Когда снимок фиксируется, в Keeper создаётся узел по пути /clickhouse/snapshot/committed/{snapshot_id}. Перед удалением любой части данных сервер проверяет, не удерживает ли какой-либо зафиксированный снимок блокировку на эту часть. Если удерживает, удаление не выполняется. Блокировка сохраняется, пока вы явно не разблокируете снимок.
SELECT *
FROM system.snapshot_locks| Столбец | Тип | Описание |
|---|---|---|
id |
String |
Идентификатор снимка |
info |
String |
Пункт назначения снимка, например S3('...') |
ctime |
DateTime |
Время создания этой блокировки в Keeper |
lock_path |
String |
Путь в Keeper для этой блокировки |
Каждая строка соответствует одному зафиксированному снимку. Если вы видите блокировки для снимков, у которых больше нет действительного пункта назначения резервной копии, выполните SYSTEM UNLOCK SNAPSHOT, чтобы удалить их.
Чтобы проверить, существует ли блокировка для конкретного снимка:
SELECT id, info, lock_path
FROM system.snapshot_locks
WHERE id = 'events_snapshot_1'system.snapshot_parts
system.snapshot_parts показывает части данных, которые в данный момент закреплены как минимум одной блокировкой снимка. Для каждой заблокированной части в Keeper существует узел по пути /clickhouse/snapshot/{table_uuid}/{part_name}, содержащий размер части в сжатом и несжатом виде. Эта таблица читает эти узлы, чтобы показать, какие части в настоящее время защищены от удаления.
SELECT *
FROM system.snapshot_parts
ORDER BY data_compressed_bytes DESC
LIMIT 20| Столбец | Тип | Описание |
|---|---|---|
name |
String |
Имя части данных |
table_id |
String |
UUID таблицы, которой принадлежит эта часть |
data_compressed_bytes |
UInt64 |
Размер этой части в сжатом виде |
data_uncompressed_bytes |
UInt64 |
Размер этой части в несжатом виде |
snapshots_size |
UInt64 |
Количество снимков, которые в данный момент удерживают блокировку этой части |
На части с snapshots_size > 1 ссылаются несколько снимков, и они не будут удалены из Объектного хранилища, пока не будут разблокированы все удерживающие их снимки.
Чтобы проверить общий объём закреплённого хранилища:
SELECT
formatReadableSize(sum(data_compressed_bytes)) AS total_pinned_compressed,
formatReadableSize(sum(data_uncompressed_bytes)) AS total_pinned_uncompressed,
count() AS parts_count
FROM system.snapshot_partsЧтобы найти части, заблокированные снимком, но уже удалённые или больше не являющиеся активными на сервере — то есть данные, которые сохраняются в Объектном хранилище исключительно из-за блокировок снимка:
SELECT
count(*),
sum(data_uncompressed_bytes)
FROM system.snapshot_parts
WHERE (name, table_id) NOT IN (
SELECT
name,
toString(tables.uuid)
FROM system.parts
INNER JOIN system.tables ON (parts.`table` = tables.name) AND parts.active
)┌─count()─┬─sum(data_uncompressed_bytes)─┐
│ 1000 │ 96037 │
└─────────┴──────────────────────────────┘Это полезно для оценки дополнительных затрат на хранение снимков после изменения или удаления исходных данных.
Настройки сервера
Следующие параметры конфигурации сервера управляют поведением снимков. Они задаются в файле конфигурации сервера, а не в SQL.
| Настройка | Тип | По умолчанию | Можно изменить без перезапуска | Описание |
|---|---|---|---|---|
max_held_snapshots |
UInt64 | 0 |
Нет | Максимальное количество облегчённых снимков, которые можно одновременно хранить. 0 означает без ограничений. Если предел достигнут, при создании нового снимка будет сгенерировано исключение. |
max_snapshot_commit_thread_pool_size |
UInt64 | 64 |
Да | Количество потоков, используемых для фиксации узлов блокировки снимков в Keeper. Увеличьте это значение, если на больших таблицах с большим количеством частей снимки создаются медленно. |
max_snapshot_commit_thread_pool_free_size |
UInt64 | 0 |
Да | Если число бездействующих потоков в пуле фиксации снимков превышает это значение, ClickHouse освобождает их и сокращает пул. При необходимости потоки создаются снова. 0 означает, что бездействующие потоки никогда не освобождаются. |
snapshot_cleaner_period |
UInt64 | 120 |
Нет | Как часто (в секундах) запускается очистка снимков, чтобы удалять части, на которые больше не ссылается ни одна блокировка снимка. Только в ClickHouse Cloud. |
snapshot_cleaner_pool_size |
UInt64 | 128 |
Нет | Количество потоков в пуле очистки снимков. Только в ClickHouse Cloud. |