全部产品
Search
文档中心

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

更新时间:Mar 19, 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 双向同步是否会有循环复制的风险?

不会有风险,因为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。