备份和恢复
命令摘要
BACKUP|RESTORE
TABLE [db.]table_name [AS [db.]table_name_in_backup]
[PARTITION[S] partition_expr [,...]] |
DICTIONARY [db.]dictionary_name [AS [db.]name_in_backup] |
DATABASE database_name [AS database_name_in_backup]
[EXCEPT TABLES ...] |
TEMPORARY TABLE table_name [AS table_name_in_backup] |
VIEW view_name [AS view_name_in_backup]
ALL TEMPORARY TABLES [EXCEPT ...] |
ALL [EXCEPT ...] } [,...]
[ON CLUSTER 'cluster_name']
TO|FROM File('<path>/<filename>') | Disk('<disk_name>', '<path>/') | S3('<S3 endpoint>/<path>', '<Access key ID>', '<Secret access key>')
[SETTINGS base_backup = File('<path>/<filename>') | Disk(...) | S3('<S3 endpoint>/<path>', '<Access key ID>', '<Secret access key>')]
在 ClickHouse 23.4 版本之前,ALL
仅适用于 RESTORE
命令。
背景
虽然 复制 提供了对硬件故障的保护,但它无法防止人为错误:意外删除数据、删除错误的表或错误集群上的表,以及导致数据处理错误或数据损坏的软件错误。在许多情况下,此类错误会影响所有副本。ClickHouse 具有内置的安全措施来防止某些类型的错误——例如,默认情况下,您不能只删除包含超过 50 GB 数据的类似 MergeTree 引擎的表。但是,这些安全措施并不能涵盖所有可能的情况,并且可以绕过。
为了有效地降低人为错误的可能性,您应该提前仔细准备数据备份和恢复策略。
每个公司可用的资源和业务需求都不同,因此没有通用的 ClickHouse 备份和恢复解决方案适合所有情况。适用于 1GB 数据的方法可能不适用于数十 PB 数据。有多种可能的方法,它们各有优缺点,将在下面讨论。最好使用多种方法而不是只使用一种方法,以弥补它们的各种不足。
请记住,如果您备份了一些东西并且从未尝试过恢复它,那么当您实际需要它时,恢复很可能无法正常工作(或者至少会比业务能够容忍的时间更长)。因此,无论您选择哪种备份方法,都要确保也自动化恢复过程,并在备用 ClickHouse 集群上定期练习。
备份到本地磁盘
配置备份目标
在下面的示例中,您将看到备份目标指定为 Disk('backups', '1.zip')
。要准备目标,请将文件添加到 /etc/clickhouse-server/config.d/backup_disk.xml
中,指定备份目标。例如,此文件定义名为 backups
的磁盘,然后将该磁盘添加到 backups > allowed_disk 列表中
<clickhouse>
<storage_configuration>
<disks>
<backups>
<type>local</type>
<path>/backups/</path>
</backups>
</disks>
</storage_configuration>
<backups>
<allowed_disk>backups</allowed_disk>
<allowed_path>/backups/</allowed_path>
</backups>
</clickhouse>
参数
备份可以是完整的或增量的,并且可以包含表(包括物化视图、投影和字典)和数据库。备份可以是同步的(默认)或异步的。它们可以被压缩。备份可以设置密码保护。
BACKUP 和 RESTORE 语句接受数据库和表名称列表、目标(或源)、选项和设置
- 备份的目标或恢复的源。这基于之前定义的磁盘。例如
Disk('backups', 'filename.zip')
- ASYNC:异步备份或恢复
- PARTITIONS:要恢复的分区列表
- SETTINGS
id
:备份或恢复操作的 ID,如果未手动指定,则使用随机生成的 UUID。如果已经存在具有相同id
的正在运行的操作,则会抛出异常。compression_method
和 compression_level- 磁盘上文件的
password
base_backup
:此源先前备份的目标。例如,Disk('backups', '1.zip')
use_same_s3_credentials_for_base_backup
:基本备份到 S3 是否应继承查询的凭据。仅适用于S3
。use_same_password_for_base_backup
:基本备份存档是否应继承查询的密码。structure_only
:如果启用,则允许仅备份或恢复 CREATE 语句,而不备份表的数据storage_policy
:要恢复的表的存储策略。请参阅 使用多个块设备进行数据存储。此设置仅适用于RESTORE
命令。指定的存储策略仅适用于引擎来自MergeTree
系列的表。s3_storage_class
:用于 S3 备份的存储类。例如,STANDARD
azure_attempt_to_create_container
:使用 Azure Blob 存储时,是否尝试创建指定的容器(如果它不存在)。默认值:true。
使用示例
备份然后恢复表
BACKUP TABLE test.table TO Disk('backups', '1.zip')
相应的恢复
RESTORE TABLE test.table FROM Disk('backups', '1.zip')
如果表 test.table
包含数据,则上述 RESTORE 将失败,您必须删除该表才能测试 RESTORE,或者使用设置 allow_non_empty_tables=true
RESTORE TABLE test.table FROM Disk('backups', '1.zip')
SETTINGS allow_non_empty_tables=true
表可以恢复或备份为新的名称
RESTORE TABLE test.table AS test.table2 FROM Disk('backups', '1.zip')
BACKUP TABLE test.table3 AS test.table4 TO Disk('backups', '2.zip')
增量备份
可以通过指定 base_backup
来进行增量备份。
增量备份依赖于基本备份。必须保留基本备份才能能够从增量备份中恢复。
增量存储新数据。设置 base_backup
会导致自上次备份到 Disk('backups', 'd.zip')
以来产生的数据存储到 Disk('backups', 'incremental-a.zip')
中
BACKUP TABLE test.table TO Disk('backups', 'incremental-a.zip')
SETTINGS base_backup = Disk('backups', 'd.zip')
将增量备份和基本备份中的所有数据恢复到新的表 test.table2
中
RESTORE TABLE test.table AS test.table2
FROM Disk('backups', 'incremental-a.zip');
为备份分配密码
写入磁盘的备份可以对文件应用密码
BACKUP TABLE test.table
TO Disk('backups', 'password-protected.zip')
SETTINGS password='qwerty'
恢复
RESTORE TABLE test.table
FROM Disk('backups', 'password-protected.zip')
SETTINGS password='qwerty'
压缩设置
如果您想指定压缩方法或级别
BACKUP TABLE test.table
TO Disk('backups', 'filename.zip')
SETTINGS compression_method='lzma', compression_level=3
恢复特定分区
如果需要恢复与表关联的特定分区,可以指定这些分区。要从备份中恢复分区 1 和 4
RESTORE TABLE test.table PARTITIONS '2', '3'
FROM Disk('backups', 'filename.zip')
备份为 tar 归档文件
备份也可以存储为 tar 归档文件。功能与 zip 相同,只是不支持密码。
将备份写入 tar 文件
BACKUP TABLE test.table TO Disk('backups', '1.tar')
相应的恢复
RESTORE TABLE test.table FROM Disk('backups', '1.tar')
要更改压缩方法,应将正确的文件后缀附加到备份名称。例如,要使用 gzip 压缩 tar 归档文件
BACKUP TABLE test.table TO Disk('backups', '1.tar.gz')
支持的压缩文件后缀为 tar.gz
、.tgz
tar.bz2
、tar.lzma
、.tar.zst
、.tzst
和 .tar.xz
。
检查备份状态
备份命令返回一个id
和status
,并且可以使用该id
获取备份的状态。这对于检查长时间异步备份的进度非常有用。下面的示例显示了尝试覆盖现有备份文件时发生的错误。
BACKUP TABLE helloworld.my_first_table TO Disk('backups', '1.zip') ASYNC
┌─id───────────────────────────────────┬─status──────────┐
│ 7678b0b3-f519-4e6e-811f-5a0781a4eb52 │ CREATING_BACKUP │
└──────────────────────────────────────┴─────────────────┘
1 row in set. Elapsed: 0.001 sec.
SELECT
*
FROM system.backups
where id='7678b0b3-f519-4e6e-811f-5a0781a4eb52'
FORMAT Vertical
Row 1:
──────
id: 7678b0b3-f519-4e6e-811f-5a0781a4eb52
name: Disk('backups', '1.zip')
status: BACKUP_FAILED
num_files: 0
uncompressed_size: 0
compressed_size: 0
error: Code: 598. DB::Exception: Backup Disk('backups', '1.zip') already exists. (BACKUP_ALREADY_EXISTS) (version 22.8.2.11 (official build))
start_time: 2022-08-30 09:21:46
end_time: 2022-08-30 09:21:46
1 row in set. Elapsed: 0.002 sec.
除了system.backups
表之外,所有备份和恢复操作也都会记录在系统日志表backup_log中。
SELECT *
FROM system.backup_log
WHERE id = '7678b0b3-f519-4e6e-811f-5a0781a4eb52'
ORDER BY event_time_microseconds ASC
FORMAT Vertical
Row 1:
──────
event_date: 2023-08-18
event_time_microseconds: 2023-08-18 11:13:43.097414
id: 7678b0b3-f519-4e6e-811f-5a0781a4eb52
name: Disk('backups', '1.zip')
status: CREATING_BACKUP
error:
start_time: 2023-08-18 11:13:43
end_time: 1970-01-01 03:00:00
num_files: 0
total_size: 0
num_entries: 0
uncompressed_size: 0
compressed_size: 0
files_read: 0
bytes_read: 0
Row 2:
──────
event_date: 2023-08-18
event_time_microseconds: 2023-08-18 11:13:43.174782
id: 7678b0b3-f519-4e6e-811f-5a0781a4eb52
name: Disk('backups', '1.zip')
status: BACKUP_FAILED
error: Code: 598. DB::Exception: Backup Disk('backups', '1.zip') already exists. (BACKUP_ALREADY_EXISTS) (version 23.8.1.1)
start_time: 2023-08-18 11:13:43
end_time: 2023-08-18 11:13:43
num_files: 0
total_size: 0
num_entries: 0
uncompressed_size: 0
compressed_size: 0
files_read: 0
bytes_read: 0
2 rows in set. Elapsed: 0.075 sec.
配置BACKUP/RESTORE以使用S3端点
要将备份写入S3存储桶,您需要三条信息。
- S3端点,例如
https://mars-doc-test.s3.amazonaws.com/backup-S3/
- 访问密钥ID,例如
ABC123
- 密钥访问密钥,例如
Abc+123
创建S3存储桶的方法在将S3对象存储用作ClickHouse磁盘中进行了介绍,保存策略后,请返回此文档,无需配置ClickHouse来使用S3存储桶。
备份的目标将这样指定。
S3('<S3 endpoint>/<directory>', '<Access key ID>', '<Secret access key>')
CREATE TABLE data
(
`key` Int,
`value` String,
`array` Array(String)
)
ENGINE = MergeTree
ORDER BY tuple()
INSERT INTO data SELECT *
FROM generateRandom('key Int, value String, array Array(String)')
LIMIT 1000
创建基本(初始)备份
增量备份需要从基本备份开始,此示例稍后将用作基本备份。S3目标的第一个参数是S3端点,后跟用于此备份的存储桶中的目录。在此示例中,目录名为my_backup
。
BACKUP TABLE data TO S3('https://mars-doc-test.s3.amazonaws.com/backup-S3/my_backup', 'ABC123', 'Abc+123')
┌─id───────────────────────────────────┬─status─────────┐
│ de442b75-a66c-4a3c-a193-f76f278c70f3 │ BACKUP_CREATED │
└──────────────────────────────────────┴────────────────┘
添加更多数据
增量备份填充了基本备份和正在备份的表的当前内容之间的差异。在进行增量备份之前,请添加更多数据。
INSERT INTO data SELECT *
FROM generateRandom('key Int, value String, array Array(String)')
LIMIT 100
进行增量备份
此备份命令类似于基本备份,但添加了SETTINGS base_backup
和基本备份的位置。请注意,增量备份的目标目录与基本备份的目标目录不同,它使用相同的端点,但在存储桶中使用不同的目标目录。基本备份位于my_backup
中,增量备份将写入my_incremental
。
BACKUP TABLE data TO S3('https://mars-doc-test.s3.amazonaws.com/backup-S3/my_incremental', 'ABC123', 'Abc+123') SETTINGS base_backup = S3('https://mars-doc-test.s3.amazonaws.com/backup-S3/my_backup', 'ABC123', 'Abc+123')
┌─id───────────────────────────────────┬─status─────────┐
│ f6cd3900-850f-41c9-94f1-0c4df33ea528 │ BACKUP_CREATED │
└──────────────────────────────────────┴────────────────┘
从增量备份恢复
此命令将增量备份恢复到一个新表data3
中。请注意,恢复增量备份时,基本备份也将包含在内。恢复时,只需指定增量备份即可。
RESTORE TABLE data AS data3 FROM S3('https://mars-doc-test.s3.amazonaws.com/backup-S3/my_incremental', 'ABC123', 'Abc+123')
┌─id───────────────────────────────────┬─status───┐
│ ff0c8c39-7dff-4324-a241-000796de11ca │ RESTORED │
└──────────────────────────────────────┴──────────┘
验证计数
原始表data
中有两个插入操作,一个包含1000行,一个包含100行,总共1100行。验证恢复后的表是否包含1100行。
SELECT count()
FROM data3
┌─count()─┐
│ 1100 │
└─────────┘
验证内容
这会比较原始表data
和恢复后的表data3
的内容。
SELECT throwIf((
SELECT groupArray(tuple(*))
FROM data
) != (
SELECT groupArray(tuple(*))
FROM data3
), 'Data does not match after BACKUP/RESTORE')
使用S3磁盘进行BACKUP/RESTORE
还可以通过在ClickHouse存储配置中配置S3磁盘来将BACKUP
/RESTORE
到S3。通过将文件添加到/etc/clickhouse-server/config.d
来配置磁盘。
<clickhouse>
<storage_configuration>
<disks>
<s3_plain>
<type>s3_plain</type>
<endpoint></endpoint>
<access_key_id></access_key_id>
<secret_access_key></secret_access_key>
</s3_plain>
</disks>
<policies>
<s3>
<volumes>
<main>
<disk>s3_plain</disk>
</main>
</volumes>
</s3>
</policies>
</storage_configuration>
<backups>
<allowed_disk>s3_plain</allowed_disk>
</backups>
</clickhouse>
然后像往常一样进行BACKUP
/RESTORE
。
BACKUP TABLE data TO Disk('s3_plain', 'cloud_backup');
RESTORE TABLE data AS data_restored FROM Disk('s3_plain', 'cloud_backup');
但请记住
- 此磁盘不应用于
MergeTree
本身,仅用于BACKUP
/RESTORE
。 - 如果您的表由S3存储支持,并且磁盘类型不同,它不会使用
CopyObject
调用将部分复制到目标存储桶,而是会下载并上传它们,这非常低效。对于此用例,最好使用BACKUP ... TO S3(<endpoint>)
语法。
替代方案
ClickHouse将数据存储在磁盘上,并且有很多方法可以备份磁盘。以下是一些过去使用过的方法,这些方法可能非常适合您的环境。
在其他地方复制源数据
通常,导入到ClickHouse的数据通过某种持久队列传递,例如Apache Kafka。在这种情况下,可以配置一组额外的订阅者,这些订阅者将在数据写入ClickHouse的同时读取相同的数据流,并将其存储在某个地方的冷存储中。大多数公司已经有一些默认推荐的冷存储,可能是对象存储或分布式文件系统,例如HDFS。
文件系统快照
某些本地文件系统提供快照功能(例如,ZFS),但它们可能不是为服务实时查询的最佳选择。一个可能的解决方案是使用这种文件系统创建其他副本,并将它们从用于SELECT
查询的分布式表中排除。此类副本上的快照将无法访问任何修改数据的查询。作为额外好处,这些副本可能具有特殊的硬件配置,每个服务器连接更多磁盘,这将具有成本效益。
对于较小的数据量,简单的INSERT INTO ... SELECT ...
到远程表也可以。
分区操作
ClickHouse允许使用ALTER TABLE ... FREEZE PARTITION ...
查询创建表分区的本地副本。这是使用指向/var/lib/clickhouse/shadow/
文件夹的硬链接实现的,因此它通常不会为旧数据消耗额外的磁盘空间。创建的文件副本不受ClickHouse服务器处理,因此您可以将它们保留在那里:您将拥有一个简单的备份,不需要任何额外的外部系统,但它仍然容易受到硬件问题的影响。因此,最好将它们远程复制到另一个位置,然后删除本地副本。分布式文件系统和对象存储仍然是不错的选择,但具有足够容量的普通附加文件服务器也可以(在这种情况下,传输将通过网络文件系统或可能是rsync进行)。可以使用ALTER TABLE ... ATTACH PARTITION ...
从备份中恢复数据。
有关与分区操作相关的查询的更多信息,请参阅ALTER文档。
可以使用第三方工具来自动执行此方法:clickhouse-backup。
禁止并发备份/恢复的设置
要禁止并发备份/恢复,您可以分别使用以下设置。
<clickhouse>
<backups>
<allow_concurrent_backups>false</allow_concurrent_backups>
<allow_concurrent_restores>false</allow_concurrent_restores>
</backups>
</clickhouse>
两者的默认值都是true,因此默认情况下允许并发备份/恢复。当这些设置在集群上为false时,一次只能在集群上运行1个备份/恢复。
配置BACKUP/RESTORE以使用AzureBlobStorage端点
要将备份写入AzureBlobStorage容器,您需要以下信息
- AzureBlobStorage端点连接字符串/URL,
- 容器,
- 路径,
- 帐户名称(如果指定了URL)
- 帐户密钥(如果指定了URL)
备份的目标将这样指定。
AzureBlobStorage('<connection string>/<url>', '<container>', '<path>', '<account name>', '<account key>')
BACKUP TABLE data TO AzureBlobStorage('DefaultEndpointsProtocol=http;AccountName=devstoreaccount1;AccountKey=Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==;BlobEndpoint=http://azurite1:10000/devstoreaccount1/;',
'test_container', 'data_backup');
RESTORE TABLE data AS data_restored FROM AzureBlobStorage('DefaultEndpointsProtocol=http;AccountName=devstoreaccount1;AccountKey=Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==;BlobEndpoint=http://azurite1:10000/devstoreaccount1/;',
'test_container', 'data_backup');