在信息技术与数据管理领域,备份是指将文件系统或数据库系统中的数据加以复制,一旦发生灾难或者错误操做时,得以方便而及时地恢复系统的有效数据和正常运做。在实际备份过程当中,最好将重要数据制做三个或三个以上的备份,而且放置在不一样的场所异地备援,以供往后回存之用。html
备份有两个不一样的目的,其主要的目的是在数据丢失后恢复数据,不管数据是被删除仍是被损坏。备份的第二个目的是根据用户定义的数据保留策略从较早的时间恢复数据,一般在备份应用程序中配置须要备份多长时间的数据副本。数据库
因为备份系统至少会包含一个被认为值得保存的全部数据的副本,所以对数据存储的要求可能会很高,组织此存储空间和管理备份过程多是一项复杂的任务。现在,有许多不一样类型的数据存储设备可用于进行备份,还能够经过许多不一样的方式来安排这些设备以提供地理冗余,数据安全性和可移植性。安全
在将数据发送到其存储位置以前,会选择,提取和操做它们,目前已经有许多不一样的技术来优化备份过程,其中包括处理打开的文件(open files)和实时数据源的优化,以及压缩,加密和重复数据删除等。每一个备份方案都应包括演习过程,以验证正在备份的数据的可靠性,更重要的是要认识到任何备份方案中涉及的限制和人为因素。并发
对于存储系统而言,数据的安全可靠永远是第一位的,要保障数据尽量高的可靠性,须要从两个方面保障:app
下图为Table Store备份恢复的逻辑结构图,基于全增量一体的通道服务咱们能够很容易的构建一整套的数据备份和数据恢复方案,同时具有了实时增量备份能力和秒级别的恢复能力。只要提早配置好备份和恢复的计划,整个备份恢复系统能够作到彻底的自动化进行。框架
目前表格存储虽然未推出官方的备份和恢复功能,可是笔者会step-by-step的带你们基于表格存储的通道服务设计属于本身的专属备份恢复方案,实战步骤会分为备份和恢复两部分。less
1. 备份函数
2. 恢复性能
备份策略须要肯定备分内容、备份时间和备份方式,目前常见的备份策略主要有如下几类。优化
在这次的实战中,笔者对于备份计划和策略的选择以下(这里能够根据本身的需求进行自定义设计,而后利用通道服务的SDK来完成相应的需求)。
备份时间:
这部分会结合代码片断的形式讲解,详细的代码后续会开源出去(连接会更新在本文中),尽请期待。在进行实战以前,推荐先阅读通道服务Java SDK的 使用文档。
private static void createTunnel(TunnelClient client, String tunnelName) { CreateTunnelRequest request = new CreateTunnelRequest(TableName, tunnelName, TunnelType.BaseAndStream); CreateTunnelResponse resp = client.createTunnel(request); System.out.println("RequestId: " + resp.getRequestId()); System.out.println("TunnelId: " + resp.getTunnelId()); }
转换部分的核心代码参见以下代码片断,笔者处理这部分用的是univocity-parsers(CSV)和Gson库,有几个地方须要特别注意下:1). Gson反序列化Long类型会转为Number类型,可能会形成进度丢失,有若干解决办法,笔者采用的是将其转为String类型序列化。2). 对于binary类型的数据的特殊处理,笔者进行了base64的编解码。3). 能够直接流式写入OSS中,减小本地持久化的消耗。
this.gson = new GsonBuilder().registerTypeHierarchyAdapter(byte[].class, new ByteArrayToBase64TypeAdapter()) .setLongSerializationPolicy(LongSerializationPolicy.STRING).create(); // ByteArrayOutputStream到ByteArrayInputStream会有一次array.copy, 可考虑用管道或者NIO channel. public void streamRecordsToOSS(List<StreamRecord> records, String bucketName, String filename, boolean isNewFile) { if (records.size() == 0) { LOG.info("No stream records, skip it!"); return; } try { CsvWriterSettings settings = new CsvWriterSettings(); ByteArrayOutputStream out = new ByteArrayOutputStream(); CsvWriter writer = new CsvWriter(out, settings); if (isNewFile) { LOG.info("Write csv header, filename {}", filename); List<String> headers = Arrays.asList(RECORD_TIMESTAMP, RECORD_TYPE, PRIMARY_KEY, RECORD_COLUMNS); writer.writeHeaders(headers); System.out.println(writer.getRecordCount()); } List<String[]> totalRows = new ArrayList<String[]>(); LOG.info("Write stream records, num: {}", records.size()); for (StreamRecord record : records) { String timestamp = String.valueOf(record.getSequenceInfo().getTimestamp()); String recordType = record.getRecordType().name(); String primaryKey = gson.toJson( TunnelPrimaryKeyColumn.genColumns(record.getPrimaryKey().getPrimaryKeyColumns())); String columns = gson.toJson(TunnelRecordColumn.genColumns(record.getColumns())); totalRows.add(new String[] {timestamp, recordType, primaryKey, columns}); } writer.writeStringRowsAndClose(totalRows); // write to oss file ossClient.putObject(bucketName, filename, new ByteArrayInputStream(out.toByteArray())); } catch (Exception e) { e.printStackTrace(); } }
public class TunnelBackup { private final ConfigHelper config; private final SyncClient syncClient; private final CsvHelper csvHelper; private final OSSClient ossClient; public TunnelBackup(ConfigHelper config) { this.config = config; syncClient = new SyncClient(config.getEndpoint(), config.getAccessId(), config.getAccessKey(), config.getInstanceName()); ossClient = new OSSClient(config.getOssEndpoint(), config.getAccessId(), config.getAccessKey()); csvHelper = new CsvHelper(syncClient, ossClient); } public void working() { TunnelClient client = new TunnelClient(config.getEndpoint(), config.getAccessId(), config.getAccessKey(), config.getInstanceName()); OtsReaderConfig readerConfig = new OtsReaderConfig(); TunnelWorkerConfig workerConfig = new TunnelWorkerConfig( new OtsReaderProcessor(csvHelper, config.getOssBucket(), readerConfig)); TunnelWorker worker = new TunnelWorker(config.getTunnelId(), client, workerConfig); try { worker.connectAndWorking(); } catch (Exception e) { e.printStackTrace(); worker.shutdown(); client.shutdown(); } } public static void main(String[] args) { TunnelBackup tunnelBackup = new TunnelBackup(new ConfigHelper()); tunnelBackup.working(); } }
在数据备份完成后,咱们能够根据需求进行全量的恢复和部分数据的恢复,来下降RTO。在恢复数据时,能够有一些措施来优化恢复的性能:
public class TunnelRestore { private ConfigHelper config; private final SyncClient syncClient; private final CsvHelper csvHelper; private final OSSClient ossClient; public TunnelRestore(ConfigHelper config) { this.config = config; syncClient = new SyncClient(config.getEndpoint(), config.getAccessId(), config.getAccessKey(), config.getInstanceName()); ossClient = new OSSClient(config.getOssEndpoint(), config.getAccessId(), config.getAccessKey()); csvHelper = new CsvHelper(syncClient, ossClient); } public void restore(String filename, String tableName) { csvHelper.parseStreamRecordsFromCSV(filename, tableName); } public static void main(String[] args) { TunnelRestore restore = new TunnelRestore(new ConfigHelper()); restore.restore("FullData-1551767131130.csv", "testRestore"); } }
本文首先介绍了Table Store备份的需求,接着介绍了使用表格存储的通道服务进行数据备份和恢复的原理,最后结合实际的代码片断讲述了如何一步步的构建Tabel Store数据备份恢复方案。
本文为云栖社区原创内容,未经容许不得转载。