现正在开发Doms Storage系统的第二版,在这里将第一版的(子)文件系统(Doms FileSystem)部分的设计点滴做一个简单记录,希望开发这类系统的朋友能给予指点、多多交流。
(dss第一版的结构图)
Doms Storage项目的初始设计需求有点特殊,除了一般网络文件所要求的海量文件容量和文件数目之外,系统运行于企业内部(局域)网的,前台应用程序(win app)会在短时间内发出大量的读写请求,所以要求文件的访问响应速度要极快(客户的要求是要能像在本地计算机浏览目录和文件时的感觉一样)。所以很多部件就不能利用现有“臃肿缓慢”的解决方案了(如asp.net,数据库等),于是自己设计了网络子文件系统、数据块传输协议、轻量级HTTP服务等。
其中走最多弯路的是文件系统这个模块,系统要求能容纳多帐号、以树形结构组织目录和文件、每个文件均能设置访问权限等,刚开始时使用mySQL,msSQL等数据库来实现FileIndex(文件索引)服务,数据表的设计倒是很简单,即使用一般的所谓“无限分类”的设计方法,不过当记录达到百万条时,想快速更新多条记录,数据库的响应速度就明显下降了。感觉类似在256MB内存的电脑里运行了一大堆服务,然后用资源管理器展开硬盘某个目录时的感觉——点击“+”号等了两秒才听到“哒”一声。
(PS:如果存储系统用于网站的后台服务,用数据库来做文件索引还是最方便最可靠的方案,因为一般的网站不会对文件的响应速度不需要很高,而且“写”比“读”操作要少很多,后来在uubox.net网站上印证了这点)
无奈之下只好自己写一个专用的轻量级的“数据库”用来实现文件系统的FileIndex这部分了。数据当然是用二进制文件储存了,然而数据结构尝试了多种,最后还是用“链”的形式最快。虽然这种方法已经街知巷闻,不过真正实现起来还是挺麻烦的,下面就是每一条记录的结构(简化的):

其中的id是记录的编号,记录是定长的记录,所以编号也就是文件的偏移地址了。(后记:为了便于扩展,id最好是一个自增的Int64,以B树储存,再由id找出记录的实际文件偏移地址)
pid:父记录的id,如果==0表示顶层(根)目录,-1表示记录已经被废弃。
firstChild:表示第一个子目录或文件(文件和目录被视为“文件”)的id,如果没有的话,则为0。
prevNode:同一层的上一个文件的id。
nextNode:同一层的下一个文件的id。
name:文件名
properties etc...:文件的其他属性,例如本记录是文件还是目录、文件大小、创建时间等等。
另外还有一个存储文件扩展meta信息表、访问权限信息表,结构都是相同的。
如果需要储存下面这样的一个目录结构:

