类似文件的追加写操作,在对象的末尾增加新的数据内容。
本文有如下假定:
下面讨论追加写操作时的方案和注意事项。
业界主流对象存储服务比如AWS S3并未定义追加写操作,而国内的各家公有云对象存储服务基于对象语义和文件语义的理解,提供了对象的追加写操作。
国内的公有云对象存储服务,提供的追加写操作,相关文档的链接(排名不分先后),如下:
以阿里云OSS的AppendObject为例,接口定义如下:
POST /ObjectName?append&position=Position HTTP/1.1
Content-Length:ContentLength
Content-Type: ContentType
Host: BucketName.oss.aliyuncs.com
Date: GMT Date
Authorization: SignatureValue
本接口的关键参数,如下:
append
,不需要指定参数值。position
,参数值为追加写的开始位置。Content-Length
承载。另外允许更新对象的元数据,对象的元数据的说明见文档。
考虑到对象上传特点的特点,可以划分为PUT方式上传的对象,和使用多段方式上传的对象,因此在实现追加写时,需要考虑这两种对象和上传方式的差异,借鉴实现思路。
对于PUT方式上传的对象,实现追加写时,可以考虑如下方案:
对于多段方式上传的对象,实现追加写时,可以考虑如下方案:
下面从对象服务特性的角度,分别评估上述方案。
参考AWS S3数据一致性,ETag
基于对象的数据,使用MD5
算法计算得到。
客户应用在请求的头部增加Content-MD5
,指定本次上传数据的MD5
值,或者对象数据整体的MD5
值。
本次上传数据的MD5
值
客户应用在请求的头部增加Content-MD5
,指定本次上传数据的MD5
值。
对象存储服务收到数据后,计算数据的MD5
值,并和Content-MD5
值对比:
对象存储服务处理追加写操作成功后,在返回的消息中使用ETag
字段,返回服务端依据接收到的数据计算得到的MD5
值,方便对象存储的客户应用执行客户端的校验。
本方案中,由于只需要计算追加的数据的MD5
值,因此客户应用并不需要获得对象的完整数据。相应的,对象存储的服务端同样只需要计算追加的数据的MD5
值,不需要读取对象的完整数据,操作简单。
对象数据整体的MD5
值
客户应用在请求的头部增加Content-MD5
,指定对象数据整体的MD5
值。
对象存储服务收到数据后,需要读取对象的数据,结合本次上传的数据,一并计算,得到MD5
值。然后和Content-MD5
值对比:
对象存储服务处理追加写操作成功后,在返回的消息中使用ETag
字段,返回服务端的对象的MD5
值,方便对象存储的客户应用执行客户端的校验。
本方案中,由于需要计算完整对象的MD5
值,因此客户应用在执行追加写操作前,本地需要获得对象的全部数据,结合本次追加写操作的数据,一起计算MD5
值。相应的,对象存储的服务端需要执行类似的操作,操作流程相对复杂,加大了服务端的负担。
按照AWS S3多版本中的说明,多版本特性的开关作用在桶级,包含如下状态:
Buckets can be in one of three states:
- Unversioned (the default)
- Versioning-enabled
- Versioning-suspended
当未开启多版本特性,即Unversioned
,对象执行追加写操作时,通过直接修改对象的数据来实现。
当开启多版本特性,即Versioning-enabled
,有如下选择:
delete-marker
,返回失败。delete-marker
,返回失败。delete-marker
,修改对象的指定版本。当暂停多版本特性,即Versioning-suspended
,有如下选择:
delete-marker
,返回失败。delete-marker
,返回失败。delete-marker
,修改对象的指定版本。不过依据前述接口的定义,没有定义versionId
,推断不支持修改历史版本,因此可以不考虑这么复杂的特性。
参考AWS S3 归档和AWS S3 分级中的说明,处于归档状态的对象,需要先取回才能访问。
显而易见,此处为了维护对象语义,照顾对象存储服务的实现,当对象处于归档状态时,不允许通过调用追加写接口来追加数据。
参考AWS S3 Object Lock中的说明,开启WORM后:
因此从维护对象语义的角度讲,在保护期内的对象、保护期外的对象,均不允许通过调用追加写接口来修改对象。
参考AWS S3 Lifecycle,追加写操作过程中,被操作的对象可能符合生命周期规则,从而被恰好正在运行的后台任务删除掉。
此时有如下选择:
依据SSE-C的说明,客户应用在执行PUT/GET/Head/Copy操作时,均需要提供加密数据的密钥。
即在发起请求时,提供如下头部:
x-amz-copy-source-server-side-encryption-customer-algorithm
x-amz-copy-source-server-side-encryption-customer-key
x-amz-copy-source-server-side-encryption-customer-key-MD5
另外说明对象的元数据和数据均被加密。
依据AWS S3 事件通知中的说明,对象存储服务可以提供事件通知,目前支持的事件类型见文档,显然不包括追加写操作,可以扩展事件名,比如:
s3:ObjectAppended:Put
,使用PUT方式追加。s3:ObjectAppended:Post
,使用POST方式追加。依据AWS S3 消息格式,为了适配追加写操作,可以考虑在object中增加扩展字段,用于说明追加写操作,比如:
sizeAppended
,本次追加写操作的数据量,单位为bytes
。eTagAppended
,本次追加写操作的数据的MD5
值。依据AWS S3 data consistency model的说明,对象存储服务提供read-after-write
的模型。
当多客户端对相同对象并发的发起追加写操作时,实现方案较复杂,需要平衡语义、功能、性能等方面的诉求,设计实现方案。
参照文件的追加写,即把对象当成文件,将追加写操作的数据追加到对象的数据的尾部。
对象规格
使用PUT方式上传的对象,默认小于5G,因此实现时有两种选择,一是维持约束,将5G作为对象大小的上限,二是打破5G的约束,但保持最大规格的约束。
加密存储
可行的实现方案,如下:
方案一
方案二
方案二中,在对象的元数据中需要记录每次追加数据的明文、密文的长度,以及加密算法需要保留的信息。
参照多段对象,将追加写的数据当成一个新的段,原有的对象当成多段方式上传的第一个段。
对象规格
维持多段对象的限制,在接口中增加必要的校验,即
按照上述限制,对象的大小,至多为约48.8TiB。
加密存储
可行的实现方案,如下:
参照文件的追加写,即把对象的最后一个段当成文件,将追加写操作的数据追加到最后一个段的数据的尾部。
对象规格
依据多段对象的限制,如下:
假如直接在多段对象的最后一个段执行追加写操作,如维持上述约束,则可追加写的数据的总和不超过5GiB,应用场景受限。
假如允许多次追加操作后的数据量超出5GiB,则可能破坏对象语义。
加密存储
可行的实现方案,如下:
方案一
方案二
方案二中,在对象的最后一个段的元数据中需要记录每次追加数据的明文、密文的长度,以及加密算法需要保留的信息。
参照多段对象,将追加写的数据当成一个新的段,追加到对象的段的尾部。
对象规格
维持多段对象的限制,在接口中增加必要的校验,即
按照上述限制,对象的大小,至多为约48.8TiB。
加密存储
可行的实现方案,如下: