减少OSS请求并提升挂载点性能

使用ossfs 2.0OSS(对象存储)交互时,合理优化发往OSS服务端的元数据请求量,不仅能减少OSS请求以节省服务调用成本,还能提升系统并发处理能力,同时改善挂载点的读写性能。

基本原理

ossfs 2.0基于FUSE(Filesystem in Userspace)框架进行开发,可以将文件系统的元数据操作接口,转换为对应的OSS请求,从而实现通过文件系统的操作方式访问OSS存储资源。

命令

接口转换规则

lookup

当执行lookupstat操作且元数据缓存失效时,ossfs 2.0将首先发送GetObjectMeta请求到OSS,用于获取同名对象的属性信息。

GetObjectMeta请求返回404响应(表示对象不存在),则进一步发送ListObject(max-keys=1)请求,以查询是否存在同名的虚拟文件夹对象。

stat

readdir

执行readdirreaddirplus操作,ossfs 2.0会循环发送ListObject请求到OSS。

需注意,ossfs 2.0默认启用readdirplus功能。在该功能开启状态下,ListObject请求的结果将用于更新对应文件夹下所有子项的元数据缓存信息,进而有效减少后续对子文件的元数据请求次数。

readdirplus

场景解析

在文件系统中访问一个文件与在OSS中访问一个同名对象存在以下显著差异:

文件访问方式

OSS采用从根目录逐级向下的访问方式访问文件,以获取位于/dir/object路径下object文件的属性信息为例,使用stat /dir/object命令执行流程如下:

  1. 首先对/dir执行操作,发送GetObjectMeta dir请求,若返回404 Not Found则表明该对象不存在,继而发送ListObject (max-keys=1)dir/请求,若返回200 OK,则说明存在对应的虚拟文件夹。

  2. 对/dir/object执行操作,发送GetObjectMeta dir/object请求,若返回200 OK,则成功获取到对象属性信息。

综上分析可得,单次stat /dir/object命令执行,最终转换为两次GetObjectMeta请求与一次ListObject请求。并且文件系统的元数据请求会转换为多个OSS请求,且随文件的深度增长而增长,造成性能大幅下降。

文件元数据缓存影响

重要

ossfs 2.0默认开启文件元数据缓存,且元数据缓存默认失效时间为60秒。元数据缓存的缓存容量基于FUSE low-level API进行实现,由操作系统内核决定何时淘汰,内存较多的机器通常可以缓存更多的元数据信息。

以获取位于/dir/目录下100个子文件的属性信息为例,说明文件元数据缓存对性能的影响。

  • 未开启元数据缓存

    • 已知文件列表访问文件:

      循环执行stat /dir/object-<i> 命令,每一次stat操作都将转换为一次GetObjectMeta请求,最终产生100GetObjectMeta请求发送到OSS以获取文件属性,从而导致元数据请求次数过多影响性能。

    • 未知文件列表访问文件:

      执行ls命令,该操作将转换为一次ListObject请求发送到OSS以获取文件列表,再通过获取到的文件列表循环执行stat /dir/object-<i>命令获取文件属性,最终将产生一次ListObject请求和100GetObjectMeta请求发送至OSS,从而导致元数据请求次数过多影响性能。

  • 开启元数据缓存

    • 已知文件列表访问文件:

      循环执行stat /dir/object-<i> 命令,每一次stat操作都将转换为一次GetObjectMeta请求,最终产生100GetObjectMeta请求。这100次请求在元数据缓存有效期内会直接命中本地元数据缓存获取文件属性,从而有效减少发送至OSS的请求次数。

    • 未知文件列表访问文件:

      执行ls命令,该操作将转换为一次ListObject请求发送至OSS同时更新本地元数据缓存。在完成缓存更新后,再循环执行stat /dir/object-<i>命令,由于此时元数据已在本地缓存中,将不会发送额外的OSS请求。

