思考:
通常,大小在1MB以内的文件称为小文件;百万级数量及以上称为海量。
典型应用场景:
(1)Facebook存储了600亿张以上的图片,推出了专门针对海量小图片定制优化的Haystack进行存储。
(2)淘宝存储超过200亿张图片,平均大小仅为15KB,也推出了针对小文件优化的TFS文件系统存储这些图片,并且进行了开源。
(3)动漫渲染和影视后期制作应用,会使用大量的视频、音频、图像、纹理等原理素材,一部普通的动画电影可能包含超过500万的小文件,平均大小在10-20KB之间。
(4)金融票据影像,需要对大量原始票据进行扫描形成图片和描述信息文件,单个文件大小为几KB至几百KB的不等,文件数量达到数千万乃至数亿,并且逐年增长。
应用范例:FastDFS 海量小文件存储解决之道
Linux通过node存储文件信息,但inode也会消耗硬盘空间,所以硬盘格式化的时候,操作系统自动将硬盘分成两个区域:一个是数据区,存放文件数据;另一个是inode区(inode table),存放inode所包含的信息。
每个inode节点的大小,一般是128字节或256字节。inode节点的总数,在格式化时就给定,一般是每1KB或每2KB就设置一个inode。
小文件主要有2个问题:
合并文件存储相关的配置都在tracker.conf中。配置完成后,重启tracker和storage server。
支持小文件存储,只需要设置tracker的use_trunk_file=true, store_server=1, 其他保持默认即可。
# 是否启用trunk存储,缺省false,需要打开配置
use_trunk_file=true
#trunk文件最小分配单元 字节,缺省256,即使上传的文件只有10字节,也会分配这么多空间。
slot_min_size = 256
#trunk内部存储的最大文件,超过该值会被独立存储,缺省16M,超过这个size的文件,不会存储到trunk file中,而是作为一个单独的文件直接存储到文件系统中。
slot_max_size = 1MB
#trunk文件大小,默认 64MB,不要配置得过大或者过小,最好不要超过256MB。
trunk_file_size = 64MB
store_server=1
注意slot_max_size 的大小,这样才知道多大的文件触发小文件存储机制。
向FastDFS上传文件成功时,服务器返回该文件的存取ID叫做fileid:
为了区别,下面将采用合并存储后的大文件统称为Trunk文件,没有合并存储的文件统称为源文件。注意区分三个概念:
Trunk文件文件名格式:fdfs_storage1/data/00/00/000001 文件名从1开始递增,类型为int。
(1)独立文件存储的file id:
4 bytes 4 bytes 8 bytes 4 bytes 2 bytes
+--------+--------+----------------+--------+-----+
| IP | time | file_size | crc32 |校验值|
文件名(不含后缀名)采用Base64编码,包含如下5个字段(每个字段均为4字节整数):
group1/M00/00/00/wKgqHV4OQQyAbo9YAAAA_fdSpmg855.txt
这个文件名中,除了.txt为文件后缀,wKgqHV4OQQyAbo9YAAAA_fdSpmg855 这部分是一个base64编码缓冲区,组成如下:
(2)合并存储(或小文件存储)file id:
如果采用了合并存储,生成的文件ID将变长,文件名后面多了base64文本长度16字符(12个字节)。
这部分同样采用Base64编码,包含如下几个字段:
group1/M00/00/00/eBuDxWCwrDqITi98AAAA-3Qtcs8AAAAAQAAAgAAAAIA833.txt
采用合并的文件ID更长,因为其中需要加入保存的大文件id以及偏移量,具体包括了如下信息:
4 bytes 4 bytes 8 bytes 4 bytes 4 bytes 4 bytes 4 bytes 2 bytes
+--------+--------+----------------+--------+--------+--------+----------+-----+
| IP | time | file_size | crc32 |trunk ID| offset |alloc_size|校验值|
file_size是小文件实际大小。
trunk ID是对应trunk文件的序号。
offset是对应小文件在trunk文件的偏移起始位置。
alloc_size是实际占用的空间,大小等于小文件大小。
trunk内部是由多个小文件组成,每个小文件都会有一个trunkHeader,以及紧跟在其后的真实数据,结构如下:
|—————————————————---—————— 24bytes ——————————————————————————————————————|
|— 1byte —|— 4bytes —|— 4bytes —|— 4bytes— |— 4bytes—|———-— 7bytes —--———|
|—filetype—|—alloc_size—|—filesize—|—crc32 —|—mtime —|—formatted_ext_name—|
|--—————————————————————— file_data filesize bytes ——————————————————————-|
|———————————————————————— file_data ——————————————————————————————————————|
Trunk文件为64MB(默认),因此每次创建一次Trunk文件总是会产生空余空间,比如为存储一个10MB文件,创建一个Trunk文件,那么就会剩下接近54MB的空间,下次要想再次存储10MB文件时就不需要创建新的文件,存储在已经创建的Trunk文件中即可。另外当删除一个存储的文件时,也会产生空余空间。
每次创建一个trunk文件时,是安装既定的文件大小去创建该文件trunk_file_size,当要删除trunk文件时,需要该trunk文件没有存储小文件才能删除。
在Storage内部会为每个store_path构造一颗以空闲块大小作为关键字的空闲平衡树,相同大小的空闲块保存在链表之中。每当需要存储一个文件时会首先到空闲平衡树中查找大于并且最接近的空闲块,然后试着从该空闲块中分割出多余的部分作为一个新的空闲块,加入到空闲平衡树中。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。