GridFS学习与实践

GridFS学习与实践

    GridFS是基于mongodb存储引擎是实现的“分布式文件系统”,不过严格意义上说,它并非是一个文件系统(以本地文件系统的方式组织),而是基于Nosql模式、application级别的“文件系统”;它具备大数据存储的多个优点。GridFS适宜存储超过16MB的大型文件,不过16M数据在当今互联网时代,已经不足为奇。

 

    GridFS并不是将单个文件直接存储为一个document,而是将文件分成多个parts或者说chunks,然后将每个chunk作为作为一个单独的document存储。默认情况下,GridFS的chunk大小位255k。GridFS使用2个collections来存储这些文件,一个collection存储文件的chunks(实际文件数据),另一个则存储文件的metadata(用户自定义的属性,filename,content-type等)。

 

    当用户查询GridFS中的文件时,客户端或者driver将会重新按序组装这些chunks。用户可以range查询文件,也可以获取文件的任意部分的信息,当然也可以跳过(skip)视频或者音频(任何文件)的中间部分--实现range access of single file。

 

    GridFS不仅对存储超过16M的数据非常有用,用户可以存储任何尺寸的文件,而不需要将它们整个加载入内存。(注意:mongodb中普通的document大小最大为16M,如果超过16M,需要使用GridFS支持)使用GridFS存储文档,可以从Mongodb的高性能、高可用、replications等特性中获益。

 

When shoud I use GridFS?

    对于mongodb而言,如果你的document的大小超过16M,那么应该将它存储在GridFS中。

    在一些情况下,将这些大文件存储在GridFS中,比直接存储在系统级的文件系统中更加适合:

    1)如果你的文件系统对每个目录下文件的个数有限制(或者太多,将会影响文件的打开速度等),你应该根据需要将它们存储在Mongodb中。

    2)如果你的文件数据,有分数据中心镜像保存(大数据情况,可用性保证),那么Mongodb是一个不错的选择。

    3)如果你希望访问一个超大的文件,而不希望将它全部加入内存,而是有“range access”的情况---分段读取,那么GridFS天生就具备这种能力,你可以随意访问任意片段。

 

    对于一个大文件,如果你希望原子性的更新它的全部内容,那么GridFS将不适合。

    如果你文件大小通常小于16M,比如一些图片、CSS数据,那么开发者应该手动设定将文件保存在一个document中,可以使用BinData数据类型来保存这些数据,直接存储在Mongodb Collection中而非GridFS。(参见org.bson.types.Binary)不过,我们还是有一种倾向,将这些小文件也使用GridFS保存,当然性能也不会差的太多,如果我们的文件通常在1M左右,而不需要“range access”,特别是图片、CSS静态文件,我们可以在存储时手动设置它的chunkSize,避免文件被切分成多个chunks,来提高性能。

 

GridFS Collections

    GridFS将文件存储在2个Collections中:

    1)chunks:存储二进制数据。

{
  "_id" : <ObjectId>,	//chunk ID,全局唯一
  "files_id" : <ObjectId>,	//file collection表的"_id"值
  "n" : <num>,	//chunk的序列号,第一个chunk位0。
  "data" : <binary>	//二进制数据
}

 

    2)files:存储文件的metadata。

{
  "_id" : <ObjectId>,	//
  "length" : <num>,	//文件的长度
  "chunkSize" : <num>,	//chunkSize
  "uploadDate" : <timestamp>,	//文件生成的时间戳
  "md5" : <hash>,	//HASH值

  "filename" : <string>,	//文件名,用户指定
  "contentType" : <string>,	//
  "aliases" : <string array>,	//别名
  "metadata" : <dataObject>,	//用户自定的metadata
}

 

    GridFS将上述两个collection的命名默认位fs.files、fs.chunks。

 

GridFS Index

    GridFS在chunks这个collection上,对“files_id”和“n”两个字段创建了“unique、compound”索引,file_id的值与fs.files表中的_id对应。那么从chunks表中获取一个file的所有chunks将是非常简单。

    GridFS API中提供了多种方式可以获取一个文件或者指定的chunk。

 

代码实践

    1、MongoDBClient.java:通用代码,用来链接mongodb实例,封装indexes等。

/**
 * Author: qing
 * Date: 14-10-11
 */
