Backup是StarRocks目前提供的唯一一种可将数据与元数据一并导出的导出方式。Backup操作也需要Broker组件,通过Broker将数据备份到远端存储系统中,例如HDFS、OSS、COS或S3等。
基于Backup的特性,这种方式通常用于对数据进行定期的快照备份,或者用于在不同集群间进行数据迁移。
与Backup对应的命令是Restore,用于将Backup备份后的数据进行还原。因为Backup备份后的文件中已经包含了元数据,所以在进行Restore还原操作时,我们无需手动建表。
目前,备份(Backup)操作或还原(Restore)可操作的最小单位是分区,最大单位是表,还不直接支持整库的备份或还原。当前也只支持全量备份,还未支持增量。所以如果需要对数据进行定期备份,我们需要在建表时合理规划表的分区,例如按时间分区,然后在后续业务中,按照分区粒度进行备份或还原。
注意:目前主键模型还不支持Restore操作,虽然可以Backup成功,但无法正常还原。
StarRocks官网对于备份还原的介绍材料主要有:
备份与恢复 @ Backup_and_restore @ StarRocks Docshttps://docs.starrocks.com/zh-cn/main/administration/Backup_and_restore
BACKUP @ BACKUP @ StarRocks Docshttps://docs.starrocks.com/zh-cn/main/sql-reference/sql-statements/data-definition/BACKUP
RESTORE @ RESTORE @ StarRocks Docshttps://docs.starrocks.com/zh-cn/main/sql-reference/sql-statements/data-definition/RESTORE
CREATE REPOSITORY @ CREATE REPOSITORY @ StarRocks Docshttps://docs.starrocks.com/zh-cn/main/sql-reference/sql-statements/data-definition/CREATE%20REPOSITORY
其他相关语法可参考官网文档->参考手册->SQL参考->DDL/DML。
Backup&Restore操作非常常用,咱们着重演示,需要使用到的集群组件有:
集群编号 |
节点IP |
部署服务 |
端口 |
版本 |
说明 |
集群A |
172.17.0.2 [starrocks] |
FE |
9030 |
2.1.0 |
用户名密码均为root |
BE |
8030 |
||||
Broker |
8000 |
名称为:hdfs_broker |
|||
mysql-client |
5.7.37 |
||||
集群B |
192.168.110.101 [node01] |
FE |
9030 |
2.0.1 |
用户名密码均为root |
BE |
8030 |
||||
Broker |
8000 |
名称均为:hdfs_broker |
|||
mysql-client |
5.7.35 |
||||
192.168.110.102 [node02] |
FE |
2.0.1 |
|||
BE |
|||||
Broker |
|||||
192.168.110.103 [node03] |
FE |
2.0.1 |
|||
BE |
|||||
Broker |
|||||
OSS |
桶名:wu########ng "fs.oss.accessKeyId" = "LTA################TbR", "fs.oss.accessKeySecret" = "HqD################PKN", "fs.oss.endpoint" = "oss-cn-beijing.aliyuncs.com" |
备注:
1、集群A与集群B都可以与外网通信;
2、集群A和集群B不在同一个内网,两套集群间无法直接通信。
为相对全面的涵盖常规业务场景,现模拟需求如下:
1、备份集群B数据库backup_test中的所有表(这里假设共三张表,即表backup_visit、backup_user和backup_supplier)的所有数据到OSS中,OSS相关信息见上表;
2、将集群B备份到OSS中的部分数据还原至集群A中,要求:
1)将数据还原至集群A的restore库;
2)backup_user表只还原分区p202201和p202202;
3)backup_supplier表需重命名为restore_supplier;
4)由于集群A只有一个BE节点,要求还原时设置只还原为单副本。
在node01节点访问StarRocks:
[root@node01 ~]# mysql -h192.168.110.101 -P9030 -uroot -proot
创建数据库:
mysql> create database backup_test;
mysql> use backup_test;
创建表backup_visit:
mysql> CREATE TABLE backup_visit(
record_id int,
seller_id int,
store_id int,
sale_date date,
sale_amt bigint
) Distributed BY hash(record_id)
properties(
"replication_num" = "1"
);
为该表分别创建Rollup Index和物化视图(后面解释原因),创建rollup_visit:
mysql> ALTER TABLE backup_visit ADD ROLLUP rollup_visit (sale_date,sale_amt);
创建Rollup和创建物化视图都是异步操作,需等待Rollup创建完成后再创建物化视图,创建进度查看命令:
mysql> SHOW ALTER MATERIALIZED VIEW FROM backup_test;
继续创建物化视图mv_visit:
mysql> CREATE MATERIALIZED VIEW mv_visit AS SELECT store_id,SUM(sale_amt) FROM backup_visit GROUP BY store_id;
为backup_visit表导入演示数据:
mysql> insert into backup_visit values(211014001,10001,13,'2021-10-14',1999),(211014002,10002,13,'2021-10-14',6999),(211015098,16573,19,'2021-10-15',3999);
查看backup_visit表结构:
mysql> desc backup_visit all;
创建表backup_user:
mysql> CREATE TABLE backup_user (
event_day DATE,
site_id INT DEFAULT '10',
city_code VARCHAR(100),
user_name VARCHAR(32) DEFAULT '',
pv BIGINT DEFAULT '0'
)
PARTITION BY RANGE(event_day)(
PARTITION p202201 VALUES LESS THAN ("2022-02-01"),
PARTITION p202202 VALUES LESS THAN ("2022-03-01"),
PARTITION p202203 VALUES LESS THAN ("2022-04-01")
)
DISTRIBUTED BY HASH(event_day, site_id) BUCKETS 2;
插入演示数据:
mysql> insert into backup_user values('2020-12-23',12,'49','lm',23),('2021-12-25',10,'26','Mark',55),( '2022-03-01',13,'52','SR',10086);
创建表backup_supplier:
mysql> CREATE TABLE `backup_supplier` (
`s_suppkey` int(11) NOT NULL COMMENT "",
`s_name` varchar(26) NOT NULL COMMENT "",
`s_address` varchar(26) NOT NULL COMMENT "",
`s_city` varchar(11) NOT NULL COMMENT "",
`s_nation` varchar(16) NOT NULL COMMENT "",
`s_region` varchar(13) NOT NULL COMMENT "",
`s_phone` varchar(16) NOT NULL COMMENT ""
)DUPLICATE KEY(`s_suppkey`)
DISTRIBUTED BY HASH(`s_suppkey`) BUCKETS 2
PROPERTIES (
"replication_num" = "3"
);
使用Stream Load导入演示数据(supplier.tbl是使用SSB测试集生成的数据文件):
[root@node01 ~]# curl --location-trusted -u root:root -H "label:supplier_20220301001" -H "column_separator:|" -T supplier.tbl http://192.168.110.101:8030/api/backup_test/backup_supplier/_stream_load
在StarRocks中查看表backup_supplier数据条数:
mysql> select count(1) from backup_supplier;
+-----------------+
| count(1) |
+-----------------+
| 2000 |
+-----------------+
在StarRocks中创建OSS远端仓库的语法为:
CREATE REPOSITORY `仓库名称`
WITH BROKER `broker_name`
ON LOCATION "oss://存储桶名称/路径"
PROPERTIES
(
"fs.oss.accessKeyId" = "xxx",
"fs.oss.accessKeySecret" = "yyy",
"fs.oss.endpoint" = "oss-cn-beijing.aliyuncs.com"
);
fs.oss.endpoint信息可以从阿里官网文档中获取:
访问域名和数据中心 - 对象存储 OSS - 阿里云https://help.aliyun.com/document_detail/31837.html?spm=a2c6h.13066369.0.0.11384123BRZZFf
根据我们OSS的信息修改语句,创建远端仓库oss_repo:
mysql> CREATE REPOSITORY `oss_repo`
WITH BROKER `hdfs_broker`
ON LOCATION "oss://wu########ng/StarRocks"
PROPERTIES
(
"fs.oss.accessKeyId" = "LTA################TbR",
"fs.oss.accessKeySecret" = "HqD################PKN",
"fs.oss.endpoint" = "oss-cn-beijing.aliyuncs.com"
);
查看已创建的仓库信息:
mysql> SHOW REPOSITORIES;
仅root或ADMIN_PRIV权限用户可以创建或删除仓库。假设仓库信息有误,可以删除后重新创建,删除语法为:
mysql> DROP REPOSITORY `repo_name`;
登录集群A的StarRocks:
[root@starrocks ~]# mysql -h172.17.0.2 -P9030 -uroot -proot
登录后,首先在集群A中创建目标库restore:
mysql> create database restore;
mysql> use restore;
接下来,特别注意,务必创建与集群B名称相同的远端仓库oss_repo,若仓库名称不同,即便我们的OSS信息和路径都一致,也无法进行还原:
mysql> CREATE REPOSITORY `oss_repo`
WITH BROKER `hdfs_broker`
ON LOCATION "oss://wu########ng/StarRocks"
PROPERTIES
(
"fs.oss.accessKeyId" = "LTA################TbR",
"fs.oss.accessKeySecret" = "HqD################PKN",
"fs.oss.endpoint" = "oss-cn-beijing.aliyuncs.com"
);
这里也可使用CREATE READ ONLY REPOSITORY语句创建只读仓库,只读仓库下只能使用仓库进行Restore恢复操作,不能进行Backup备份。
创建完成后,查看仓库信息,确认集群A中的仓库名称与集群B是相同的:
mysql> SHOW REPOSITORIES;
集群A就准备完毕,不需要建表。
备份(backup)操作实际是将指定表或分区的数据,直接以StarRocks存储的文件的形式,上传到远端仓库中进行存储。
同一数据库下只能有一个正在执行的BACKUP或RESTORE任务,多个数据库,从代码看这里也加了锁,也即当前可以认为集群中同时只能进行一个备份或者还原的任务。
Backup命令的语法为:
BACKUP SNAPSHOT [db_name].{snapshot_name}
TO `repository_name`
ON (
`table_name` [PARTITION (`p1`, ...)],
...
)
PROPERTIES ("key"="value", ...);
1、ON子句中标识需要备份的表和分区。如果不指定分区,则默认备份该表的所有分区。目前不直接支持整库的备份,如果需要整库备份,需要将库内所有的表都写在这里。
2、Backup的PROPERTIES目前只支持以下两个属性:
"type" = "full":表示这是一次全量更新(默认),目前也只支持全量,可以省略;
"timeout" = "3600":任务超时时间,默认为一天,单位秒,默认时可省略。
3、执行备份命令后,集群首先会对涉及到的tablet进行一次快照,快照耗时很少,快照名称为我们Backup语句中指定的snapshot_name。之后的备份操作实际都是对这个快照进行操作。也即在快照之后,对表进行的导入、修改等操作都不再影响备份的结果。
结合我们的需求改写备份语句,在集群B中执行备份任务:
mysql> BACKUP SNAPSHOT backup_test.snapshot_label22030101
TO `oss_repo`
ON
(
backup_visit,
backup_user,
backup_supplier
)
PROPERTIES (
"type" = "full",
"timeout" = "3600"
);
Backup为异步操作,可以通过show backup语句查看最近一次Backup作业的执行状态(StarRocks中每个数据库仅保存最近一次BACKUP任务):
mysql> SHOW BACKUP FROM backup_test\G
*************************** 1. row ***************************
JobId: 84472
SnapshotName: snapshot_label22030101
DbName: backup_test
State: UPLOADING
BackupObjs: [default_cluster:backup_test.backup_supplier], [default_cluster:backup_test.backup_user], [default_cluster:backup_test.backup_visit]
CreateTime: 2022-03-01 21:07:26
SnapshotFinishedTime: 2022-03-01 21:07:30
UploadFinishedTime: NULL
FinishedTime: NULL
UnfinishedTasks: 84481=10004, 84480=10004, 84473=10002, 84475=10002, 84474=10002, 84477=10003, 84476=10003, 84479=10004, 84478=10003
Progress: [84473: 3/5], [84476: 0/0], [84479: 0/0]
TaskErrMsg:
Status: [OK]
Timeout: 3600
重点关注以下五个参数:
1)SnapshotName:我们手动指定的本次备份作业的名称,后续还原时需要用到。
2)State:任务状态,需要着重关注,根据任务的进度可能会有以下8种可能的状态:
3)UnfinishedTasks:Backup作业同样会拆分为多个子任务并行执行,以84473=10002为例,表示task id为84473的子任务在BackendId为10002的BE节点上进行,目前进行的进度为3/5,这个进度为“已完成的tablet数/该子任务共包含的tablet数”,即Backup或Restore过程中,仍是以tablet作为最小数据单位。
4)TaskErrMsg和Status:会显示任务过程中Task的一些错误信息,但只要State列不为CANCELLED,就说明作业依然在继续,这些Task有可能会重试成功。当然,有些Task错误,也会直接导致作业失败。
当一次备份作业涉及到的表和数据都很多时,我们需要不时关注作业状态,若发现任务长时间没有进度或有其他问题,也可以将任务手动取消,取消语法为:
mysql> CANCEL BACKUP FROM db_name;
我们演示的数据量很小,备份作业可以很快完成。查看任务,当任务State为FINISHED时,表示备份作业成功完成,我们再进行后续的操作。
恢复(restore)操作需要我们指定一个远端仓库中已存在的备份,然后将这个备份的内容恢复到本地集群中。前面已经说过,目前一个集群中只能有一个Backup或Restore任务。Restore的语法和Backup非常相似:
RESTORE SNAPSHOT [db_name].{snapshot_name}
FROM `repository_name`
ON (
`table_name` [PARTITION (`p1`, ...)] [AS `tbl_alias`],
...
)
PROPERTIES ("key"="value", ...);
主要参数说明:
1)snapshot_name,即我们备份时定义的那个快照名。
2)ON子句中指定需要恢复的表和分区。还原时同样不支持直接进行库级别的还原,若要整库还原,需要列出库内所有的表。如果不指定分区,则默认恢复该表的所有分区。所指定的表和分区必须已存在于仓库备份中。
3)可以通过AS语句将仓库中备份的表名恢复为新的表,但新表名不能已存在于数据库中。表的其他信息均不能修改。
4)可以将仓库中备份的表恢复替换数据库中已有的同名表,但需保证两张表的表结构完全一致。表结构包括:表名、列、分区、Rollup及物化视图等等。同时,特别注意,从恢复作业的COMMIT阶段开始,如果恢复作业失败或被取消,有可能造成之前的数据已损坏且无法访问,因此,如无必要,强烈不建议使用覆盖替换同名表的方式恢复数据,除非确认当前表中的数据已不再使用。
5)可以指定恢复表的部分分区,系统会检查分区Range是否能够匹配。在任务中以分区为增量单位来备份并还原数据,这种方式是推荐的。这种情况与上面的第四条不同,不是覆盖,而是为目标表增量添加分区。
6)Restore的PROPERTIES目前支持以下属性:
"backup_timestamp" = "2022-02-22-16-45-08":指定了恢复对应备份的哪个时间版本,必填。该信息可以通过SHOW SNAPSHOT ON repo;语句获得。
"replication_num" = "3":指定恢复的表或分区的副本数。默认为3。若恢复已存在的表或分区,则副本数必须和已存在表或分区的副本数相同。同时,考虑到StarRocks的副本策略,必须有足够Host的BE节点来容纳多个副本。
"timeout" = "3600":任务超时时间,默认为一天,单位秒。
首先,在oss_repo中查看快照的时间版本信息:
mysql> SHOW SNAPSHOT ON oss_repo;
+------------------------------------+-----------------------------------+----------+
| Snapshot | Timestamp | Status |
+------------------------------------+-----------------------------------+----------+
| snapshot_label22030101 | 2022-03-01-21-07-26-597 | OK |
+------------------------------------+-----------------------------------+----------+
当Status为OK时表示备份正常,若不为OK,可能表示备份还未完成或出现了其他异常情况。如果想查看仓库中快照的详细信息,也可以在SHOW SNAPSHOT语句中拼接上Snapshot和Timestamp信息再次查看:
mysql> SHOW SNAPSHOT ON `oss_repo` WHERE SNAPSHOT = "snapshot_label22030101" AND TIMESTAMP = "2022-03-01-21-07-26-597"\G
*************************** 1. row ***************************
Snapshot: snapshot_label22030101
Timestamp: 2022-03-01-21-07-26-597
Database: backup_test
Details: {
"database": "backup_test",
"meta_version": 92,
"backup_time": 1646140046597,
"name": "snapshot_label22030101",
"starrocks_meta_version": 3,
"backup_result": "succeed",
"backup_objects": {
"backup_visit": {"partitions": {"backup_visit": {}}},
"backup_supplier": {"partitions": {"backup_supplier": {}}},
"backup_user": {"partitions": {
"p202201": {},
"p202202": {},
"p202203": {}
}}
}
}
Status: OK
返回值会以Json的形式,展示整个备份的库表及分区信息。
接下来,根据我们模拟的需求,在集群A中执行还原语句,这里再把还原部分的需求摘录一份:
将集群B备份到OSS中的部分数据还原至集群A中,要求:
1)将数据还原至集群A的restore库;
2)backup_user表只还原分区p202201和p202202;
3)backup_supplier表需重命名为restore_supplier;
4)由于集群A只有一个BE节点,要求还原时设置只还原为单副本。
根据备份信息及业务需求,在集群A中编写Restore语句并执行:
mysql> RESTORE SNAPSHOT restore.`snapshot_label22030101`
FROM `oss_repo`
ON (
backup_visit,
backup_user PARTITION (`p202201`, `p202202`),
backup_supplier AS restore_supplier
)
PROPERTIES
(
"backup_timestamp"="2022-03-01-21-07-26-597",
"replication_num" = "1",
"timeout" = "3600"
);
还原同样为异步操作,通过SHOW RESTORE [FROM db_name]语句查看还原状态(StarRocks中每个数据库也只会保存最近一次RESTORE任务):
mysql> SHOW RESTORE\G
*************************** 1. row ***************************
JobId: 13005
Label: snapshot_label22030101
Timestamp: 2022-03-01-21-07-26-597
DbName: default_cluster:restore
State: DOWNLOADING
AllowLoad: false --恢复时是否允许导入,当前不支持(这个参数只是预留)
ReplicationNum: 1
RestoreObjs: backup_visit, backup_supplier, backup_user PARTITIONS [p202201, p202202]
CreateTime: 2022-03-01 21:16:12
MetaPreparedTime: 2022-03-01 21:16:14
SnapshotFinishedTime: 2022-03-01 21:16:17
DownloadFinishedTime: NULL
FinishedTime: NULL
UnfinishedTasks: 13127=10002, 13128=10002
Progress: [13127: 0/0]
TaskErrMsg:
Status: [OK]
Timeout: 3600
在数据还原过程中,会自动创建还原操作涉及到的表,创建完成后,这些表可见,但不能访问,需要等待还原任务完成后方可正常访问。
还原任务的状态信息我们通常只需要关注State、TaskErrMsg及Status:
1)State:恢复作业当前所在阶段,根据任务进度可能会有八种状态:
2)TaskErrMsg及Status:显示还原作业过程中可能出现的错误信息,如果任务失败,这里也会展示失败信息。同样的,只要State列不为CANCELLED,则说明作业依然在继续,作业中失败的task可能会重试成功,当然也有可能重试失败,最终导致任务CANCELLED。
与Backup操作时相同,当我们发现任务进度持续卡住,或者因为其他原因需要取消还原任务时,取消的语法为:
mysql> CANCEL RESTORE FROM db_name;
再次强调,如果是覆盖表式的还原,当取消处于COMMIT或之后阶段的恢复作业时,可能导致数据损坏,进而导致被恢复的表无法访问。
本次演示还原的数据量不大,当Restore任务状态为FINISHED时,即表示还原作业已成功完成,我们来查看一下还原后集群A的库表信息。
mysql> show tables from restore;
+-----------------------------+
| Tables_in_restore |
+-----------------------------+
| backup_user |
| backup_visit |
| restore_supplier |
+-----------------------------+
表名没有问题,backup_supplier表在还原时已被重命名为restore_supplier。
查看backup_visit的表结构:
mysql> desc backup_visit all;
确认物化视图及Rollup索引都能被一并备份还原过来。
检查backup_visit的分区还原情况:
mysql> show partitions from backup_visit;
分区正常,可以单独还原的我们给定的分区。后续我们也可以再次对该表单独还原p202203分区,这也是业务中比较常用的备份还原形式。
备份还原时,若集群中资源相对空闲,也可以通过调整作业中子任务的并行度来进行加速,主要涉及到参数有三个:
BE配置项,上传文件最大线程数,默认值为1,作用于BACKUP阶段。调优时可以适当调大,例如设为5。
BE配置项,下载线程数,默认1个,作用于RESTORE阶段。调优时可以适当增大,例如设为5。
BE配置项,最大下载速度限制,默认值:50000(kb/s)。备份还原任务通常不会跑到这个参数的上限,假设是由于这个速度限制导致的,可根据带宽情况适当调整。
出于篇幅考虑,咱们没有创建更多的示例表来控制单一变量演示说明问题,这里我将我们实际业务中使用备份还原出现过的问题及解决方案以FAQ的形式整理出来,供大家参考。
在StarRocks早期版本中出现过,动态分区表备份还原后,查看建表语句,虽然dynamic_partition.enable属性为true,但是不能正常的自动创建新分区(已提issue)。
解决方案:将该表的动态分区属性关闭一次后重新启用。即先后执行:
ALTER TABLE tbl SET("dynamic_partition.enable"="false");
ALTER TABLE tbl SET("dynamic_partition.enable"="true");
设置了Colocate属性的表在进行备份还原后,无法正常查询(已提issue)。
解决方案:将该表的Colocate属性关闭掉,即执行:
ALTER TABLE tbl SET ("colocate_with" = "");
执行CREATE REPOSITORY语句后直接报错,提示errors while write data to output stream, cause by: buffer is null。
解决方案:broker的conf目录中有一个hdfs-site.xml,里面配置了备份还原时本地的缓存目录,默认是/tmp/,可以排查一下权限问题。若创建仓库阶段有报错,基本可以从本地权限、通信及OSS权限三个角度来逐个排查。
备份还原操作在用户层面并不涉及复杂的业务编码,当出现完全理不出头绪的报错,除了及时向社区求助,也可以先从这几个角度排查解决:
1、拆分任务,如果一次任务涉及的表或者数据量很大,可以将任务拆分为多个,逐个进行,这样通常可以解决大部分问题。
2、排查版本,通常我们升级StarRocks时,只关注FE和BE,可能忘记了同时升级Broker,此时可以将Broker升级至和主程序一致的版本,再次执行任务。
1、Rollup和物化视图可以同步还原(其中Rollup是物化视图的前身,是一个逐渐被弱化的概念,它的功能也已经被物化视图完全涵盖,现在并不建议使用)。
2、在进行还原操作时,库名可以不和源库的一致,表名可以改,副本数可以改,其他的都改不了。
3、仅支持备份OLAP类型的表,视图不能备份,会报错,需要手动创建。
1、当备份还原语句中有存在仅以大小写区分的表时(例如表ABC_cost和表abc_cost),任务会报错,需要将它们拆分为不同的任务进行(已提issue)。
2、Backup一个空表时,出现过一直卡在SNAPSHOTING阶段的情况,所以目前需要注意不要备份空表(已提issue)。
3、创建或删除远端仓库需要root或ADMIN_PRIV权限,执行备份还原任务时只需要普通权限。
4、目前备份的快照数据无法在StarRocks上使用命令删除,DROP REPOSITORY命令只是删除StarRocks与远端存储的连接,已经存在于远端存储中的文件需要我们登录OSS等进行手动删除。
5、最后也是最重要,不要一次备份或还原大量的表和数据,当表的数据量很大时,可以按照分区拆分任务进行,个人建议单次任务不要超过10G,以避免失败重试的时间成本。同时,在前期也要注意合理建表,规范分区分桶,尤其是分桶数,不要过多,当一个表涉及的tablet过多时,即使总数据量很小,依然可能需要备份或恢复较长时间。