难度问题:海量的小文件存储(300分)

  • 主题发起人 主题发起人 Adnil
  • 开始时间 开始时间
A

Adnil

Unregistered / Unconfirmed
GUEST, unregistred user!
程序大概要生成100万个小文件,每个文件的大小大概是1K,总容量是1G左右,但由于文件系统的关系,磁盘实际占用空间要用掉16G。 文件生成之后不会改动,只用来读取或者删除,因为对文件生成和读取的速度要求比较高,所以没办法使用数据库系统来保存。

请问有啥办法又能保证文件生成和读取的速度,又尽可能降低磁盘的占用空间? 谢谢!
 
如果单个小文件的最大不超过某个数量, 例如 2K, 则可以使用单个大文件的方式:
即一个大文件包含所有小文件的内容, 这样既不占硬盘, 访问速度也更快;
但是要写一点自己维护数据块(相当于小文件)的功能: 例如: 创建,写,删除等..., 不过也非常简单的;
 
有想过使用大文件,不过这样又得创建一个索引文件来记录某个文件的在大文件中的起始地址和大小,索引文件读取(根据小文件名)的性能又成了问题。
 
>>创建一个索引文件来记录某个文件的在大文件中的起始地址和大小,索引文件读取(根据小文件名)的性能又成了问题

你是说:根据文件名,找到索引文件的对应的数据信息成为性能瓶颈?
 
文件名采用一定的规则
如果平均都是2K以下.那给每个文件2K的空间,相当于每个文件相等尺寸,这样查找也很方便。
文件名/数据/文件名/数据/..................

这样速度会很快吧。生成的文件如果很大,按个规则适当的分成几个文件就成了。

听听其他高手的意见。
 
看下磁盘簇大小 小于这个簇大小的文件 都是浪费
 
因为每次磁盘IO 至少都是读写一簇 假如你的磁盘簇为16KB 你可以把16个1KB文件合并
 
同意xusong168的观点:
可以从磁盘的结构上入手,即所产生的文件是簇的整数倍,这样的浪费是最小的
 
To: aerobull
文件名规则就是 xxxxx.dat, xxxxx代表某个数字,没有规律。现在假设我要读取12345.dat的内容,如何从合并后的文件中读取呢?

To: xusong168
磁盘簇的大小不限,因为这是在最终用户的电脑上运行。
 
1. 楼主应该重新考虑系统设计, 数据库正好是做这方面的事情的, 本机数据库速度不会慢;
2. 如果使用一个大文件, 前面几位所谓磁盘镞的说法没有意义的, 16个1k文件合并成一个文件的想法是天真的, 效果比1k一个文件还差, 维护更困难;
3. 最简单的办法就是一个大文件, 固定2K数据块, 例如: 简单的文件结果如下:
type
index_type= packed record
name : string[11]; //名称(序号), 可以不包括 '.dat', 如果用数字最好, 直接用 integer, 处理(检索)速度快几十倍以上;
index: integer; // 数据块位移(地址)
end; // 共16字节
data_block = array[0..2047] of char; // 一个数据块;
const max_data_count = 100000;
整个文件结构(内容)如下:
-------------------------------------------------------
max_data_count * index_type; // 头部, 索引信息
max_data_count * data_bock; // 数据
-----------------------------------------------------------------
编程操作的几个关键:
-----------------------------------------------------------------
0. 建议index_type中的name直接用整数: integer;
1. 固定结构文件的定位,读写操作...
2. 对文件头部(索引信息)要一次读出(或写入)约16M数据到内存中(就是个数组);
3. 如果数据比较固定(例如字库等类似的数据), 组织数据(建立原始文件)的时候最好按文件名(序号)排好序, 这个非常重要, 效果最好;
4. 如果数据不固定, 那么剩下的问题就是对文件名(实际上是序号)排序检索的工作了;
 
文件名规则就是 xxxxx.dat, xxxxx代表某个数字,没有规律。现在假设我要读取12345.dat的内容,如何从合并后的文件中读取呢?

簇是最小单位 就算你不用 系统也给你读写了
读一簇进来 在缓冲区里弄个指针 就可以在 合并后文件里 访问合并前文件数据
 
继续
假如你合并16个文件 那么每个文件16KB
原来的文件名 他是个整数 mod 16之后 作为新文件名
0~15 => 0.dat
16~31 => 1.dat
 
To: 新世纪
谢谢你提供的方案,不过本机数据库可能不太适合,读取和写入速度都比文件系统要慢很多,1G以上的单机数据库也不够稳定。固定2K数据块,对数据要求高了一些,我似乎不能保证文件一定小于1k或者2k。文件头部索引信息(存放原始文件id,原始文件在大文件中的起始地址,原始文件大小。。。)是否放在另外一个文件中比较好? 另外还有从文件头部索引信息搜索id的效率问题。问题似乎越来越多。。。。哎

To: xusong168
我前面提到过,这个数字没有规律的
 
我觉得比较好的方法是创建一个索引文件和一个内容文件。如调用例子
var index[0..1000000] of Pointer; //首先定义一个以最大文件名数据大小的数据索引文件。这个索引以文件名数值当数组上标和下标存取偏移,这样做索引文件是大了一点,但是可以省略搜索偏移的时间。
index[文件名ID] := 文件名ID内容的偏移地址。
在以后的读取当中,就可以打开索引文件和内容文件。首先在索引文件中以:
文件名ID偏移 := index[文件名ID]; //这样直接获取内容文件的偏移而不用搜索的办法取得索引偏移,速度是很可观的。
 
经过上面的操作之后,内容文件可以定义一个内容结构来存取数据。如:
TMemFile = packed record
FileID:DWORD; // 文件名ID,用以判断是否取得正确的文件内容。
FileMemSize:DWORD; // 内容大小(在字节),用以判断数据的大小。
FileMem:array[0..MAX_DATA] of byte; //这里存放数据。
end;
 
这样做也有一个弊处就是如果修改前面的数据导至后面的偏移改变而必须要修正索引文件中的偏移位置。
 
如果全部装入内存,需要不到1G左右空间,你那个文件能不能再弄小点,如果能再小到一半,那么大概4-500M内存就装下了,现在内存条这么便宜,装个几G一点问题没有。
存取速度极快,占用空间又小![:D]
 
楼主走入误区了
数据库完全能满足你要求,我曾在某项目中使用Access桌面数据库存储700万条数据,其中一个表还存储了大对象数据,效果很好;
使用自定义文件存储,然后自己写增、删、改、查的函数或方法不是好办法,这会影响你的程序的坚固性和可维护性;
劝楼主寻找一个好的小型数据库,执行效率是不成问题的,只要规划好数据库的索引策略就能有很好速度,使用数据库可以减少你程序的开发量,提高坚固性和可维护性,使数据更安全可靠(可以对数据库做备份恢复);
简单的方法就是最坚固的方法,也可能是最有效的方法。
 
FireBird,SQLite等单机数据库都可以做到很大
 
To: [xiaopei]
你的方案挺不错的,只要id小于100万,索引定位的执行效率非常高。谢谢!你说的弊处对我来说关系不大,我前面说过数据只有生成和读取和删除,没有修改的。

To: man8888
运行的时候,往往只需要读某几个文件就行了,没有理由去加载1G的内容浪费内存。另外文件大小不是我说了算的。

To: guxiaoke
我知道索引的,但是只要是使用了数据库系统,最终执行效率天生就比文件系统要低一个级别,原因不多说了,写个程序比较一下读一个小文件和创建对象,然后执行sql然后再读取Blob/Memo字段的速度就行了。
 
后退
顶部