public class MongoDBClient {

    private BSONConvertor convertor = BSONConvertor.getInstance();

    protected MongoDBDriver mongoDBDriver;

    protected String dbname;

    protected boolean inited = false;

    public void setMongoDBDriver(MongoDBDriver mongoDBDriver) {
        this.mongoDBDriver = mongoDBDriver;
    }

    public void setDbname(String dbname) {
        this.dbname = dbname;
    }

    public BSONConvertor getConvertor() {
        return convertor;
    }

    public synchronized void init() {
        if(inited) {
            return;
        }
        mongoDBDriver.ensureIndex(this.dbname);
        inited = true;
    }

    public DBCollection getCollection(String collectionName) {
        DB db = mongoDBDriver.getDb(dbname);
        DBCollection dbc = db.getCollection(collectionName);
        return dbc;
    }

    public DB getDB() {
        return mongoDBDriver.getDb(this.dbname);
    }

}

 

    2、GridFSClient.java:用来访问指定GridFS库。

/**
 * Author: qing
 * Date: 14-10-11
 */
public class GridFSClient extends MongoDBClient{

    private GridFS gridFS = null;

    private Object lock = new Object();

    public void setMongoDBDriver(MongoDBDriver mongoDBDriver) {
        this.mongoDBDriver = mongoDBDriver;
    }

    public GridFS getInstance() {
        if(gridFS != null) {
            return gridFS;
        }
        synchronized (lock) {
            if(gridFS != null) {
                return gridFS;
            }
            gridFS = new GridFS(mongoDBDriver.getDb(this.dbname));
            return gridFS;
        }

    }

    public void close() {
        mongoDBDriver.close();
    }
}

 

    3、spring配置:

    <bean id="mongodbProperties" class="org.springframework.beans.factory.config.PropertiesFactoryBean">
        <property name="locations">
            <list>
                <value>classpath:mongodb-config.properties</value>
            </list>
        </property>
    </bean>

    <bean id="mongodbDriver" class="com.test.common.mongo.MongoDBDriver" init-method="init" destroy-method="close">
        <property name="properties" ref="mongodbProperties"/>
        <property name="configuration">
            <bean class="com.test.common.mongo.object.MongoDBConfig">
                <property name="addresses" value="${mongodb.filesystem.addresses}"/>
                <property name="credentials">
                    <list>
                        <bean class="com.test.common.mongo.object.MongoDBCredential">
                            <property name="dbname" value="${mongodb.filesystem.dbname}"/>
                            <property name="username" value="${mongodb.filesystem.username}"/>
                            <property name="password" value="${mongodb.filesystem.password}"/>
                        </bean>
                    </list>
                </property>

            </bean>
        </property>
    </bean>


    <bean id="gridFSClient" class="com.test.common.mongo.GridFSClient">
        <property name="mongoDBDriver" ref="mongodbDriver"/>
        <property name="dbname" value="${mongodb.filesystem.dbname}"/>
    </bean>

 

    5、GridFS操作--写入

GridFS gridFS = gridFSClient.getInstance();
String filename = randomFileName();//生成一个随机的名字
//如果有文件重复,则重新生成filename
while (true) {
	GridFSDBFile _current = gridFS.findOne(filename);
	//如果文件不存在,则保存操作
	if (_current == null) {
		break;
	}
	//否则,重新生成文件名称
	filename = randomFileName();
}

GridFSInputFile file = gridFS.createFile(inputStream, filename);
//file.setChunkSize(1 * 1024 * 1024);//1M,通常我们采用默认
DBObject metadata = new BasicDBObject();
metadata.put("format",format);
file.setMetaData(metadata);
file.save();

 

    6、GridFS操作--读取

GridFS gridFS = gridFSClient.getInstance();
GridFSDBFile file = gridFS.findOne(path + "/" + filename);
if(file != null) {
	return file.getInputStream();//无需关闭,GridFS会在读取结束后关闭
}

 

    对于GridFS作为online类型web文件存储,nginx中支持了gridfs模块:https://github.com/mdirolf/nginx-gridfs,相关的安装和配置,请参考其他文档。

 

 

参考文档:

1)GridFS:【官方文档】

2)GridFS构建解答:【1】【2】  

 

你可能感兴趣的:(grid)