在火山引擎创建EMR或者将Proton应用到自建Hadoop集群后,可以根据使用场景的不同进行针对性性能优化。
Proton在写大文件时,会基于对象存储MPU机制进行异步分片上传,在客户端每写满一个分片之前,默认会优先写到本地磁盘,一个分片写满之后,会异步上传到对象存储,并清除磁盘上的分片数据。所以文件写入速度可能会受限于本地磁盘写入带宽和上传网络带宽,默认情况下,前者远高于后者。因此写入流程可以充分利用多块本地磁盘来提升数据写入性能,可以通过修改core-site.xml
中的fs.tos.multipart.staging-dir
进行多盘配置,比如:
<property> <name>fs.tos.multipart.staging-dir</name> <value>/data01/stagingdir,/data02/stagingdir,/data03/stagingdir</value> </property>
说明
EMR场景下已经默认根据EMR节点的磁盘挂载情况进行了配置,无需再额外配置。手动配置该参数时,需要注意写数据的任务有这些配置目录的读写权限。
同时,基于MPU上传大文件时,由于MPU有10000分片个数的限制,所以需要设置合适的分片值。以写一个100G的对象为例,分片的最小值为10.24MB,这种情况下建议将对应的参数fs.tos.multipart.size
设置为2^n
, 即16MB。
<property> <name>fs.tos.multipart.size</name> <value>16777216</value> <description>默认值为8MB</description> </property>
同时也可以增大并发上传分片的线程数,来加速文件的写操作。
<property> <name>fs.tos.multipart.thread-pool-size</name> <value>64</value> <description>默认值为所在ECS节点核数的2倍,不建议超过超过ECS节点核数的4倍</description> </property>
与写大文件类似,客户端会写一批数据到本地之后,再上传到对象存储。高并发写小文件场景下,客户端可以直接将数据写入内存中,然后上传到对象存储,没有必要写到本地磁盘。因此可以提升小文件的上传速度。可以通过设置fs.tos.multipart.staging-buffer-size
参数的值,作为将数据写入哪种存储介质的阈值,当写入的数据量小于该值时,会写入内存,否则会写入本地磁盘。生产实践中,增大该值时,建议同时调整JVM堆的参数,避免内存不足导致频繁GC影响性能以及OOM问题。
<property> <name>fs.tos.multipart.staging-buffer-size</name> <value>4096</value> <description>默认值为4k,单位为byte</description> <property>
同时,在上传数据到对象存储时,可以选择MPU多分片上传或者直接PUT两种上传模式。对于小文件场景,直接PUT的方式有利于提升写入性能,可以通过设置fs.tos.multipart.threshold
参数配置阈值,如果写入的对象大小大于该值时,会采用MPU分片异步上传的方式,小于该值时,使用直接PUT的方式。
<property> <name>fs.tos.multipart.threshold</name> <value>10485760</value> <description>默认值为10M,不建议将该指设置的特别大,同时如果启用Job Committer场景下,会直接使用MPU</description> </property>
当我们需要对一个大目录执行Rename/MV操作时,客户端首先会批量的list该目录下的子文件/子目录,然后再并行对子文件/子目录并行执行Rename操作,可以通过增大并发数fs.tos.task.thread-pool-size
来提升Rename子目录/子文件的速度。
<property> <name>fs.tos.task.thread-pool-size</name> <value>64</value> <description>默认值为所在ECS节点核数的2倍</description> </property>
Proton默认通过Copy + Delete来执行对一个文件的Rename操作,火山引擎TOS原生支持Rename语义,所以直接使用TOS的Rename语义可以极大的提升Rename的性能。使用Rename语义需要执行以下两个操作:
core-site.xml
的fs.tos.rename.enabled
参数<property> <name>fs.tos.rename.enabled</name> <value>true</value> <description>默认false,开启后直接使用Rename语义代替Copy+Delete操作</description> </property>
说明
大数据场景下,调用Filesystem.getFileStatus(path)
获取目录或者文件的状态信息是一个非常普遍的使用场景。由于对象存储本身不存在文件或者目录的概念,所以我们将对象存储当做文件系统使用时,往往会将对象名称是否以/
结尾来判断一个对象是一个文件还是目录,同时以/
结尾的对象往往是一个空对象。比如对象a/b/c
是一个文件,a/b/c/
是一个目录。
基于对象存储实现Filesystem.getFileStatus(path)
时,可能需要2次headObject + 1次listObject操作,才能得到该path在对象存储上的元数据,这个过程存在RPC次数放大的问题,特别是path对应的对象不存在时。由于Filesystem.getFileStatus(path)
是一个高频操作,所以RPC次数放大问题会随着调用次数的增长而加剧,极端情况下,会打到对象存储QPS流控上限配额,导致性能下降。
火山引擎TOS针对该场景,提供了对应的getFileStatus
接口,可以将3次RPC请求合并为1次RPC请求,解决RPC访问次数放大的问题。通过Proton访问对象存储时,需要在core-site.xml
中添加或修改fs.tos.get-file-status.enabled
参数,才能使用getFileStatus
功能。
<property> <name>fs.tos.get-file-status.enabled</name> <value>true</value> </property>