综上分析可得,元数据缓存机制可以有效减少重复发送到OSS的请求次数,若要遍历访问某个文件夹下的所有文件时,通过ls可以提前预载元数据缓存,从而有效减少后续对子文件的OSS请求。

优化方式

通过以下方式减少发送至OSS的元数据请求从而提升整体性能:

延长元数据缓存时间

如果读取的数据一旦上传到OSS中就不会修改,或修改时间间隔远大于元数据缓存时间,可通过attr_timeout挂载选项配置更长的元数据缓存失效时间,从而减少重复的元数据请求以提升性能。挂载选项配置示例如下。

  • 业务场景:在数据标注场景中,系统会读取预先收集的一批原始数据,经处理后再生成一批新数据。此场景下原始数据一旦上传至OSS就不会修改。

  • 挂载配置:在ossfs 2.0配置文件中,配置元数据缓存失效时间为7200秒。

    # Bucket所处Endpoint(地域节点)
    --oss_endpoint=https://oss-cn-hangzhou-internal.aliyuncs.com
    
    # Bucket名称
    --oss_bucket=bucketName
    
    # 元数据缓存失效时间
    --attr_timeout=7200
    
    # 访问密钥AccessKey IDAccessKey Secret(ossfs 2.0.1及后续版本该配置项可选)
    --oss_access_key_id=LTAI******************
    --oss_access_key_secret=8CE4**********************

获取文件列表后操作

在遍历访问某个目录下的所有文件时,可先通过ls命令或发送ListObject请求,将目标文件夹下所有文件的元数据提前加载至本地元数据缓存,同时配合较长的缓存失效时间,以此减少重复的元数据请求,最终实现整体性能的提升。

ls命令可替换为任意用于读取文件夹内容的高级语言程序。以获取/mnt/data/目录下文件列表为例,常见示例如下。

Python

os.listdir('/mnt/data/')

Go

entries, err := os.ReadDir("/mnt/data/")

C

dir = opendir("/mnt/data/");
if (dir != NULL) {
  struct dirent *entry;
  while((entry = readdir(dir)) != NULL) {}
  closedir(dir);
}

使用negative缓存加速文件创建速度

文件系统在创建新文件时,会依次执行lookup + create两次系统调用:

  1. 使用lookup来查询对应文件是否存在,在ossfs 2.0中会被解析成GetObjectMeta + ListObjects请求。

  2. 若返回404不存在,则使用create来创建对应文件,在ossfs 2.0中执行create时也需要发送GetObjectMeta + ListObjects请求来查询OSS中是否存在该文件。

故一次创建新文件的流程,会执行4OSS的元数据查询操作。

ossfs 2.0支持缓存OSS返回的404请求减少后续的重复请求。开启方式为挂载时指定:

  • --oss_negative_cache_timeout=30(单位:秒,默认0,建议小于attr_timeout

  • --oss_negative_cache_size=10000(默认10000个)

oss negative缓存开启后,创建新文件时lookup中发送的404请求会被缓存下来,在create中再次查询会命中negative缓存,不再发送请求给OSS。此时原先一个创建新文件流程中的4OSS请求被减少成2次。

重要

开启oss negative缓存后,若曾缓存过文件object-A404缓存项,即便在OSS上立刻创建了object-A,也需要等到oss_negative_cache_timeout秒缓存失效后,才能在挂载点中看到该文件。在一致性要求高的场景不建议开启。

性能对比

测试方法:在与目标OSS Bucket相同地域的ECS实例中,使用ossfs 2.0工具通过内网域名挂载OSS Bucket并开启元数据缓存,随后读取已挂载Bucket目录下10000个文件的元数据。

测试结果

操作

耗时

未预载元数据缓存(未提前执行ls命令,直接读取文件夹下文件元数据)

111 秒

预载元数据缓存(提前执行ls命令,再读取文件夹下文件元数据)

18 秒

测试结论:在大量文件元数据读取的场景下,提前进行预载元数据缓存并配合合理的元数据缓存失效时间,可以大幅减少发送至OSS的元数据请求次数从而提升整体性能。