相应的记录情况如下:
。
在检索一个路径对应的信息时,需要将路径的各个部分有序地查找,例如 /ROOT/MUSIC/mp3,先查找ROOT的id,然后找第一个子文件“document”,经比较不是想要的,然后通过document记录的nextNode找到Music,发现符合之后再找它的第一个子文件ape,如此类推。
所以将文件的读写封装起来就需要有这些方法:
int FindFirstFile(int dirId)
int FindNextFile(int fileId)
int AddFile(int parentDirId, fileinfo...) //加子链,或断同层最后一个文件的链,再加记录
int RemoveFile(int fileId) //先断链,将上一记录和下一记录连接
int UpdateFile(int fileId)
int MoveFile(int fileId, int newparentFileID)
再封装一层就可以得到类似 System.IO.DirectoryInfo的类了,(模仿.Net本身的DirectoryInfo和FileInfo有利于开发者的习惯)
MyFileInfo file = new MyFileInfo("/root/music/mp3"); //查找路径对应的fid
MyFileInfo[] = file.GetDirectoriesOrFiles(); //获取本层的所有子目录和文件
到这里,FileIndex模块的基本功能就实现了,当然为了加速某些操作,例如批量添加文件时防止重名,在内部还是做了很多额外的缓存才行。
另外就是为了实现并行访问,需要在外层使用ReaderWriterLock锁,将所有“读”操作放到Read锁,所有“写”操作放到Write锁,这样就不会因多人同时访问而导致数据文件出错了。其次,所有“写”操作完成之后,最好调用FileStream的Flush方法,防止程序异常退出或断电导致数据文件损坏。还有就是数据文件最好先预分配空间(使用FileStream.Seek和Write(0)方法扩大文件大小)再写数据,这样会提高组件的速度。
FileIndex就想到这么多了,以后有空再把chunk和tinyHttpd的一些点滴也发上来。
(dss第一版的结构图)Doms Storage项目的初始设计需求有点特殊,除了一般网络文件所要求的海量文件容量和文件数目之外,系统运行于企业内部(局域)网的,前台应用程序(win app)会在短时间内发出大量的读写请求,所以要求文件的访问响应速度要极快(客户的要求是要能像在本地计算机浏览目录和文件时的感觉一样)。所以很多部件就不能利用现有“臃肿缓慢”的解决方案了(如asp.net,数据库等),于是自己设计了网络子文件系统、数据块传输协议、轻量级HTTP服务等。
其中走最多弯路的是文件系统这个模块,系统要求能容纳多帐号、以树形结构组织目录和文件、每个文件均能设置访问权限等,刚开始时使用mySQL,msSQL等数据库来实现FileIndex(文件索引)服务,数据表的设计倒是很简单,即使用一般的所谓“无限分类”的设计方法,不过当记录达到百万条时,想快速更新多条记录,数据库的响应速度就明显下降了。感觉类似在256MB内存的电脑里运行了一大堆服务,然后用资源管理器展开硬盘某个目录时的感觉——点击“+”号等了两秒才听到“哒”一声。
(PS:如果存储系统用于网站的后台服务,用数据库来做文件索引还是最方便最可靠的方案,因为一般的网站不会对文件的响应速度不需要很高,而且“写”比“读”操作要少很多,后来在uubox.net网站上印证了这点)
无奈之下只好自己写一个专用的轻量级的“数据库”用来实现文件系统的FileIndex这部分了。数据当然是用二进制文件储存了,然而数据结构尝试了多种,最后还是用“链”的形式最快。虽然这种方法已经街知巷闻,不过真正实现起来还是挺麻烦的,下面就是每一条记录的结构(简化的):
其中的id是记录的编号,记录是定长的记录,所以编号也就是文件的偏移地址了。(后记:为了便于扩展,id最好是一个自增的Int64,以B树储存,再由id找出记录的实际文件偏移地址)
pid:父记录的id,如果==0表示顶层(根)目录,-1表示记录已经被废弃。
firstChild:表示第一个子目录或文件(文件和目录被视为“文件”)的id,如果没有的话,则为0。
prevNode:同一层的上一个文件的id。
nextNode:同一层的下一个文件的id。
name:文件名
properties etc...:文件的其他属性,例如本记录是文件还是目录、文件大小、创建时间等等。
另外还有一个存储文件扩展meta信息表、访问权限信息表,结构都是相同的。
如果需要储存下面这样的一个目录结构:

相应的记录情况如下:
。在检索一个路径对应的信息时,需要将路径的各个部分有序地查找,例如 /ROOT/MUSIC/mp3,先查找ROOT的id,然后找第一个子文件“document”,经比较不是想要的,然后通过document记录的nextNode找到Music,发现符合之后再找它的第一个子文件ape,如此类推。
所以将文件的读写封装起来就需要有这些方法:
int FindFirstFile(int dirId)
int FindNextFile(int fileId)
int AddFile(int parentDirId, fileinfo...) //加子链,或断同层最后一个文件的链,再加记录
int RemoveFile(int fileId) //先断链,将上一记录和下一记录连接
int UpdateFile(int fileId)
int MoveFile(int fileId, int newparentFileID)
再封装一层就可以得到类似 System.IO.DirectoryInfo的类了,(模仿.Net本身的DirectoryInfo和FileInfo有利于开发者的习惯)
MyFileInfo file = new MyFileInfo("/root/music/mp3"); //查找路径对应的fid
MyFileInfo[] = file.GetDirectoriesOrFiles(); //获取本层的所有子目录和文件
到这里,FileIndex模块的基本功能就实现了,当然为了加速某些操作,例如批量添加文件时防止重名,在内部还是做了很多额外的缓存才行。
另外就是为了实现并行访问,需要在外层使用ReaderWriterLock锁,将所有“读”操作放到Read锁,所有“写”操作放到Write锁,这样就不会因多人同时访问而导致数据文件出错了。其次,所有“写”操作完成之后,最好调用FileStream的Flush方法,防止程序异常退出或断电导致数据文件损坏。还有就是数据文件最好先预分配空间(使用FileStream.Seek和Write(0)方法扩大文件大小)再写数据,这样会提高组件的速度。
FileIndex就想到这么多了,以后有空再把chunk和tinyHttpd的一些点滴也发上来。

添加至收藏夹