全部产品
Search
文档中心

对象存储 OSS:数据复制常见问题

更新时间:Mar 25, 2024

本文介绍同账号和跨账号下(包括同区域和跨区域)的数据复制常见问题以及排查方法。

为什么无法创建数据复制规则?

权限问题

  • 同账号复制

    对于同账号复制,您需要授予角色在源Bucket和目标Bucket之间执行复制操作的权限。关于RAM角色创建以及授权的具体操作,请参见角色类型

  • 跨账号复制

    对于跨账号复制,您需要通过账号A为角色授予源Bucket执行数据复制的权限,并通过账号B授予角色在目标Bucket接收复制对象的权限。关于RAM角色创建以及授权的具体操作,请参见角色授权

Bucket版本控制状态不一致

开启数据同步的源Bucket和目标Bucket的版本控制状态必须一致,即这两个Bucket同时处于未开启或者已开启版本控制。

Endpoint或者AccessKey等配置不正确

通过SDK或者ossutil等方式创建数据复制规则时,需要检查以下配置:

为什么源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

  • 使用的KMS密钥:为目标Object指定加密的KMS密钥。

    您需要提前在KMS平台创建一个与目标Bucket相同地域的KMS密钥。具体操作,请参见创建密钥

  • 授权角色:授权一个RAM角色对目标Object执行KMS加密操作。

    • 新建角色:新建RAM角色对目标Object执行KMS加密,角色名称格式为kms-replication-源Bucket名称-目标Bucket名称

    • AliyunOSSRole:使用AliyunOSSRole角色对目标Object执行KMS加密。若您之前未创建AliyunOSSRole角色,当您选择此项时,OSS将自动创建AliyunOSSRole角色。

    说明

    如果是您创建的角色或者修改角色权限时,请确保授予角色AliyunOSSFullAccess的权限,否则可能导致数据无法复制。

您可以通过HeadObjectGetBucketEncryption分别查询源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双向同步是否会有循环复制的风险?

不会有风险。Bucket A与Bucket B之间配置双向复制关系后,通过复制关系从Bucket A到Bucket B的数据(包括新增和历史数据),不会再次从Bucket B复制到Bucket A。同理,通过复制关系从Bucket B到Bucket A的数据(包括新增和历史数据),不会再次从Bucket A复制到Bucket B。

源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。