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

Резервное копирование и восстановление снимков

Резервное копирование снимков — это облегченный режим резервного копирования для облачных движков таблиц. Вместо копирования данных он записывает в 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 не поддерживается — оно доступно только в UI. Через 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>'

-- or per-table:
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 Vertical
Row 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:   0

system.snapshot_locks

system.snapshot_locks показывает зафиксированные снимки, которые в данный момент зарегистрированы в Keeper. Когда снимок фиксируется, в Keeper создаётся узел по пути /clickhouse/snapshot/committed/{snapshot_id}. Перед удалением любой части данных сервер проверяет, не удерживает ли какой-либо зафиксированный снимок блокировку этой части. Если да, удаление пропускается. Блокировка сохраняется, пока вы явно не снимете её со снимка.

SELECT *
FROM system.snapshot_locks
СтолбецТипОписание
idStringИдентификатор снимка
infoStringМесто назначения снимка, например S3('...')
ctimeDateTimeВремя создания этой блокировки в Keeper
lock_pathStringПуть в Keeper для этой блокировки

Каждая строка соответствует одному зафиксированному снимку. Если вы видите блокировки снимков, для которых больше не существует действительного места назначения резервной копии, выполните SYSTEM UNLOCK SNAPSHOT, чтобы удалить их.

Чтобы проверить, существует ли блокировка для конкретного снимка:

SELECT id, info, lock_path
FROM system.snapshot_locks
WHERE id = 'events_snapshot_1'

system.snapshot_parts

system.snapshot_parts показывает части данных, в данный момент закреплённые как минимум одной блокировкой снимка. Для каждой заблокированной части по пути /clickhouse/snapshot/{table_uuid}/{part_name} существует узел Keeper, содержащий сжатый и несжатый размеры части. Эта таблица считывает эти узлы и показывает, какие части сейчас защищены от удаления.

SELECT *
FROM system.snapshot_parts
ORDER BY data_compressed_bytes DESC
LIMIT 20
СтолбецТипОписание
nameStringИмя части
table_idStringUUID таблицы, которой принадлежит эта часть
data_compressed_bytesUInt64Размер этой части в сжатом виде
data_uncompressed_bytesUInt64Размер этой части в несжатом виде
snapshots_sizeUInt64Количество снимков, которые в данный момент удерживают блокировку этой части

На части, для которых 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 │
└─────────┴──────────────────────────────┘

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