共计 16810 个字符,预计需要花费 43 分钟才能阅读完成。
clickhouse 的数据备份和复原性能在大数据运维中是十分罕用的性能,目前也有很多比拟优良的开源计划可供选择,比方 clickhouse-backup,以及 clickhouse 自带的 clickhouse-copier
。
本文介绍应用 clickhouse 自带的 BACKUP 和 RESTORE 命令进行备份和复原。
我认为,一个比拟好的备份复原工具,至多须要满足以下几个性能:
- 能够批量抉择表
- 能够增量备份
- 能够比拟不便地对数据进行复原
- 反对集群操作
- 能够抉择多个备份目的地,如 Local、S3、HDFS 等
- 为了不便复原,元数据也须要一起备份
根本语法
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 DATABASES [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>')]
从该语法中,咱们大抵能读到以下信息:
- 能够指定某一张表的某个 partition 进行备份
- 能够指定某个数据库进行备份,且能够剔除该数据库中的某些表不进行备份
- 能够备份长期表,视图
- 能够全量备份所有表,所有数据库(反对黑名单排除)
- 反对在集群上做备份
- 反对备份到 File、Disk 和 S3
- 反对压缩备份
- 反对增量备份
接下来咱们以实战的形式,演示一下该命令的一些操作。
备份到文件
筹备工作
配置筹备
首先,咱们须要在配置文件中退出以下内容:
<clickhouse>
<backups>
<allowed_path>/data01/backup</allowed_path>
</backups>
</clickhouse>
代表容许备份,且备份目录为/data01/backup
。
数据筹备
我以后集群信息如下:
该集群有 3 个节点,其中 ck93 和 ck94 组成一个分片,ck96 独自一个分片。
咱们在集群上创立一张表,并导入一些数据:
CREATE TABLE t1 ON CLUSTER abc
(
`id` Int64,
`timestamp` DateTime,
`value` Float32
)
ENGINE = ReplicatedMergeTree
PARTITION BY toYYYYMMDD(timestamp)
ORDER BY (id, timestamp)
并向该表写入了 1 亿条数据:
数据分布如下:
--shard1
ck94 :) select count() from t1;
SELECT count()
FROM t1
Query id: a9b4610b-daa9-48b3-806b-3136657d2d9e
┌──count()─┐
│ 50000000 │
└──────────┘
1 row in set. Elapsed: 0.002 sec.
--shard2
ck96 :) select count() from t1;
SELECT count()
FROM t1
Query id: f655d9ce-0176-4220-8e35-69d2261fc60d
┌──count()─┐
│ 50000000 │
└──────────┘
1 row in set. Elapsed: 0.003 sec.
备份
别离在 shard1 和 shard2 上执行 backup 命令如下:
--shard1
ck94 :) backup table default.t1 to File('20230528');
BACKUP TABLE default.t1 TO File('20230528')
Query id: c1214b1c-dc84-48f4-9d01-c9adebf21bf3
┌─id───────────────────────────────────┬─status─────────┐
│ 1d30a0c9-4094-43b7-b2b6-9645e79d7fc1 │ BACKUP_CREATED │
└──────────────────────────────────────┴────────────────┘
1 row in set. Elapsed: 0.050 sec.
--shard2
ck96 :) backup table default.t1 to File('20230528');
BACKUP TABLE default.t1 TO File('20230528')
Query id: 40df720f-8c2c-47c1-97d1-035186becac2
┌─id───────────────────────────────────┬─status─────────┐
│ 30c9a090-f6a5-4055-a157-5747b1d0772c │ BACKUP_CREATED │
└──────────────────────────────────────┴────────────────┘
1 row in set. Elapsed: 0.049 sec.
执行实现后,在 ck94 的 /data01/backup 目录下,有如下数据生成:
[root@ck94 backup]# tree
20230528/
├── data
│ └── default
│ └── t1
│ ├── 20230416_0_20_4
│ │ ├── checksums.txt
│ │ ├── columns.txt
│ │ ├── count.txt
│ │ ├── default_compression_codec.txt
│ │ ├── id.bin
│ │ ├── id.mrk2
│ │ ├── minmax_timestamp.idx
│ │ ├── partition.dat
│ │ ├── primary.idx
│ │ ├── timestamp.bin
│ │ ├── timestamp.mrk2
│ │ ├── value.bin
│ │ └── value.mrk2
│ ├── 20230416_21_51_6
│ │ ├── checksums.txt
│ │ ├── count.txt
│ │ ├── id.bin
│ │ ├── id.mrk2
│ │ ├── primary.idx
│ │ ├── timestamp.bin
│ │ ├── timestamp.mrk2
│ │ ├── value.bin
│ │ └── value.mrk2
│ ├── 20230416_52_52_0
│ │ ├── checksums.txt
│ │ ├── count.txt
│ │ ├── data.bin
│ │ ├── data.mrk3
│ │ ├── minmax_timestamp.idx
│ │ └── primary.idx
│ ├── 20230416_53_53_0
│ │ ├── checksums.txt
│ │ ├── count.txt
│ │ ├── data.bin
│ │ ├── data.mrk3
│ │ └── primary.idx
│ ├── 20230423_0_5_1
│ │ ├── checksums.txt
│ │ ├── count.txt
│ │ ├── id.bin
│ │ ├── id.mrk2
│ │ ├── minmax_timestamp.idx
│ │ ├── partition.dat
│ │ ├── primary.idx
│ │ ├── timestamp.bin
│ │ ├── timestamp.mrk2
│ │ ├── value.bin
│ │ └── value.mrk2
│ ├── 20230423_12_12_0
│ │ ├── checksums.txt
│ │ ├── count.txt
│ │ ├── id.bin
│ │ ├── id.mrk2
│ │ ├── primary.idx
│ │ ├── timestamp.bin
│ │ ├── timestamp.mrk2
│ │ ├── value.bin
│ │ └── value.mrk2
│ ├── 20230423_13_13_0
│ │ ├── checksums.txt
│ │ ├── id.bin
│ │ ├── id.mrk2
│ │ ├── primary.idx
│ │ ├── timestamp.bin
│ │ ├── timestamp.mrk2
│ │ ├── value.bin
│ │ └── value.mrk2
│ ├── 20230423_14_14_0
│ │ ├── checksums.txt
│ │ ├── id.bin
│ │ ├── id.mrk2
│ │ ├── minmax_timestamp.idx
│ │ ├── primary.idx
│ │ ├── timestamp.bin
│ │ ├── timestamp.mrk2
│ │ ├── value.bin
│ │ └── value.mrk2
│ ├── 20230423_15_15_0
│ │ ├── checksums.txt
│ │ ├── count.txt
│ │ ├── data.bin
│ │ ├── data.mrk3
│ │ └── primary.idx
│ ├── 20230423_16_16_0
│ │ ├── checksums.txt
│ │ ├── count.txt
│ │ ├── data.bin
│ │ ├── data.mrk3
│ │ ├── minmax_timestamp.idx
│ │ └── primary.idx
│ ├── 20230423_6_11_1
│ │ ├── checksums.txt
│ │ ├── id.bin
│ │ ├── id.mrk2
│ │ ├── primary.idx
│ │ ├── timestamp.bin
│ │ ├── timestamp.mrk2
│ │ ├── value.bin
│ │ └── value.mrk2
│ ├── 20230424_0_5_1
│ │ ├── checksums.txt
│ │ ├── id.bin
│ │ ├── id.mrk2
│ │ ├── minmax_timestamp.idx
│ │ ├── partition.dat
│ │ ├── primary.idx
│ │ ├── timestamp.bin
│ │ ├── timestamp.mrk2
│ │ ├── value.bin
│ │ └── value.mrk2
│ ├── 20230424_12_12_0
│ │ ├── checksums.txt
│ │ ├── id.bin
│ │ ├── id.mrk2
│ │ ├── primary.idx
│ │ ├── timestamp.bin
│ │ ├── timestamp.mrk2
│ │ ├── value.bin
│ │ └── value.mrk2
│ ├── 20230424_13_13_0
│ │ ├── checksums.txt
│ │ ├── id.bin
│ │ ├── id.mrk2
│ │ ├── primary.idx
│ │ ├── timestamp.bin
│ │ ├── timestamp.mrk2
│ │ ├── value.bin
│ │ └── value.mrk2
│ ├── 20230424_14_14_0
│ │ ├── checksums.txt
│ │ ├── id.bin
│ │ ├── id.mrk2
│ │ ├── primary.idx
│ │ ├── timestamp.bin
│ │ ├── timestamp.mrk2
│ │ ├── value.bin
│ │ └── value.mrk2
│ ├── 20230424_15_15_0
│ │ ├── checksums.txt
│ │ ├── id.bin
│ │ ├── id.mrk2
│ │ ├── primary.idx
│ │ ├── timestamp.bin
│ │ ├── timestamp.mrk2
│ │ ├── value.bin
│ │ └── value.mrk2
│ ├── 20230424_16_16_0
│ │ ├── checksums.txt
│ │ ├── id.bin
│ │ ├── id.mrk2
│ │ ├── primary.idx
│ │ ├── timestamp.bin
│ │ ├── timestamp.mrk2
│ │ ├── value.bin
│ │ └── value.mrk2
│ └── 20230424_6_11_1
│ ├── checksums.txt
│ ├── id.bin
│ ├── id.mrk2
│ ├── primary.idx
│ ├── timestamp.bin
│ ├── timestamp.mrk2
│ ├── value.bin
│ └── value.mrk2
└── metadata
└── default
└── t1.sql
ck98 目录同样也是如此,这里就不贴出来了。
从以上目录构造,能够看出,备份的文件分为 data
和metadata
两个子目录、data
用来存储数据,metadata
用来存储表构造,并且外部依据数据库、表、partition 做了层级辨别。
看一下 ck96 上备份目录大小:
[root@ck94 backup]# du -sh 20230528/
108M 20230528/
[root@ck96 backup]# du -sh 20230528/
112M 20230528/
可见备份的数据也是压缩后的数据。(压缩前有 1.49G)
接下来,咱们试试间接在集群层面进行备份:
ck94 :) backup table default.t1 on cluster abc to File('abc_20230528');
BACKUP TABLE default.t1 ON CLUSTER abc TO File('abc_20230528')
Query id: e0763743-a6bc-430a-a28b-c493c7fd6478
0 rows in set. Elapsed: 0.188 sec.
Received exception from server (version 23.3.1):
Code: 655. DB::Exception: Received from localhost:19000. DB::Exception: Got error from 192%2E168%2E101%2E93:19000. DB::Exception: Lock file .lock suddenly disappeared while writing backup File('abc_20230528'). (FAILED_TO_SYNC_BACKUP_OR_RESTORE)
它报了一个错,这个谬误在 github 上有人提了 issue:https://github.com/ClickHouse/ClickHouse/issues/41313,粗心就是,在集群备份时,选取的备份的目录不能是各个节点本人的目录,须要一个近程共享目录(这一点对于 to Disk 是同样实用的)。这里我就没有尝试了,下次有暇能够测试一下应用 NFS 目录在集群层面进行备份。
由以上信息,能够得出以下论断:
- 备份速度十分快,1 亿条数据根本不到 1 秒就能实现
- 备份的同时,元数据也进行了备份,因而能够疾速复原
- 备份是同比压缩备份的,不会呈现数据收缩
- 无奈进行集群级别备份(除非设置为近程共享目录)
复原
复原次要应用 RESTORE 命令。
当原始表有数据时,间接复原是会报错的:
ck94 :) RESTORE TABLE default.t1 FROM File('20230528');
RESTORE TABLE default.t1 FROM File('20230528')
Query id: e5d3eea4-5a58-4283-b9ce-d2fa9fa5fa1c
0 rows in set. Elapsed: 0.009 sec.
Received exception from server (version 23.3.1):
Code: 608. DB::Exception: Received from localhost:19000. DB::Exception: Cannot restore the table default.t1 because it already contains some data. You can set structure_only=true or allow_non_empty_tables=true to overcome that in the way you want: While restoring data of table default.t1. (CANNOT_RESTORE_TABLE)
起因是原表曾经有数据了。解决方案有 3 个:
计划 1:备份到另一张表
具体操作如下:
ck94 :) RESTORE TABLE default.t1 AS default.t2 FROM File('20230528');
RESTORE TABLE default.t1 AS default.t2 FROM File('20230528')
Query id: e39dc666-3a00-4556-b8df-69e0e8d7fb63
0 rows in set. Elapsed: 0.017 sec.
Received exception from server (version 23.3.1):
Code: 253. DB::Exception: Received from localhost:19000. DB::Exception: Replica /clickhouse/tables/abc/default/t1/1/replicas/192.168.101.94 already exists: While creating table default.t2. (REPLICA_ALREADY_EXISTS)
上述操作报了一个错,该报错的起因是咱们在创立 t2 表时,zk 门路曾经存在了。
这是因为咱们设置的 zk 默认门路如下:
<default_replica_name>{replica}</default_replica_name>
<default_replica_path>/clickhouse/tables/{cluster}/{database}/{table}/{shard}</default_replica_path>
而咱们创立 t1 表时,指定的 engine 为 ReplicatedMergeTree,没有带任何参数,默认应用的就是这个地址,因而咱们创立 t2 表时,zoopath 抵触,导致不能复原胜利。
咱们尝试手动建表来复原,依然不能胜利,起因是咱们备份的元数据中,t1 表的 zoopath 曾经与 t2 表不统一了,无奈复原。
ck94 :) create table t2 on cluster abc AS t1 ENGINE=ReplicatedMergeTree() PARTITION BY toYYYYMMDD(timestamp) ORDER BY (id, timestamp);
CREATE TABLE t2 ON CLUSTER abc AS t1
ENGINE = ReplicatedMergeTree
PARTITION BY toYYYYMMDD(timestamp)
ORDER BY (id, timestamp)
Query id: 61d6f3eb-5c58-4e88-a5aa-63712d538d8c
┌─host───────────┬──port─┬─status─┬─error─┬─num_hosts_remaining─┬─num_hosts_active─┐
│ x.x.x.x │ 19000 │ 0 │ │ 2 │ 0 │
│ x.x.x.x │ 19000 │ 0 │ │ 1 │ 0 │
│ x.x.x.x │ 19000 │ 0 │ │ 0 │ 0 │
└────────────────┴───────┴────────┴───────┴─────────────────────┴──────────────────┘
3 rows in set. Elapsed: 0.115 sec.
ck94 :) RESTORE TABLE default.t1 AS default.t2 FROM File('20230528');
RESTORE TABLE default.t1 AS default.t2 FROM File('20230528')
Query id: fa2086ec-3a65-436e-b871-eb7cf960c11e
0 rows in set. Elapsed: 0.007 sec.
Received exception from server (version 23.3.1):
Code: 608. DB::Exception: Received from localhost:19000. DB::Exception: The table has a different definition: CREATE TABLE default.t2 (`id` Int64, `timestamp` DateTime, `value` Float32) ENGINE = ReplicatedMergeTree('/clickhouse/tables/{cluster}/default/t2/{shard}', '{replica}') PARTITION BY toYYYYMMDD(timestamp) ORDER BY (id, timestamp) SETTINGS index_granularity = 8192 comparing to its definition in the backup: CREATE TABLE default.t2 (`id` Int64, `timestamp` DateTime, `value` Float32) ENGINE = ReplicatedMergeTree('/clickhouse/tables/{cluster}/default/t1/{shard}', '{replica}') PARTITION BY toYYYYMMDD(timestamp) ORDER BY (id, timestamp) SETTINGS index_granularity = 8192: While checking table default.t2. (CANNOT_RESTORE_TABLE)
因而,这个计划无解,除非咱们批改掉 zoopath 的规定。
计划 2:设置容许非空表备份
ck94 :) RESTORE TABLE default.t1 FROM File('20230528') SETTINGS allow_non_empty_tables=true;
RESTORE TABLE default.t1 FROM File('20230528') SETTINGS allow_non_empty_tables = 1
Query id: 8bc125ef-5a48-4595-9b1a-977b62e98f4e
┌─id───────────────────────────────────┬─status───┐
│ 7f2662a8-8bab-4b4b-bdc3-24a3df5231f9 │ RESTORED │
└──────────────────────────────────────┴──────────┘
1 row in set. Elapsed: 0.114 sec.
这种计划是可行的,然而,咱们查问一下复原后的数据:
ck94 :) select count() from t1;
SELECT count()
FROM t1
Query id: 7510842d-7278-4bf7-9539-4c5c34b38043
┌───count()─┐
│ 100000000 │
└───────────┘
1 row in set. Elapsed: 0.002 sec.
ck94 :)
它在原有的数据根底上翻了个倍。等于说数据冗余了一倍,如果设置的不是去重的引擎,那么这些数据将始终存在,将会大大影响磁盘占用和查问效率。
因而,这种伎俩只实用于原表曾经不存在,或者原表数据清空的状况下做复原。
计划 3:设置仅复原 structure
ck94 :) RESTORE TABLE default.t1 FROM File('20230528') SETTINGS structure_only=true;
RESTORE TABLE default.t1 FROM File('20230528') SETTINGS structure_only = 1
Query id: 77ce87fd-2eb3-47df-8a96-3423c248a54d
┌─id───────────────────────────────────┬─status───┐
│ a0d63803-4b32-49be-983f-6d71d8c7451f │ RESTORED │
└──────────────────────────────────────┴──────────┘
1 row in set. Elapsed: 0.004 sec.
ck94 :) select count() from t1;
SELECT count()
FROM t1
Query id: cd0404cd-1dd0-49f0-a828-de3e6fa4e8d9
┌───count()─┐
│ 100000000 │
└───────────┘
1 row in set. Elapsed: 0.003 sec.
该操作数据并没有翻倍,然而,该操作仅仅是同步了表 schema,并没有同步数据,如果原表数据因为某种原因缺失了(比方只剩下了 100w 条),当咱们执行 restore 语句,依然是 100w 条数据,而没有把原始的 5000w 条数据都复原回来。
ck94 :) select count() from t1;
SELECT count()
FROM t1
Query id: b8f19f4a-0339-44d7-8618-f3d741ebc679
┌─count()─┐
│ 1000000 │
└─────────┘
1 row in set. Elapsed: 0.003 sec.
ck94 :) RESTORE TABLE default.t1 FROM File('20230528') SETTINGS structure_only=true;
RESTORE TABLE default.t1 FROM File('20230528') SETTINGS structure_only = 1
Query id: 94e70a0d-4910-4802-8af2-3606fc3a7a1e
┌─id───────────────────────────────────┬─status───┐
│ d18869cc-880a-42d0-9405-863e6ac33216 │ RESTORED │
└──────────────────────────────────────┴──────────┘
1 row in set. Elapsed: 0.003 sec.
ck94 :) select count() from t1;
SELECT count()
FROM t1
Query id: 32df3010-00c2-48a1-9f3d-cfc42c69ad12
┌─count()─┐
│ 1000000 │
└─────────┘
1 row in set. Elapsed: 0.002 sec.
如果表不存在,咱们应用该命令进行复原,也仅仅复原的是表 schema,而不是所有数据:
ck94 :) drop table t1 sync;
DROP TABLE t1 SYNC
Query id: f3a596b8-f18e-4606-908d-8da26d5efd37
Ok.
0 rows in set. Elapsed: 0.022 sec.
ck94 :) RESTORE TABLE default.t1 FROM File('20230528') SETTINGS structure_only=true;
RESTORE TABLE default.t1 FROM File('20230528') SETTINGS structure_only = 1
Query id: 5c27980f-857c-4b87-93ab-70375ef51c78
┌─id───────────────────────────────────┬─status───┐
│ 7191ab88-f59a-477b-924f-93ce34ed3dea │ RESTORED │
└──────────────────────────────────────┴──────────┘
1 row in set. Elapsed: 0.606 sec.
ck94 :) select count() from t1;
SELECT count()
FROM t1
Query id: 2a11f16e-11ad-44cd-88f7-66e0008f8c16
┌─count()─┐
│ 0 │
└─────────┘
1 row in set. Elapsed: 0.002 sec.
当然,如果表都不存在了,咱们应用最原始的命令就能复原:
ck94 :) RESTORE TABLE default.t1 FROM File('20230528');
RESTORE TABLE default.t1 FROM File('20230528')
Query id: efb398bb-8368-4aa8-9efa-dacb316faca0
┌─id───────────────────────────────────┬─status───┐
│ 54a3b913-e91e-456b-84ce-d3aa10237ff6 │ RESTORED │
└──────────────────────────────────────┴──────────┘
1 row in set. Elapsed: 0.146 sec.
ck94 :) select count() from t1;
SELECT count()
FROM t1
Query id: a6fa6b7b-834c-4c41-bf9c-658695b19bc7
┌──count()─┐
│ 50000000 │
└──────────┘
1 row in set. Elapsed: 0.003 sec.
增量备份
表的增量备份应该是数据备份最根本的需要,如果每次只反对全量备份,第一,效率上难以保障(尽管这种备份看起来速度很快,因为相当于间接拷贝了数据目录),二来磁盘空间限度,不太可能频繁全量备份。
咱们在 ck96 上插入 100 条数据:
ck96 :) insert into t1 select * from t1 limit 100;
INSERT INTO t1 SELECT *
FROM t1
LIMIT 100
Query id: a4515af0-f32d-4af2-b4ec-fae2b08425e7
Ok.
0 rows in set. Elapsed: 0.010 sec.
ck96 :) select count() from t1;
SELECT count()
FROM t1
Query id: 63c7b438-3dcd-4ea7-bb98-c6ed90021eac
┌──count()─┐
│ 50000100 │
└──────────┘
1 row in set. Elapsed: 0.004 sec.
咱们的诉求是只备份这增量的 100 条数据,而不备份存量的 5000w 条数据。
咱们能够通过设置 base_backup 来实现,即:在某次备份的根底上进行备份。
操作如下:
ck96 :) backup table default.t1 to File('20230529') SETTINGS base_backup = File('20230528');
BACKUP TABLE default.t1 TO File('20230529') SETTINGS base_backup = File('20230528')
Query id: 41e871ae-63bf-420a-956d-254a18c7a4af
┌─id───────────────────────────────────┬─status─────────┐
│ dd5fcaf4-303c-4246-a255-f75ac664af4d │ BACKUP_CREATED │
└──────────────────────────────────────┴────────────────┘
1 row in set. Elapsed: 0.027 sec.
能够看到,在备份目录下,呈现了 20230529 的目录:
[root@ck96 backup]# ll
total 8
drwxr-x--- 4 clickhouse clickhouse 4096 May 29 07:06 20230528
drwxr-x--- 3 clickhouse clickhouse 4096 May 29 07:38 20230529
[root@ck96 backup]# du -sh *
112M 20230528
72K 20230529
并且该目录是没有元数据的,只有数据目录:
[root@ck96 backup]# tree 20230529
20230529
└── data
└── default
└── t1
└── 20230416_55_55_0
├── checksums.txt
├── count.txt
├── data.bin
├── data.mrk3
├── minmax_timestamp.idx
└── primary.idx
查看 count.txt,可见此次备份的数据条数为 100 条:
[root@ck96 backup]# cat 20230529/data/default/t1/20230416_55_55_0/count.txt
100[root@ck96 backup]#
备份到 Disk
配置筹备
咱们在存储策略里减少一个 backup 的 Disk:
<storage_configuration>
<disks>
<backup>
<path>/data01/ssd1/</path>
<type>local</type>
</backup>
</disks>
</storage_configuration>
而后在 backups
中减少容许备份的磁盘:
<backups>
<allowed_disk>backup</allowed_disk>
<allowed_path>/data01/backup</allowed_path>
</backups>
数据备份
咱们仍以 t1 表为例,备份命令为:
ck94 :) BACKUP TABLE default.t1 TO Disk('backup', 't1.zip');
BACKUP TABLE default.t1 TO Disk('backup', 't1.zip')
Query id: 07df2cf7-de21-4a9a-bf0d-98e96d5e5a08
Connecting to localhost:19000 as user default.
Connected to ClickHouse server version 23.3.1 revision 54462.
┌─id───────────────────────────────────┬─status─────────┐
│ e0765c7a-afe4-4475-843c-92769d7b8089 │ BACKUP_CREATED │
└──────────────────────────────────────┴────────────────┘
1 row in set. Elapsed: 2.754 sec.
能够看到,同样是 5000w 条数据,该操作就比间接备份到 File 要慢近 100 倍左右。它这个慢,次要在于,备份到 File,是间接目录拷贝,而备份到 Disk,则多了压缩的过程。
然而咱们同时也看到,备份后的数据是以 zip 压缩的,压缩后数据大小为 69M,比之原始数据的 108M 要小将近 1 倍。
[root@ck94 ssd1]# du -sh t1.zip
69M t1.zip
咱们将该 zip 文件解压进去:
[root@ck94 ssd1]# du -sh *
108M data
4.0K metadata
69M t1.zip
可见原始数据依然是 108M 大小。
至于集群层面备份,以及增量备份、数据恢复,与 File 都是一样的,这里就不多做演示了。
还有一些精密的玩法,如备份到磁盘时对压缩包进行加密,仅备份某一个 partition,以及指定压缩算法和压缩等级等。
因为 Disk 自身的个性,咱们甚至能够将 Disk 设置到 HDFS 以及 S3 上,因而能够应用该性能,将数据间接备份到这些对象存储之上。
当然备份到 S3,还有另外的计划。BACKUP 和 RESTORE 命令是间接反对了备份到 S3 的。接下来咱们就来演示一下。
备份到 S3
S3 环境筹备
咱们应用 docker 启动一个 minio 来模仿 S3 环境。启动命令如下:
docker run --restart=always -itd --name minio \
--publish 49000:9000 \
--publish 49001:9001 \
--privileged=true \
-e TZ="Asia/Shanghai" \
-e MINIO_ROOT_USER=minio \
-e MINIO_ROOT_PASSWORD=minio@123 \
-e MINIO_REGION_NAME=zh-west-1 \
bitnami/minio:latest
启动后,进入http://localhost:49001/browser
即可关上前端页面。
咱们在用户名和明码处填写启动 docker 时传入的环境变量,即:minio/minio@123
,登陆进去后界面如下所示:
数据备份到 S3 须要以下信息:
- endpoint
- Access key ID
- Secret access key
endpoint 咱们曾经有了,接下来咱们创立一组 access key。
如下图所示,顺次点击 Access Keys,Create Access key:点击 Create:
咱们须要将这一组 access key 和 secret key 记下来:
数据备份是要备份到 bucket 上面的,咱们须要提前创立好 bucket:
如图,我提前创立了一个名为 backup 的 bucket:
至此,筹备工作实现。
数据备份
咱们仍然备份 t1 表,命令如下:
ck94 :) BACKUP TABLE default.t1 TO S3('http://192.168.101.94:49000/backup/t1', 'W0Plkfnyy8clAvPp', 'oNGbi5vUvJC9huolu0k8lkl05nsUxv6p');
BACKUP TABLE default.t1 TO S3('http://192.168.101.94:49000/backup/t1', 'W0Plkfnyy8clAvPp', 'oNGbi5vUvJC9huolu0k8lkl05nsUxv6p')
Query id: a02479c8-d54e-4bdc-b3f6-0bb638754dcc
┌─id───────────────────────────────────┬─status─────────┐
│ 040d8cb6-7d2c-45c4-95ad-844aa4e87ce2 │ BACKUP_CREATED │
└──────────────────────────────────────┴────────────────┘
1 row in set. Elapsed: 0.515 sec.
这时,咱们上浏览器曾经能查看到数据了:
咱们尝试在集群上备份数据:
ck94 :) BACKUP TABLE default.t1 ON CLUSTER abc TO S3('http://192.168.101.94:49000/backup/t1_all', 'W0Plkfnyy8clAvPp', 'oNGbi5vUvJC9huolu0k8lkl05nsUxv6p');
BACKUP TABLE default.t1 ON CLUSTER abc TO S3('http://192.168.101.94:49000/backup/t1_all', 'W0Plkfnyy8clAvPp', 'oNGbi5vUvJC9huolu0k8lkl05nsUxv6p')
Query id: e88f933b-3f73-47e1-8502-25a44c8727cd
┌─id───────────────────────────────────┬─status─────────┐
│ a846e557-953e-4f9c-bbe2-d512baf0a030 │ BACKUP_CREATED │
└──────────────────────────────────────┴────────────────┘
1 row in set. Elapsed: 6.653 sec.
从它的目录排布,它曾经主动依照 shard 做了辨别:
因为 S3 是近程共享目录,是能够执行的,不过整个执行过程比拟耗时。1 亿一条数据,用时靠近 6 秒。不过从单节点备份只耗时 0.5 秒来看,该工夫并不是随着数据量的减少而正比例递增的,而是次要耗时在与 zookeeper 的交互,获取元数据上。
增量备份与数据恢复与 File 相似,此处就不多做介绍了。
总结
BACKUP 和 RESTORE 命令的备份复原的长处是不言而喻的:
- 无论是备份还是复原,执行效率都十分高
- 同压缩比备份,反对不同的备份目的地
- 反对增量备份
但毛病也是有的,我认为次要是以下几点:
- 须要凋谢配置,操作性上不太敌对
- 集群备份到本地时,必须要选共享目录,如果每个分片独自备份,则无奈进行数据汇总,存留于节点本身,与没有备份没什么区别,还不如多加正本
- 数据恢复性能尚不太齐备