本文介绍同账号和跨账号下(包括同区域和跨区域)的数据复制常见问题以及排查方法。
为什么无法创建数据复制规则?
权限问题
Bucket版本控制状态不一致
开启数据同步的源Bucket和目标Bucket的版本控制状态必须一致,即这两个Bucket同时处于未开启或者已开启版本控制。
Endpoint或者AccessKey等配置不正确
通过SDK或者ossutil等方式创建数据复制规则时,需要检查以下配置:
检查用于建立复制关系的源Bucket以及目标Bucket所在地域的Endpoint是否正确。更多信息,请参见访问域名和数据中心。
检查建立复制关系的AccessKey是否正确。更多信息,请参见查看RAM用户的AccessKey信息。
为什么源Bucket对象副本未出现在目标Bucket?
在源存储空间(Bucket)配置了数据复制规则后,如果对象副本未出现在目标Bucket中,请参考以下几种可能原因排查并修复问题。
时间限制
数据复制采用异步(近实时)复制的机制,将数据复制到目标Bucket需要一定的时间,通常几分钟到几小时不等,取决于数据的大小。如果要复制的对象较大,请稍等片刻,再检查对象副本是否出现在目标Bucket中。
源Bucket配置问题
数据复制状态是否为已开启(Enabled)。
前缀(Prefix)是否正确。
同步指定对象:如果需要同步源Bucket中的指定对象到目标Bucket,请将Prefix设置为指定对象名称。例如,Prefix设置为log,则仅复制log/date1.txt、log/date2.txt等以log开头的对象。与指定Prefix不匹配的对象不会复制到目标Bucket,例如date3.txt。
同步所有对象:如果需要将源Bucket中的全部对象复制到目标Bucket时,请将Prefix置空。
复制规则限制
如果Bucket中的某个对象是另一个复制配置创建的副本,则OSS不会复制该对象。例如,您配置了Bucket A同步到Bucket B,Bucket B再同步到Bucket C,则OSS不会将从Bucket A同步到Bucket B的对象副本复制到Bucket C。
未选中复制KMS加密目标对象
在源Object或者目标Bucket使用了KMS托管密钥加密方式(即SSE-KMS,指定CMK ID)的情况下,要将Object复制到目标Bucket,则必须选中复制,并配置以下参数:
使用的KMS密钥:为目标Object指定加密的KMS密钥。
您需要提前在KMS平台创建一个与目标Bucket相同地域的KMS密钥。具体操作,请参见创建密钥。
授权角色:授权一个RAM角色对目标Object执行KMS加密操作。
新建角色:新建RAM角色对目标Object执行KMS加密,角色名称格式为
kms-replication-源Bucket名称-目标Bucket名称
。AliyunOSSRole:使用AliyunOSSRole角色对目标Object执行KMS加密。若您之前未创建AliyunOSSRole角色,当您选择此项时,OSS将自动创建AliyunOSSRole角色。
说明如果是您创建的角色或者修改角色权限时,请确保授予角色
AliyunOSSFullAccess
的权限,否则可能导致数据无法复制。
您可以通过HeadObject和GetBucketEncryption分别查询源Object和目标Bucket的加密状态。
如何验证复制完成后目标Bucket与源Bucket的数据是否一致?
您可以通过以下代码验证复制完成后目标Bucket与源Bucket的数据一致性。
import com.aliyun.oss.OSSClient;
import com.aliyun.oss.common.auth.*;
import com.aliyun.oss.model.*;
import com.aliyun.oss.OSSException;
import com.aliyuncs.exceptions.ClientException;
public class Demo {
public static void main(String[] args) throws ClientException {
// 从环境变量中获取访问凭证。运行本代码示例之前,请确保已设置环境变量OSS_ACCESS_KEY_ID和OSS_ACCESS_KEY_SECRET。
EnvironmentVariableCredentialsProvider credentialsProvider = CredentialsProviderFactory.newEnvironmentVariableCredentialsProvider();
// srcEndpoint填写Bucket所在地域对应的Endpoint。
String srcEndpoint = "https://oss-cn-hangzhou.aliyuncs.com";
OSSClient srcClient = new OSSClient(srcEndpoint , credentialsProvider);
// 填写源Bucket名称。
String srcBucketName = "src-replication-bucket";
// destEndpoint填写Bucket所在地域对应的Endpoint。
String destEndpoint = "https://oss-cn-beijing.aliyuncs.com";
OSSClient destClient = new OSSClient(destEndpoint, credentialsProvider);
// 填写目标Bucket名称。
String destBucketName = "dest-replication-bucket";
// 源Bucket与目标Bucket处于非版本控制状态时,通过listObjectsV2列举源Bucket被复制的文件。
// 源Bucket与目标Bucket处于开启或暂停版本控制状态时,通过listVersions列举源Bucket被复制的文件。
ListObjectsV2Result result;
ListObjectsV2Request request = new ListObjectsV2Request(srcBucketName);
do {
result = srcClient.listObjectsV2(request);
for (OSSObjectSummary summary : result.getObjectSummaries())
{
String objectName = summary.getKey();
ObjectMetadata srcMeta;
try {
// 获取源Bucket被复制文件的元数据。
srcMeta = srcClient.headObject(srcBucketName, objectName);
} catch (OSSException ossException) {
if (ossException.getErrorCode().equals("NoSuchKey")) {
continue;
} else {
System.out.println("head src-object failed: " + objectName);
}
continue;
}
ObjectMetadata destMeta;
try {
// 获取复制到目标Bucket的文件元数据。
destMeta = destClient.headObject(destBucketName, objectName);
} catch (OSSException ossException) {
if (ossException.getErrorCode().equals("NoSuchKey")) {
System.out.println("dest-object not exist: " + objectName);
} else {
System.out.println("head dest-object failed: " + objectName);
}
continue;
}
// 检查源Bucket与目标Bucket复制文件的CRC是否一致。
Long srcCrc = srcMeta.getServerCRC();
String srcMd5 = srcMeta.getContentMD5();
if (srcCrc != null) {
if (destMeta.getServerCRC() != null) {
if (!destMeta.getServerCRC().equals(srcCrc)) {
System.out.println("crc not equal: " + objectName
+ " | srcCrc: " + srcCrc + " | destCrc: " + destMeta.getServerCRC());
}
continue;
}
}
// 检查源Bucket与目标Bucket复制文件的MD5是否一致。
if (srcMd5!= null) {
if (destMeta.getContentMD5() != null) {
if (!destMeta.getContentMD5().equals(srcMd5)) {
System.out.println("md5 not equal: " + objectName
+ " | srcMd5: " + srcMd5 + " | destMd5: " + destMeta.getContentMD5());
}
continue;
}
}
// 检查源Bucket与目标Bucket复制文件的ETag是否一致。
if (srcMeta.getETag() == null || !srcMeta.getETag().equals(destMeta.getETag())) {
System.out.println("etag not equal: " + objectName
+ " | srcEtag: " + srcMeta.getETag() + " | destEtag: " + destMeta.getETag());
}
}
request.setContinuationToken(result.getNextContinuationToken());
request.setStartAfter(result.getStartAfter());
} while (result.isTruncated());
}
}
是否支持复制传递?
不支持。例如,Bucket A配置了到Bucket B的数据复制规则(本示例中提及的数据复制规则包括跨区域或者同区域复制),Bucket B配置了到Bucket C的数据复制规则,则Bucket A的数据仅复制到Bucket B,不会复制到Bucket C。
如果要将Bucket A的数据复制到Bucket C,则您还需要为Bucket A配置到Bucket C的数据复制规则。
有种特殊情况,如果Bucket A和Bucket B同时开启了历史复制,在历史复制未完成时,有可能存在新写入Bucket A的数据被历史复制的任务扫描到,从而复制到Bucket C中。
OSS bucket 双向同步是否会有循环复制的风险?
不会有风险,因为OSS的增量复制过来的数据不会传递,并且历史复制只会对数据扫描复制一次。
源Bucket通过生命周期规则指定删除的文件,目标Bucket是否会同步删除这些文件?
源Bucket数据复制策略指定为增/改 同步,则源Bucket通过生命周期规则指定删除的文件,目标Bucket不会同步删除这些文件。
源Bucket数据复制策略指定为增/删/改 同步,则源Bucket通过生命周期规则指定删除的文件,目标Bucket也会同步删除这些文件。
说明如果您在目标Bucket仍然可以找到源Bucket通过生命周期指定删除的文件,并非是增/删/改 同步的数据复制策略不生效,可能是您通过目标Bucket手动写入了与源Bucket删除的同名文件。
为什么历史数据复制进度长时间显示为0%?
历史数据复制进度并非实时更新,需要等待所有文件扫描完成后才会复制更新。如果您Bucket内的文件数量较多(例如达到上亿级别),则可能需要等待数小时才会更新历史数据复制进度。历史数据复制进度未更新,并不代表历史数据没有复制到目标Bucket。
您可以通过查看目标Bucket的存储容量以及复制流入和流出流量产生的变化,确认源Bucket内的历史数据是否已开始复制到目标Bucket。关于查看目标Bucket的存储容量以及复制流入和流出流量的具体步骤,请参见查询Bucket级别的用量情况。
暂停版本控制状态下的Bucket是否支持数据复制?
不支持。仅允许对同时处于非版本化或开启版本控制状态的两个Bucket开启数据复制。
如果目标Bucket采用KMS加密,是否收取KMS加密的加密算法API费用?
如果目标Bucket采用KMS加密,会收取KMS加密的加密算法API费用。费用详情,请参见KMS计费说明。
开启数据复制后,是否支持关闭?
支持。您可以通过单击规则右侧的关闭复制来停止数据复制。
关闭复制后,已复制的数据将被保留在目标Bucket中,源Bucket中的增量数据将不再复制到目标Bucket。