在信息技術與數據管理領域,備份是指將文件系統或數據庫系統中的數據加以複製,一旦發生災難或者錯誤操做時,得以方便而及時地恢復系統的有效數據和正常運做。在實際備份過程當中,最好將重要數據製做三個或三個以上的備份,而且放置在不一樣的場所異地備援,以供往後回存之用。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數據備份恢復方案。
本文爲雲棲社區原創內容,未經容許不得轉載。