GridFS简介
GridFS是Mongo的一个子模块,使用GridFS可以基于MongoDB来持久存储文件。并且支持分布式应用(文件分布存储和读取)。作为MongoDB中二进制数据存储在数据库中的解决方案,通常用来处理大文件,对于MongoDB的BSON格式的数据(文档)存储有尺寸限制,最大为16M。但是在实际系统开发中,上传的图片或者文件可能尺寸会很大,此时我们可以借用GridFS来辅助管理这些文件。

GridFS不是MongoDB自身特性,只是一种将大型文件存储在MongoDB的文件规范,所有官方支持的驱动均实现了GridFS规范。GridFS制定大文件在数据库中如何处理,通过开发语言驱动来完成、通过API接口来存储检索大文件。

使用场景
①如果您的文件系统在一个目录中存储的文件数量有限(太多将会影响文件的打开速度),你可以使用GridFS存储尽可能多的文件。
②当你想访问大型文件的部分信息,却不想加载整个文件到内存时,您可以使用GridFS存储文件,并读取文件部分信息,而不需要加载整个文件到内存。
③当你想让你的文件和元数据自动同步并部署在多个系统和设施,你可以使用GridFS实现分布式文件存储。

存储原理
GridFS使用两个集合(collection)存储文件。一个集合是chunks, 用于存储文件内容的二进制数据;一个集合是files,用于存储文件的元数据。GridFS会将两个集合放在一个普通的buket中,并且这两个集合使用buket的名字作为前缀。MongoDB的GridFs默认使用fs命名的buket存放两个文件集合。因此存储文件的两个集合分别会命名为集合fs.files ,集合fs.chunks。当然也可以定义不同的buket名字,甚至在一个数据库中定义多个bukets,但所有的集合的名字都不得超过mongoDB命名空间的限制。

MongoDB集合的命名包括了数据库名字与集合名字,会将数据库名与集合名通过“.”分隔(eg:<database>.<collection>)。而且命名的最大长度不得超过120bytes。当把一个文件存储到GridFS时,如果文件大于chunksize (每个chunk块大小为256KB),会先将文件按照chunk的大小分割成多个chunk块,最终将chunk块的信息存储在fs.chunks集合的多个文档中。然后将文件信息存储在fs.files集合的唯一一份文档中。其中fs.chunks集合中多个文档中的file_id字段对应fs.files集中文档_id字段。

读文件时,先根据查询条件在files集合中找到对应的文档,同时得到_id字段,再根据_id在chunks集合中查询所有files_id等于_id的文档。最后根据n字段顺序读取chunk的data字段数据,还原文件。

存储过程如图下所示:

mongodb 存文件会比原文件小么 mongodb大文件存储规范的原理_存储文件

fs.files集合存储文件元数据,以类json格式文档形式存储。每在GridFS存储一个文件,则会在fs.files集合中对应生成一个文档。
fs.files集合中文档的存储内容如下:

mongodb 存文件会比原文件小么 mongodb大文件存储规范的原理_字段_02

fs.chunks 集合存储文件文件内容的二进制数据,以类json格式文档形式存储。每在GridFS存储一个文件,GridFS就会将文件内容按照chunksize大小(chunk容量为256k)分成多个文件块,然后将文件块按照类json格式存在.chunks集合中,每个文件块对应fs.chunk集合中一个文档。一个存储文件会对应一到多个chunk文档。
fs.chunks集合中文档的存储内容如下:

mongodb 存文件会比原文件小么 mongodb大文件存储规范的原理_gridfs_03

为了提高检索速度 MongoDB为GridFS的两个集合建立了索引。fs.files集合使用是filename与uploadDate字段作为唯一、复合索引。fs.chunk集合使用的是files_id与n字段作为唯一、复合索引。

GridFS命令行操作工具 — mongofiles
Put: 从"C:\Users\ChangQing\Pictures\a.jpg"上传文件a.jpg,存在数据库mytest中,命名为"changqing"
 mongofiles -d gridfs -l "C:\Users\ChangQing\Pictures\a.jpg" put "changqing"
 mongofiles -d gridfs put "C:\Users\ChangQing\Pictures\a.jpg" //此种方式上传文件名即为C:\Users\ChangQing\Pictures\a.jpg
Get: 从数据库gridfs下载文件"changqing"至"C:\Users\ChangQing\Pictures\a\a.jpg"
 mongofiles -d gridfs -l "C:\Users\ChangQing\Pictures\a\a.jpg" get "changqing"
List: 查看文件列表
 mongofiles -d gridfs list
Search: 查找名为"changqing"的文件
 mongofiles -d gridfs search "changqing"
Delete:删除名为"changqing"的文件
 mongofiles -d gridfs delete "changqing"


其中-d指定数据库实例,-l[--local]:上传/下载时的本地文件名,默认与gridfs上的文件名一致。

注意点
GridFs不会自动处理md5值相同的文件,也就是说,同一个文件进行两次put命令,将会在GridFS中对应两个不同的存储,对于存储来说,这是一种浪费。对于md5相同的文件,如果想要在GridFS中只有一个存储,需要通过API进行扩展处理。
MongoDB 不会释放已经占用的硬盘空间。即使删除db中的集合 MongoDB也不会释放磁盘空间。同样,如果使用GridFS存储文件,从GridFS存储中删除无用的垃圾文件,MongoDB依然不会释放磁盘空间的。这会造成磁盘一直在消耗,而无法回收利用的问题。

释放磁盘空间
①可以通过修复数据库来回收磁盘空间,即在mongo shell中运行db.repairDatabase()命令或者db.runCommand({ repairDatabase: 1 })命令。(此命令执行比较慢)。使用通过修复数据库方法回收磁盘时需要注意,待修复磁盘的剩余空间必须大于等于存储数据集占用空间加上2G,否则无法完成修复。因此使用GridFS大量存储文件必须提前考虑设计磁盘回收方案,以解决mongoDB磁盘回收问题。
②使用dump&restore方式,即先删除mongoDB数据库中需要清除的数据,然后使用mongodump备份数据库。备份完成后,删除MongoDB的数据库,使用Mongorestore工具恢复备份数据到数据库。
当使用db.repairDatabase()命令没有足够的磁盘剩余空间时,可以采用dump&restore方式回收磁盘资源。如果MongoDB是副本集模式,dump&restore方式可以做到对外持续服务,在不影响MongoDB正常使用下回收磁盘资源。
MogonDB使用副本集, 实践使用dump&restore方式回收磁盘资源。70G的数据在2小时之内完成数据清理及磁盘回收,并且整个过程不影响MongoDB对外服务,同时可以保证处理过程中数据库增量数据的完整。

参考:http://rdc.hundsun.com/portal/article/703.html