海量小文件存储最优解决方案,杉岩数据MOS完美解决
面对千亿量级的小文件,存储系统压力山大
所谓小文件,指的是存储占用空间相对较小的文件,一般来说低于64MB的文件就可以被认定为小文件,而大量的小文件大小则在几KB到几十KB之间。在云计算、大数据业务中,文本、图片、音乐等是典型的小文件应用场景。
随着数字化创新的加速,组织内部的数据呈现出指数级增长的趋势,特别是小文件更是随着业务增长到一个巨大的量级。与大文件的存储不同的是,大量磁盘在小文件存储场景中的性能极低,单块企业级SATA磁盘如果全部存储4KB左右的小文件,带宽只有520KB/s,远远小于应有的120MB/s的带宽标准值,很容易因为存储系统的性能不足造成上层应用“卡顿”。把磁盘全部换成固态盘固然可以解决问题,但是,固态盘的价格数倍于SATA磁盘,对于很多用户来说,全面的应用固态盘在成本上仍然不现实。
百亿~万亿量级的小文件对存储性能提出
而且,每个应用场景对于存储系统的性能往往有着不同的要求。例如,某领先电商平台已经存储了数量以百亿计算的图片文件,这些图片平均大小在15KB左右,用户对于这些图片文件的读取完全是随机读取,一旦大量用户同时在线访问网址或者搜索商品,往往就会给存储系统的随机读写能力带来巨大的挑战;在交警系统中,路口的抓拍摄像头会将违章图片传送至区中心的计算服务器,不仅摄像头数量多,而且每台摄像头每天都可能生成数千乃至上万张照片,某市每天相关图片写入甚至超过一亿张,要降低存储系统的压力,就需要及时删除正常的图片,这对存储系统的写入、删除能力要求很高。
海量小文件带来的问题
1、海量数据的性能问题。这是64K写的性能测试,一开始的1万到4亿个对象,这个时候会发现写入的性能逐步下降,从开始有15K的oes到下降到2.5,实际上下滑比例已经达到了80%多,海量小文件的冲击是非常大的。
究其原因,如果用的是Fd,底下的文件是要从Fd到dentry再到inode,superblock,1亿的文件,256B,它已经占了24G,小文件带来的是要直接操作磁盘,直接操作磁盘会导致性能下降,海量的小文件会破坏空间的连续性,会产生大量的随即读写。海量小文件会有大量源数据,性能还是不能得到流畅。
2、数据恢复效率问题, 数据在做恢复的过程中效率的问题,海量场景下恢复的速度,后者是前者的10倍,海量的小文件场景下,数据恢复的速度比较缓慢,而且效率很低,在此期间如果刚好不巧有业务请求过来,有可能会看到Slow Requses或是blocked,是存在风险的。
3、扩容、恢复以后集群的性能还会出现骤降的情况。如果做了扩容或是故障恢复,这个时候大量的小文件可能会被删除,可能会出现大量的碎片,这个碎片可能出现在磁盘上,如果不进行碎片的回收,里面系统的性能会出现骤降的情况,这是我们测的一组数据,前面很平稳,一旦扩容等恢复以后,这个比例会变大。
4、海量小文件的场景,数据的迁移效率比较低。可能想用数据冷热分层的方式把热数据移到冷的存储池,这个时候有大量的小文件在这个过程中会产生迁移,而且这个迁移的过程中前面也会有,迁移以后数据可能进行大量的删除操作。之前测试的4000万的小文件迁移消耗时间大于72个小时,算下来要两天多的时间。这种情况下迁移的效率太低了。
解决小文件问题的几点想法
海量小文件,是不是可以做合并?合并之后的文件,不是体积很小的文件,空间的使用效率是比较高的,对磁盘还是比较友好的。合并以后是不是可以提高读写的性能?
我们都知道,现在流数据,而且是写在对象里,源数据是不是可以独立?为什么做独立?源数据独立有两个好处,源数据可以更加灵活的做选择存储,可以对这些源数据做一些其他的操作,比如说源数据是不是可以做其他方面的管理、检索,都很方便的做。是不是可以把源数据部分抽出来?
同步和删除的流程是不是可以改进?如果做到第一点,文件做了合并的话,传输也是可以做合并的,而且删除操作的时候,批量删除是可以提升效率的。
基于这三点,Sandstone Mos针对海量小文件的问题提出了方案。
Sandstone Mos海量小文件的方案
第一步我们把源数据从Data pool抽出来进行分离,源数据存在index pool,部署方式是不是和其他的部署方式不一样,没必要和Data pool绑在一起。每个对象把源数据抽出来以后,放在BI,源数据有它的BI,会有对应的Data pool数据实体。我们去到ID里的Index,多个对象放在一起管理,这个时候很方便的能实现多个版本的数据管理。
这方面我们也需要一些数据,简化整体的数据结构。比如说读写数据有非常重要的结构,在应用数据结构的时候发现有很多数据,这些数据不好的地方是没怎么用、没什么用,放在这里很占空间。里面有RGW很占空间,读写数据的时候其实没必要关心这个对象是什么样的,我们也去掉一部分的冗余数据简化数据结构。
在这上面我们也做了文件合并的工作。开始的想法是不同的对象可以写到一个大项里,实际上这里会有几个问题,对象写进来以后应该朝哪个地方写?后面会讲一下大概的流程,处理小文件写入的时候可不可以给一些因素简化流程?不同的bucke的小文件合并到不同的大文件里,如果对象是同一个bucket会写到同一个大对象里。我们都知道用的是shard的思想,BI分了不同的shard,为了简化处理流程,,不同bucket的小文件存到不同的大文件。
文件合并以后要读的时候应该怎么读?小文件的读取比较简单,存进去改一下加上offs和lenght就可以了,所有的对象指向的数据实体只有一个,对象读的时候知道开始位置就可以得到对应小文件的数据。还是会有问题,简单的话是合并就完了,问题是这个东西进来以后,应该写到哪个对象上?所以对象进来以后可能好几层,这里就会有一个问题,假设两个对象是写到同一个大对象上,这个大对象的空间分配会是一个关键的问题,对象的空间分配一定要统一一个来源,可以避免写同一个对象,保证区间不会相互交集。我们在Bucket使用omap_key存储大对象元数据,可以起到空间分配的作用,对象写进来以后,最后是不是要确定对象放在哪个Bucket shard上,可以看到需要用哪个merged object,可能第一个写0到24,第二个会记一个状态,会把他的状态记为已经使用,下一次线程过来就不会用空间。会把空闲的空间分配给他,我每个线程写的时候拿到的空间分配信息之后,就可以写相应的偏移。我们每个对象在上面都做了空间的管理。
删除操作可能会出现几个问题,删除的空间怎么进行回收?可能删了几个小对象后大对象整体会出现空洞的情况,什么时候进行整体的回收?进行整体回收的时候这些小文件要怎么进行适度的迁移?前面说的空间管理情况下有条目的状态,既可以在空间分配中用到,也可以在删除小文件的时候用到,删除小文件的时候是异步删除的方式,可以在这个大对象上做个状态的更改,里面会涉及到两个操作,不单只是删除小文件的时候要删原来的小文件BI,还要改大BI的状态。
我们有两个对象可能被删除会标deleted的状态,删除的时候如果数据不进行立刻的回收,空间是存在一定的浪费。这个时候要灵活运用object的接口,这个时候空间做了一定的回收,所以这个接口是比较友好的,对于快速释放空间,提高空间的利用率,有可能大对象的区间被删除,当空间使用率达到一定程度的时候,我们要进行整体的回收,大对象可以反向索引到每个小对象上,小对象也需要记录自己所处的大对象,这两个操作是为了后面铺垫的,要做删除操作的时候,小对象必须自己清楚,我是属于哪个大对象的?删除的时候可以在上面记这个状态,大对象有要记自己被哪些小对象引用,为什么这样处理?如果说空间使用率低到一定程度的时候,要进行整体的回收,这个时候还在使用小文件怎么办?大对象必须反向索引到有哪些小对象在用它?这个时候我们把小文件的数据挪出来,迁移到其他大对象里。整体的机器操作是完全的异步方式,如果你在删除对象的时候会进行res的,为了处理其他的问题,把整个过程变成异步的,包括BI的删除和数据的删除都变成异步的方式。
同步和数据迁移原理是一样的,无非是把大量的数据从一个站点,从一个存储池迁到另一个存储池,这一点是最为头疼的,会涉及到非常多逻辑的问题,比如说合并后的文件是怎么进行同步的?小文件的元数据,合并以后的文件进行同步,元数据怎么同步,是不是会存在先后顺序的问题?
大家如果了解的话,多站点的同步分为两部分,一个是全量同步,一个是增量同步,两个同步的方式和base的实际结构是不太一样的。如果你是全量投入的情况下,AB两个站点是进行全量同步的,我做全量同步的时候会把里面的对象都拿过来,增量同步就不一样。先分场景做,我第一步是先把这些合并之后的文件、数据整体的拉过去,包括前面提到的大对象,实际上也有自己的BI,这个时候对我来说也是一个对象,我会把这些数据统一丢过去,小文件的元数据还是维持。
增量同步的情况比较复杂,这个大对象是写到100,这个时候怎么把它同步过来?这里面有点取巧,像这种情况下,我们要处理这个逻辑,前面全量可能把这个拉过去,根据多个BI,如果发现是多条边、是写在同一个同类项上,这个时候会把数据做合并类似于前面同步的方式,把数据合并丢过去。最后源数据还是通过BI的方式,把源数据拉过来,每次请求的效率会比以前高一些。
两个数据池迁移的时候,跟全量同步的场景是一样的,全量同步的情况下和存储数据池的迁移是一样的场景,这个时候没有单列存储池的场景。
1.海量小文件的性能。我们在做完文件合并以后,文件数是能明显下降的,解决海量数据场景下的性能问题,文件合并以后,对于磁盘来说是比较友好的,合并以后可能是比较大的文件,比如说我们合并至少都是4M的文件,合并以后文件的数据量会明显下降。
2.恢复效率。文件合并以后效率也可以得到巨大的支撑,原来都是32K的文件,有100个32K的文件,合并以后是3M多的文件,每次请求要恢复的文件数目明显减少了。
3.扩容后的性能骤降的问题,合并后的文件对于空间的使用率更高,就算会出现大量的数据删除情况,这个时候对于磁盘的使用也是更加友好的,因为这个时候磁盘也不会出现过度的空洞碎片。
4.数据迁移的整体做了优化,数据合并之后迁移的效率也会明显的提高,前面可能分了100个小文件,可能是请求100次的32K文件,可能是100次32K的请求,合并之后只需要一次移过量就可以了,后面的所有操作都是不需要再跨两个Pool。