海量小文件,不仅是一个行业难题,更是一个世界性难题,各行各业的诸多场景下都存在类似困境。此类问题难以根治,一部分原因是传统存储解决方案无法高效匹配海量小文件的场景,另一部分原因是从传统存储切换到对象存储的成本略高。此前,针对影像系统和打印系统的海量小文件场景,我们曾花费大量的时间和精力来优化海量小文件的业务存储。借此机会抛砖引玉,来深入聊一下海量小文件问题,分享些许我们在海量小文件优化上的一点心得。
对于海量小文件的定义,行业上尚未有精确规定,更像一个事实标准,而非定义标准。一般情况下,单个文件的体积并不大,一般为数十KB至数MB,但数量规模数十万甚至百万以上级别之巨,这类场景我们称为“海量小文件”。海量小文件通常呈现出一种非结构化文件的大小与数量极不平衡的特性,也正是难以根治的症结所在。
在金融保险的业务系统中,以寿险为例,有两大核心系统最易形成海量小文件场景:一是影像系统,影像系统中需要存储大量非结构化数据,如保险人相关的证件、图片、PDF文件等,同时还有海量的业务过程文件,如交易报文、日志记录等;二是打印系统,打印系统会生成大量的电子保单,同样存在大量过程文件,如渠道交易图像、电子签名照片、电子合同、相关OCR文件等。这两大系统中图像类文件在MB级别,中间过程文件在KB级别,相对比之下中间过程文件的数量级远高于影像类文件,单个系统存储的非结构化数据量在TB级别以上。伴随业务高峰期的到来,单目录下小文件数量可达百万级别,严重影响目录的读写效率,甚至长时间无响应。
在我们的业务实践中,单目录下小文件数量达到一定规模后,该共享存储目录的读写效率会出现显著下降,但影像系统与打印系统对这些非结构化数据的IOPS要求并不低。根据业务险种及渠道的不同,对保险人影像件的调取、电子保单的合成一般都要求在数秒之间,实时单批次电子保单合成同样如此,非实时大批量电子保单合成不超过数分钟。
海量小文件不仅会对在线业务的IOPS有很大影响,而且对这部分业务数据的连续数据保护也构成巨大挑战。根据监管单位的关键业务数据保留要求,近三年至五年的数据均需要可回溯,所有数据均需要永久保留。夜间闲时窗口,是数据备份的重要时间段,错开核心业务夜间核心批处理作业时间后,数据保护的执行窗口相当有限。然而当使用备份软件对海量小文件进行备份、归档时,发现在数据扫描阶段会消耗大量时间扫描小文件来建立备份索引。数据备份阶段,数百万计的小文件将写入磁带或归档存储,速率极其低效。若再加上网络等相关因素影响,海量小文件的备份归档窗口将远超计划,甚至以天计算。
造成海量小文件存储如此低效的根源到底是什么?为什么至今还能成为一个世界性的难题?
要想明白这个问题,就得回到存储本身来追根溯源了。
首先,主要是存储介质问题。传统的存储以机械磁盘为主要介质,而机械磁盘适合顺序的大文件IO读写,不适合随机的小文件IO读写。因此海量小文件的体积、数量都会进一步加剧机械磁盘的劣势。
其次是数据效率问题。传统磁盘文件系统体系结构依靠目录项(Dentry)、索引节点(Inode)以及数据块(Data)来指导、定位、读写文件系统的数据,而目录项(Dentry)、索引节点(Inode)以及数据块(Data)均存储在不同地方,也就是说一次随机文件读写至少进行3次独立访问。面对海量小文件场景时,海量小文件的并发读写汇聚形成了大量的随机访问,磁盘文件系统的体系机制放大了IO的体量,大幅降低磁盘的吞吐。
再者,磁盘文件系统的索引结构在海量小文件场景下无法发挥优势。磁盘文件系统通常采用Hash、B+树组织索引,当面对单目录下数以百万计的小文件时,索引的增删改查将消耗非常多系统资源,甚至耗尽。这一点在对象存储的体系结构里得到了极大的优化和改善,如图1。
图1 传统NAS存储与对象存储体系结构
最后是操作系统IO访问机制受制。以LinuxVirtual Filesystem(简称VFS)为例,VFS提供了统一访问接口和流程,方便与不同磁盘文件系统的对接与扩展。VFS的四种对象的交互太复杂,我们重点关注Process与VFS之间的交互操作,简化Linux I/O堆栈 (如图2)。
图2 Linux I/O Stack简化版
当应用触发文件的随机访问时,会调用一组Syscall去完成文件的操作访问,如open()/seek()/read()/write()/close()等。在这组Syscall中,open()将对文件进行路径查找,将操作系统层级上的路径名转换成其在内核中的表示。open()涉及大量的关联操作,非常消耗系统资源,尤其是针对深层次目录下的文件进行访问。在海量小文件场景下,大量深层次文件的检索直接劣化了open()操作的效率。
随着IT信息科技的发展,针对海量小文件场景已经推出了对象存储。与传统存储不同,对象存储在体系结构上更加扁平,OIDs映射Object对象,直接通过唯一标识定位文件,不论访问什么数据,都能实高效响应,既解决了文件系统体系结构的低效,又屏蔽了文件目录深度带来的巨大开销。
然而,使用对象存储虽然优化了海量小文件场景的访问效率问题,但作为非结构化文件的主存储也会面临些许问题:数据保护手段薄弱、主流备份软件对对象存储的备份支持尚未完善、无法实现全功能数据保护等。虽然可以借助对象存储的跨域复制形成数据冗余,但不满足监管单位对关键数据离线保存的要求。同时,不同于传统NAS存储,对象存储的读写方式也发生了变化,因此涉及业务代码改造。存储更替的过程还涉及大量的业务数据迁移等。
且由传统存储切换到对象存储,对于企业的代价也是相当大的。影像系统和打印系统,都属于保险业务核心,多采用NAS存储业务数据,数十年的更迭都未曾改变。影像、打印系统牵一发而动全身,若需更替存储,涉及大量业务代码改造。影像、打印系统的割接也必须“一刀切”快速切换,无法容忍长时间的业务中断。
那么该如何优化海量小文件呢?
海量小文件所在的环境及链路若未更改,优化的范围就相当有限,唯有从海量小文件结构入手,优化实践的思路大概分为三步:
(1)优化目录层次结构。
深层次的目录结构极大劣化了IO效率,使得文件寻址定位消耗大量的系统资源。过深的目录层次更加剧了海量小文件的归档难度。结合业务实际,我们对影像的存储目录进行了最多6层级的设计,以存储挂载点目录为根,第一级子目录为渠道目录,从不同渠道上传、回写的数据存放至对应目录。第二级子目录为业务功能目录,建立各场景对应的数据目录,数据按照既定的规则写入。第三级至第六级,分别对应年—月—周,方便数据以时间单位归档。值得一提的是“周”目录逻辑作了简化,我们始终以每月的1-7日为第一个周目录,8-14为第二个周目录,以此类推。
目录层次的设计需要贴合业务实际。在我们的设计中,渠道目录为第一级子目录,兼顾了各渠道下的差异,方便各渠道已开展的差异化业务都能有对应数据存储目标。“周”目录的设计没有按照常规周的方式设置,直接规定7天为一个周目录,保持最末子目录的统一性,也屏蔽星期几带来的难度升级。
(2)冷热分离、使用SSD缓存热数据,数据遇冷后转移至NAS存储。
冷热分离能最大化生产实时业务的IO效率,实时业务数据写入SSD上经过优化的目录层次,小文件不会明显大量堆积,IO效率保持稳定。同时SSD高吞吐优势,提供了冷热分离操作的性能基础,分离时不会大幅降低实时业务IO。冷热分离策略可通过脚本或者应用完成,建议至少保证1个月的业务数据作为在线数据。
(3)数据归档。
数据归档是优化的核心之一,其维系着SSD性能发挥与NAS存储效率的平衡。数据由热转冷时,同步进行打包、压缩、归档处理,进在“周”目录对冷数据目录进行打包压缩,将小文件压缩汇聚成大文件。离散的小文件数据都在“周”目录层级,打包压缩后会大幅降低小文件的量级,将离散的小文件转换为数据块连续的大文件,既方便数据归档离线,也大大提高了备份恢复的效率,如图3。
图3 影像海量小文件优化实践示意图
针对传统共享存储海量小文件场景,数据目录结构的设计存在局限性和可维护性差的劣势,有同行反馈可以借鉴一些中高端存储的功能设计,如:访问目录优化,大小IO识别,冷热缓存,自动归档等等特性。
在我们对影像、打印系统的海量小文件进行治理时,就想到了这些优化手段并将其中的部分思路进行了实践,简单分享下我们的优化心得:
1.数据目录优化
优化前我们对影像、打印系统的业务逻辑和数据逻辑进行了调研,涉及分支机构、合作单位、各级渠道类别繁多,每家实现业务功能、交互数据有明显区别。因此,我们结合系统实际重新设计了共享存储的数据目录结构,以渠道目录为主要区分,按照业务功能做数据归集,同时对业务功能内数据按照“年-月-周”分类,减轻单一目录下小文件聚集的问题。目录结构的设计方法是多样化的,怎么减轻目录优化给业务数据处理逻辑代码的改动最小才是关键。
2.大小IO识别
NAS存储的大小IO识别功能,在海量小文件场景下发挥的效能十分有限,我们曾进行简单测试,百万级别小文件随机读写效率在开启NAS IO优化功能前后并没有拉开差距,这也说明“IO数据流”读写效率并不是海量小文件的根因。
由于共享目录的数据读写已经是在文件系统层之上的实现,做大小IO智能识别就不现实了,但这也给我们提供了一种思路:可以把小文件的随机IO聚合成大文件的连续IO。在数据目录优化之后,根据各系统模块对历史数据保留的需求,对“年”“月”“周”目录进行周期性打包压缩,方便后续对历史数据的处理。
3.冷热缓存
在共享存储IO优化的测试后,我们紧跟了冷热分层的缓存功能测试,测试效果如预期一致,也没有明显改善。在这一部分,我们想到了在OS层面做了冷热分离,近期数据在SSD磁盘上,历史数据在NAS上。这种冷热存储分层的优化,并没有改变小文件的存取效率,但提供了对历史数据打包、压缩、归档以及备份等综合处理的性能基础。
4.自动归档
归档最初的设计是针对历史数据的永久性保存,将时间久远的历史数据处理完成后转存至NAS上,再由备份恢复软件进行离线出库。后来根据业务的调研,发现可以将部分非实时数据异步处理后归档至NAS,进一步减缓海量小文件的发生。热数据转冷后一连串的打包、压缩、归档,我们使用统一的定制化脚本实现或直接由应用程序的数据处理部分代码实现,若操作失败则发送告警信息。离线出库也是如此,正式备份前会调用特定脚本去检查数据的完整性,若缺失数据目录也告警通知负责人介入。