在各類系統(tǒng)應(yīng)用服務(wù)端開發(fā)中,我們經(jīng)常會(huì)遇到文件存儲(chǔ)的問題。 常見的磁盤文件系統(tǒng),DBMS傳統(tǒng)文件流存儲(chǔ)。今天我們看一下基于NoSQL數(shù)據(jù)庫MongoDb的存儲(chǔ)方案。筆者環(huán)境 以CentOS 6.5,MongoDb 2.6.3, Nginx-1.4.7 為例,您需要了解linux常用命令。
先來回顧一下MongoDb的內(nèi)部文件結(jié)構(gòu)

然后是GridFs的結(jié)構(gòu)

GridFS在數(shù)據(jù)庫中,默認(rèn)使用fs.chunks和fs.files來存儲(chǔ)文件。
其中fs.files集合存放文件的信息,fs.chunks存放文件數(shù)據(jù)。
一個(gè)fs.files集合中的一條記錄內(nèi)容如下,即一個(gè)file的信息如下:
{ "_id" : ObjectId("4f4608844f9b855c6c35e298"), //唯一id,可以是用戶自定義的類型"filename" : "CPU.txt", //文件名"length" : 778, //文件長度"chunkSize" : 262144, //chunk的大小"uploadDate" : ISODate("2012-02-23T09:36:04.593Z"), //上傳時(shí)間"md5" : "e2c789b036cfb3b848ae39a24e795ca6", //文件的md5值"contentType" : "text/plain" //文件的MIME類型"meta" : null //文件的其它信息,默認(rèn)是沒有”meta”這個(gè)key,用戶可以自己定義為任意BSON對(duì)象}對(duì)應(yīng)的fs.chunks中的chunk如下:
{ "_id" : ObjectId("4f4608844f9b855c6c35e299"), //chunk的id"files_id" : ObjectId("4f4608844f9b855c6c35e298"), //文件的id,對(duì)應(yīng)fs.files中的對(duì)象,相當(dāng)于fs.files集合的外鍵"n" : 0, //文件的第幾個(gè)chunk塊,如果文件大于chunksize的話,會(huì)被分割成多個(gè)chunk塊"data" : BinData(0,"QGV...") //文件的二進(jìn)制數(shù)據(jù),這里省略了具體內(nèi)容}文件存入到GridFS過程中,如果文件大于chunksize,則把文件分割成多個(gè)chunk,再把這些chunk保存到fs.chunks中,最后再把文件信息存入到fs.files中。
在讀取文件的時(shí)候,先據(jù)查詢的條件,在fs.files中找到一個(gè)合適的記錄,得到“_id”的值,再據(jù)這個(gè)值到fs.chunks中查找所有“files_id”為“_id”的chunk,并按“n”排序,最后依次讀取chunk中“data”對(duì)象的內(nèi)容,還原成原來的文件。
1.安裝mongoDb
增加MongoDB Repository,不清楚vim,請(qǐng)參考VIM
vim /etc/yum.repos.d/mongodb.repo
如果是64bit的
[mongodb]
name=MongoDB Repository
baseurl=http://downloads-distro.mongodb.org/repo/redhat/os/x86_64/
gpgcheck=0
enabled=1
32bit的系統(tǒng):
[mongodb]
name=MongoDB Repository
baseurl=http://downloads-distro.mongodb.org/repo/redhat/os/i686/
gpgcheck=0
enabled=1
然后安裝,會(huì)提示Y/N:
yum install mongo-10gen mongo-10gen-server
啟動(dòng):
service mongod start
查看狀態(tài)
service mongod status
停止
service mongod stop
更多,關(guān)于3.0以上版本,請(qǐng)參考官網(wǎng)。
2.安裝nginx及nginx-gridfs
依賴庫、工具
# yum -y install pcre-devel openssl-devel zlib-devel
# yum -y install gcc gcc-c++
下載nginx-gridfs源碼
# git clone https://github.com/mdirolf/nginx-gridfs.git
# cd nginx-gridfs
# git checkout v0.8
# git submodule init
# git submodule update
下載nginx源碼,編譯安裝。(高版本支持不好)
# wget http://nginx.org/download/nginx-1.4.7.tar.gz
# tar zxvf nginx-1.4.7.tar.gz
# cd nginx-1.4.7
# ./configure --with-openssl=/usr/include/openssl --add-module=../nginx-gridfs/
# make -j8 && make install –j8
注意藍(lán)色字符配置成對(duì)應(yīng)nginx-gridfs的路徑
3. 配置nginx-gridfs
vim /usr/local/nginx/conf/nginx.conf
在 server 節(jié)點(diǎn)中添加 location 節(jié)點(diǎn)
location /img/ {
gridfs testdb
field=filename
type=string;
mongo 192.168.0.159:27017;
}
location /files/ {
gridfs testdb
field=_id
type=objectid;
mongo 192.168.0.159:27017;
}
這里我們的mongo服務(wù)在ip 192.168.0.159。
如果不指定 field,默認(rèn)為 MongoDB 的自增ID,且type為int
配置參數(shù)介紹:
gridfs:nginx識(shí)別插件的關(guān)鍵字
testdb:db名
[root_collection]: 選擇collection,如root_collection=blog, mongod就會(huì)去找blog.files與blog.chunks兩個(gè)塊,默認(rèn)是fs
[field]: 查詢字段,保證mongdb里有這個(gè)字段名,支持_id, filename, 可省略, 默認(rèn)是_id
[type]: 解釋field的數(shù)據(jù)類型,支持objectid, int, string, 可省略, 默認(rèn)是int
[user]: 用戶名, 可省略
[pass]: 密碼, 可省略
mongo: mongodb url
# /usr/local/nginx/sbin/nginx
可能出現(xiàn):
Nginx [emerg]: bind() to 0.0.0.0:80 failed (98: Address already in use)
這時(shí)可用使用命令關(guān)閉占用80端口的程序
sudo fuser -k 80/tcp
用原生的命令行上傳一個(gè)文件
mongofiles put 937910.jpg --local ~/937910_100.jpg --host 192.168.0.159 --port 27017 --db testdb --type jpg
937910.jpg是我們提前下載好一個(gè)圖片文件,注意我們沒有指定collection,默認(rèn)是fs
從http://www.robomongo.org/安裝robomongo管理工具, 查看剛剛上傳的文件

最后我們?cè)跒g覽器訪問,如果看到圖片就OK了
http://192.168.0.159/img/937910.jpg
對(duì)于.net環(huán)境下mongodb CSharpDriver 1.10.0 從Nuget:
Install-Package mongocsharpdriver -Version 1.10.0
我們使用如下片段代碼:
int nFileLen = fileUploadModel.FileBytes.Length; MongoGridFSSettings fsSetting = new MongoGridFSSettings() { Root = CollectionName }; MongoGridFS fs = new MongoGridFS(mongoServer, MongoDatabaseName, fsSetting); //調(diào)用Write、WriteByte、WriteLine函數(shù)時(shí)需要手動(dòng)設(shè)置上傳時(shí)間 //通過Metadata 添加附加信息 MongoGridFSCreateOptions option = new MongoGridFSCreateOptions(); option.Id = ObjectId.GenerateNewId(); var currentDate = DateTime.Now; option.UploadDate = currentDate; option.Aliases = alias; BsonDocument doc = new BsonDocument(); //文檔附加信息存儲(chǔ) if(fileUploadModel.DocExtraInfo!=null&&fileUploadModel.DocExtraInfo.Count>0) { foreach(var obj in fileUploadModel.DocExtraInfo) { if (!doc.Elements.Any(p => p.Name == obj.Key)) { doc.Add(obj.Key, obj.Value); } } } option.Metadata = doc; //創(chuàng)建文件,文件并存儲(chǔ)數(shù)據(jù) using (MongoGridFSStream gfs = fs.Create(fileUploadModel.FileName, option)) { gfs.Write(fileUploadModel.FileBytes, 0, nFileLen); gfs.Close(); } log.ErrorFormat("附件標(biāo)識(shí):{0} 文件名:{1} 上傳成功", alias, fileUploadModel.FileName); return option.Id.ToString();注意,目前gridfs-ngnix不支持_id類型是GUID的,關(guān)于ObjectId參考官網(wǎng),如下圖:

mongodb產(chǎn)生objectid還有一個(gè)更大的優(yōu)勢(shì),就是mongodb可以通過自身的服務(wù)來產(chǎn)生objectid,也可以通過客戶端的驅(qū)動(dòng)程序來產(chǎn)生。
來自官方2.6.10版本 手冊(cè)內(nèi)容
For documents in a MongoDB collection, you should always use GridFS for storing files larger than 16 MB. In some situations, storing large files may be more efficient in a MongoDB database than on a system-level filesystem.
? If your filesystem limits the number of files in a directory, you can use GridFS to store as many files as needed.
? When you want to keep your files and metadata automatically synced and deployed across a number of systems and facilities. When using geographically distributed replica sets MongoDB can distribute files and their metadata automatically to a number of mongod instances and facilities.
? When you want to access information from portions of large files without having to load whole files into memory, you can use GridFS to recall sections of files without reading the entire file into memory.
Do not use GridFS if you need to update the content of the entire file atomically. As an alternative you can store multiple versions of each file and specify the current version of the file in the metadata. You can update the metadata field that indicates “l(fā)atest” status in an atomic update after uploading the new version of the file, and later remove PRevious versions if needed.
Furthermore, if your files are all smaller the 16 MB BSON Document Size limit, consider storing the file manually within a single document. You may use the BinData data type to store the binary data. See your drivers documentation for details on using BinData.
原理圖

上圖是MongoDB采用Replica Sets模式的同步流程


上面講了分片的標(biāo)準(zhǔn),下面是具體在分片時(shí)的幾種節(jié)點(diǎn)角色
MongoDB的32位版本也是不建議被使用的,因?yàn)槟阒荒芴幚?GB大小的數(shù)據(jù)。還記得第一個(gè)限制么?這是MongoDB關(guān)于該限制的說明。
讓我感到驚訝的是,很少有人會(huì)查詢關(guān)于他們將要使用的工具的限制。幸好,MongoDB的開發(fā)人員發(fā)布了一篇MongoDB所有限制的博客,你可以提前了解相關(guān)信息,避免在使用過程中難堪。
盡管已經(jīng)不建議被使用了,不過MongoDB還是提供了另外一種復(fù)制策略,即主從復(fù)制。它解決了12個(gè)節(jié)點(diǎn)限制問題,不過卻產(chǎn)生了新的問題:如果需要改變集群的主節(jié)點(diǎn),那么你必須得手工完成,感到驚訝?看看這個(gè)鏈接吧。
MongoDB中數(shù)據(jù)復(fù)制的復(fù)制集策略非常棒,很容易配置并且使用起來確實(shí)不錯(cuò)。但如果集群的節(jié)點(diǎn)有12個(gè)以上,那么你就會(huì)遇到問題。MongoDB中的復(fù)制集有12個(gè)節(jié)點(diǎn)的限制,這里是問題的描述,你可以追蹤這個(gè)問題看看是否已經(jīng)被解決了。
Gridfs最適合大文件存儲(chǔ) ,特別是視頻,音頻,大型圖片超過16MB大小的文件。小型文件也可以存儲(chǔ),不過需要付出2次查詢代價(jià)(metadata與file content) [Tip#18 50 Tips and Tricks for MongoDB Developers]。不要修改存儲(chǔ)文件的內(nèi)容,而是更新文件元數(shù)據(jù)如版本,或上傳新版本的文件,刪除老版本的文件。對(duì)于大量文件存儲(chǔ)時(shí),需要多個(gè)數(shù)據(jù)節(jié)點(diǎn),復(fù)制,數(shù)據(jù)分片等。別基于nginx訪問圖片文件,瀏覽器沒有緩存。 從互聯(lián)網(wǎng)存儲(chǔ)圖片案例來看,圖片大都是jpg, png與縮略圖文件,分存式文件系統(tǒng)(DFS)會(huì)是更好的解決方案。
GridFS官方
Building MongoDB applications with Binary Files Using GridFS
如有想了解更多軟件,系統(tǒng) IT,企業(yè)信息化 資訊,請(qǐng)關(guān)注我的微信訂閱號(hào):
![MegadotnetMicroMsg_thumb1_thumb1_thu[1] MegadotnetMicroMsg_thumb1_thumb1_thu[1]](http://s1.VeVb.com/20151016/f0w0b0puyvq51.jpg)
作者:Petter Liu
出處:http://m.survivalescaperooms.com/wintersun/
本文版權(quán)歸作者和博客園共有,歡迎轉(zhuǎn)載,但未經(jīng)作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接,否則保留追究法律責(zé)任的權(quán)利。
該文章也同時(shí)發(fā)布在我的獨(dú)立博客中-Petter Liu Blog。
新聞熱點(diǎn)
疑難解答
圖片精選
網(wǎng)友關(guān)注