问:线上的业务最怕什么?
答:丢数据。
继续问:比丢数据还可怕的是什么呢?
答:丢的干干净净,还无法找回!
COS对象存储有11个9的数据保障级别,但是不保障主动删除的数据,如果真是手欠清空了存储桶。那么真的是无法挽回了。
所以数据备份的事情,一定要防患于未然,提前做好高可用架构。COS产品官网上有详细的高可用架构说明文档https://cloud.tencent.com/document/product/436/37709
以上的理论内容,咱们就不详细说了。这里介绍一下,当真的手残点击了当前桶和备份桶的删除动作后,我们继续多版本的高可用架构如何可以快速的恢复我们想要的数据。
这里介绍一下快速恢复的方案。总结成一句话:遍历所有目标对象,找到第一个非删除版本的数据对象进行拷贝。
场景
- 第一条为删除标记,第二条为最新版本数据,第三条为次新版本数据。
- 第一条为最新版本数据。
- 第一条为最新版本数据,第二条为次新版本是数据。
- 第一条为删除标记,第二条为删除标记,第三条为最新版本数据,第四条为次新版本数据。
import com.qcloud.cos.COSClient;
import com.qcloud.cos.ClientConfig;
import com.qcloud.cos.auth.BasicCOSCredentials;
import com.qcloud.cos.auth.COSCredentials;
import com.qcloud.cos.exception.CosClientException;
import com.qcloud.cos.exception.CosServiceException;
import com.qcloud.cos.http.HttpProtocol;
import com.qcloud.cos.model.CopyObjectRequest;
import com.qcloud.cos.model.CopyObjectResult;
import com.qcloud.cos.model.COSVersionSummary;
import com.qcloud.cos.model.ListVersionsRequest;
import com.qcloud.cos.model.VersionListing;
import com.qcloud.cos.region.Region;
import java.util.ArrayList;
import java.util.List;
public class recoverDemo {
private static String srcbucketName = "test-revocerback-123456789";// 备份桶
private static String dstbucketName = "test-revocerdst-123456789"; // 目标桶
private static String srcbucketRegion = "ap-guangzhou"; // 备份桶的region
private static String dstbucketRegion = "ap-shanghai"; // 目标桶的region
private static List<String> copyobjs = new ArrayList<>();
private static COSClient srcCosClient = createCli(srcbucketRegion);
private static COSClient dstCosClient = createCli(dstbucketRegion);
public static void main(String[] args) {
listAndRecoverObjs();
}
private static COSClient createCli(String region) {
// 1 初始化用户身份信息(secretId, secretKey)
COSCredentials cred = new BasicCOSCredentials("AKIDxxxxxxxxxxxxxxxxxxx", "xxxxxxxxxxxxxxxxxxxxxxxxx");
// 2 设置bucket的区域, COS地域的简称请参照 https://www.zijiebao.com/document/product/436/6224
ClientConfig clientConfig = new ClientConfig(new Region(region));
clientConfig.setHttpProtocol(HttpProtocol.http);
// 生成cos客户端
return new COSClient(cred, clientConfig);
}
private static void recoverObj(String srcKey, String srcVersionId) {
String dstKey = srcKey;
CopyObjectRequest copyObjectRequest = new CopyObjectRequest(new Region(srcbucketRegion), srcbucketName, srcKey, dstbucketName, dstKey);
copyObjectRequest.setSourceVersionId(srcVersionId);
try {
CopyObjectResult result = dstCosClient.copyObject(copyObjectRequest);
System.out.println("finish recover by copying obj, srcKey: " srcKey ", srcVersionId: " srcVersionId);
copyobjs.add(srcKey);
} catch (CosServiceException cse) {
cse.printStackTrace();
} catch (CosClientException cce) {
cce.printStackTrace();
}
}
private static void listAndRecoverObjs() {
ListVersionsRequest listVersionsRequest = new ListVersionsRequest();
listVersionsRequest.setBucketName(srcbucketName);
listVersionsRequest.setPrefix("");
VersionListing versionListing = null;
do {
try {
versionListing = srcCosClient.listVersions(listVersionsRequest);
} catch (CosServiceException e) {
e.printStackTrace();
return;
} catch (CosClientException e) {
e.printStackTrace();
return;
}
List<COSVersionSummary> cosVersionSummaries = versionListing.getVersionSummaries();
for (COSVersionSummary cosVersionSummary : cosVersionSummaries) {
String key = cosVersionSummary.getKey();
String versionId = cosVersionSummary.getVersionId();
boolean isDeleteMarker = cosVersionSummary.isDeleteMarker();
boolean isLatest = cosVersionSummary.isLatest();
if (!isDeleteMarker) {
if (isLatest) {
System.out.println("最新版文件,拷贝" "key:" key ",version:" versionId);
recoverObj(key, versionId);
} else {
if (!copyobjs.contains(key)) {
System.out.println("次新文件,拷贝" "key:" key ",version:" versionId);
recoverObj(key, versionId);
}
}
}
}
String keyMarker = versionListing.getNextKeyMarker();
String versionIdMarker = versionListing.getNextVersionIdMarker();
listVersionsRequest.setKeyMarker(keyMarker);
listVersionsRequest.setVersionIdMarker(versionIdMarker);
} while (versionListing.isTruncated());
System.out.println("--------------------------------------");
}
}
1.当有DeleteMarker标记时则跳过操作。
2.当islastest为true时,说明这是一个最新版本的数据对象。则进行复制操作。
3.当既不是deletemarker标签,也不是islastest最新版时。我们会找到第一个数据对象,作为筛选后的最新版实体数据,进行复制。
4.复制操作完成后,对象名称加入objectlist,避免再次被覆盖,也就是说,每一个对象,最多只会操作一次复制。
通过这个逻辑,我们只要找到第一个有实体数据的对象,做复制操作,就可以实现所有最新版的复制功能,实现批量的数据恢复。
测试一下,我们做了一份桶的数据清单,如下
这里模拟各种删除场景,之后执行批量恢复脚本,执行结果如下
完成后在目标桶查看
验证成功。
以上就是通过多版本的方式,批量快速的恢复被删除数据的方法。
注:本方法目前只适合同账号恢复。不占用本地带宽资源,快速便捷。