clickhouse的BACKUP/RESTORE命令介绍

news2025/1/10 21:14:03

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目录同样也是如此,这里就不贴出来了。
从以上目录结构,可以看出,备份的文件分为datametadata两个子目录、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 │ 190000 │       │                   20 │
│ x.x.x.x │ 190000 │       │                   10 │
│ x.x.x.x │ 190000 │       │                   00 │
└────────────────┴───────┴────────┴───────┴─────────────────────┴──────────────────┘

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命令的备份恢复的优点是显而易见的:

  • 无论是备份还是恢复,执行效率都非常高
  • 同压缩比备份,支持不同的备份目的地
  • 支持增量备份

但缺点也是有的,我认为主要是以下几点:

  • 需要开放配置,操作性上不太友好
  • 集群备份到本地时,必须要选共享目录,如果每个分片单独备份,则无法进行数据汇总,存留于节点自身,与没有备份没什么区别,还不如多加副本
  • 数据恢复功能尚不太完备

推荐一个零声学院免费教程,个人觉得老师讲得不错,分享给大家:[Linux,Nginx,ZeroMQ,MySQL,Redis,
fastdfs,MongoDB,ZK,流媒体,CDN,P2P,K8S,Docker,
TCP/IP,协程,DPDK等技术内容,点击立即学习: C/C++Linux服务器开发/高级架构师

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/582554.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

vue实现验证码登陆

我们在使用 vue进行前端开发时&#xff0c;都需要登录验证&#xff0c;而在登录的过程中&#xff0c;用户需要输入自己的用户名和密码&#xff0c;如果是输错的话还需要进行再次输入。这样不仅容易造成用户密码泄露&#xff0c;还会影响用户体验。因此在我们的系统中都会存在验…

【深入浅出Spring Security(二)】Spring Security的实现原理

Spring Security的实现原理 一、实现原理二、内置Filter以及默认加载的Filter三、自动配置分析&#xff08;SpringBootWebSecurityConfiguration&#xff09;ConditionalOnMissingBean 四、自己配置SecurityFilterChain 一、实现原理 在 Spring Security 中&#xff0c;认证、…

交直流混合配电网潮流计算(统一求解法)

目录 1 主要内容 算例模型 统一求解法迭代方程 算法流程图 2 部分代码 3 程序结果 4 程序链接 1 主要内容 该程序为matlab代码&#xff0c;采用统一求解法对交直流混合配电网进行潮流计算&#xff0c;统一迭代法又称统一求解法&#xff0c;其思路是将混联系统中的交流网…

FPGA串口发送数据

module Uart(input clk, //H3input rst, //F10output reg TX //串口发送脚M12 );reg[9:0] buffer 10b1_1100_1010_0; //接收方&#xff1a;0XCA parameter SND_T 5208; //9600bps reg [15:0] cnt 0; //数据宽度计数 always (posedge clk or negedge rst) beginif(!rs…

Unity游戏优化指南大全(持续更新中!)

Unity游戏优化指南大全 三个官方优化提示&#xff1a; 性能和优化 (Performance and Optimization) - 关于性能分析器以及性能和优化技巧的 Unity 学习教程。Best practices for performance optimization in Unity - 优化应用程序的最佳实践指南。Ultimate guide to profili…

【gstreamer】入门介绍

概述 GStreamer是一个基于流媒体的框架&#xff0c;是一个开放源代码的多媒体框架&#xff0c;用于创建音频和视频处理应用程序。它是一个运行在多个操作系统上的跨平台框架&#xff0c;支持多种不同的多媒体格式。 GStreamer框架的核心是基于插件的体系结构&#xff0c;插件…

GEE:绘制一个点的时间序列折线图,并且显示所有可用影像(逐日合成影像集合)

作者:CSDN @ _养乐多_ 本文将介绍在 Google Earth Engine (GEE)平台上,对“COPERNICUS/S5P/NRTI/L3_CO”数据集逐日合成,并可视化所有影像,根据逐日合成数据集绘制指定点的时间序列折线图。 图1 每天合成图像可视化 图2 一个点的每天合成时间序列折线图 文章目录 一、代…

VWS型振弦式应变计安装埋设时要注意什么

VWS型振弦式应变计是一种常见的应变测量设备&#xff0c;它是通过将振弦安装在被测结构上来实现应变测量。南京峟思专业的应变计生产厂家提醒大家&#xff0c;在安装和埋设VWS型振弦式应变计时&#xff0c;需要注意以下事项&#xff1a; 南京峟思工程仪器振弦式应变计 ​​​​…

84.Rem和max-width如何工作

max-width 首先我们先看普通的width是什么样的效果&#xff01; 首先给个测试的div <div class"test">TEST</div>● 然后CSS给定一个宽度 .test {width: 1000px;background-color: red;padding: 100px; }如上图&#xff0c;不管你的浏览器窗口如何改变…

记一次处理Spring-boot使用dubbo类型转换失败的问题

第一步、使用父类接收子类&#xff0c;类型转换失败&#xff0c;猜测是不是父子类不能接收&#xff0c;&#xff08;应该不可能&#xff0c;但还是试下&#xff09;&#xff1b; 第二步、使用同一个类接收&#xff0c;还是类型转换失败&#xff1b; com.book.common.result.Res…

将 Rust 程序编译为 WebAssembly

Rust 笔记、WebAssembly 将 Rust 程序编译为 WebAssembly 的知识与实践 作者&#xff1a;李俊才 &#xff08;jcLee95&#xff09;&#xff1a;https://blog.csdn.net/qq_28550263?spm1001.2101.3001.5343 邮箱 &#xff1a;291148484163.com 本文地址&#xff1a;https://blo…

浅谈安科瑞霍尔传感器在转速测量中的选型与应用

安科瑞 徐浩竣 江苏安科瑞电器制造有限公司 zx acrelxhj 摘 要&#xff1a;在现代工业生产中存在许多需要转速测量的方面&#xff0c;针对转速测量方法落后、只能进行接触式测量等问题&#xff0c;提出把霍尔传感器应用于工业生产中 , 利用霍尔效应测量转速&#xff0c;具有动…

【linux解压和打包文件】

TOC 打包成zip文件 指令 zip zip -r -q -o html.zip html/ -r 参数表示递归打包包含子目录的全部内容&#xff0c;-q 参数表示为安静模式&#xff0c;即不向屏幕输出信息&#xff0c;-o 表示输出文件&#xff0c;需在其后紧跟打包输出文件名。解压zip文件 1.unzip -q …

数字化时代,企业面临哪些共同的挑战?

在这种全新的社会、商业环境下&#xff0c;各行各业的企业都开始寻求探索新的商业模式&#xff0c;通过转型适应当前时代的转变&#xff0c;促进业务健康持续的发展。所以数字化成为了企业进行转型的工具&#xff0c;也成为了众多领域内企业对未来的共识。 一、管理挑战 ●经…

C++服务器框架开发3——协程与线程的简单理解/并发与并行

该专栏记录了在学习一个开发项目的过程中遇到的疑惑和问题。 其教学视频见&#xff1a;[C高级教程]从零开始开发服务器框架(sylar) 上一篇&#xff1a;C服务器框架开发2——头文件memory/typedef C服务器框架开发3——协程与线程的简单理解/并发与并行 目前进度协程与线程的简…

【基于容器的部署、扩展和管理】3.1 容器编排系统和Kubernetes集群的构建

往期回顾&#xff1a; 第一章&#xff1a;【云原生概念和技术】 第二章&#xff1a;【容器化应用程序设计和开发】 3.1 基于容器的部署、扩展和管理 3.1.1 容器介绍3.1.2 容器编排系统和Kubernetes集群的构建3.1.2.1 Kubernetes 集群3.1.2.2 容器编排系统 3.1.3 Kubernetes 集…

MySQL — 锁

文章目录 锁0、概述一、全局锁1.1 概述1.2 语法1.3 一致性数据备份1.4 问题 二、表级锁2.1 表锁2.2 元数据锁2.3 意向锁 三、行级锁3.1 概述3.2 行锁3.3 间隙锁 与 临建锁 锁 0、概述 锁是计算机协调多个进程和线程并发访问某一资源的机制。 ​ 在数据库中&#xff0c;除传统…

5.5G,通信产业下一个分水岭?

通信领域的变化日新月异&#xff0c;在5G商用渐次铺开之后&#xff0c;5.5G比6G先行到来。 5月28日获悉&#xff0c;近日北京移动首个5G-A实验基站在昌平区的国际信息港建设开通&#xff0c;引发业内关注。业内观点认为&#xff0c;5.5G是5G和6G之间的过渡“台阶”&#xff0c;…

阿秀离职了

小伙伴们大家好&#xff0c;我是阿秀。 是的&#xff0c;我从字节离职了&#xff0c;从抖音研发大部门离职了。 我从21年6月份毕业后就一直在字节跳动抖音这边工作&#xff0c;到现在差不多已经 2 年时间了&#xff0c;两年时光如白马过隙一晃而过&#xff0c;时间真的很快。 天…

Matlab与ROS(1/2)---ros1_bridge(八)

0. 简介 众所周知&#xff0c;ROS 2是具有不同架构的ROS的更新版本。这两个网络是分开的&#xff0c;在ROS和ROS 2的节点之间没有直接的通信。而ros1_bridge包则是提供了一个网桥&#xff0c;可以在ROS和ROS 2之间交换消息。桥接器管理所需的所有转换&#xff0c;并在两个网络…