MongoDb gridfs-ngnix文件存儲方案

      在各種系統應用服務端開發中,咱們常常會遇到文件存儲的問題。 常見的磁盤文件系統,DBMS傳統文件流存儲。今天咱們看一下基於NoSQL數據庫MongoDb的存儲方案。筆者環境 以CentOS 6.5,MongoDb 2.6.3,  Nginx-1.4.7 爲例,您須要瞭解Linux經常使用命令。
先來回顧一下MongoDb的內部文件結構javascript

O6EzR

  1. MongoDB在數據存儲上按命名空間來劃分,一個collection是一個命名空間,一個索引也是一個命名空間
  2. 同一個命名空間的數據被分紅不少個Extent,Extent之間使用雙向鏈表鏈接
  3. 在每個Extent中,保存了具體每一行的數據,這些數據也是經過雙向連接鏈接的
  4. 每一行數據存儲空間不只包括數據佔用空間,還可能包含一部分附加空間,這使得在數據update變大後能夠不移動位置
  5. 索引以BTree結構實現

而後是GridFs的結構html

gridfs1

GridFS在數據庫中,默認使用fs.chunks和fs.files來存儲文件。java

其中fs.files集合存放文件的信息,fs.chunks存放文件數據。linux

一個fs.files集合中的一條記錄內容以下,即一個file的信息以下:nginx

{ 
"_id" : ObjectId("4f4608844f9b855c6c35e298"),       //惟一id,能夠是用戶自定義的類型
"filename" : "CPU.txt",      //文件名
"length" : 778,      //文件長度
"chunkSize" : 262144,    //chunk的大小
"uploadDate" : ISODate("2012-02-23T09:36:04.593Z"), //上傳時間
"md5" : "e2c789b036cfb3b848ae39a24e795ca6",      //文件的md5值
"contentType" : "text/plain"     //文件的MIME類型
"meta" : null    //文件的其它信息,默認是沒有」meta」這個key,用戶能夠本身定義爲任意BSON對象
}

對應的fs.chunks中的chunk以下:c++

{ 
"_id" : ObjectId("4f4608844f9b855c6c35e299"),    //chunk的id
"files_id" : ObjectId("4f4608844f9b855c6c35e298"),  //文件的id,對應fs.files中的對象,至關於fs.files集合的外鍵
"n" : 0,     //文件的第幾個chunk塊,若是文件大於chunksize的話,會被分割成多個chunk塊
"data" : BinData(0,"QGV...")     //文件的二進制數據,這裏省略了具體內容
}

文件存入到GridFS過程當中,若是文件大於chunksize,則把文件分割成多個chunk,再把這些chunk保存到fs.chunks中,最後再把文件信息存入到fs.files中。git

在讀取文件的時候,先據查詢的條件,在fs.files中找到一個合適的記錄,獲得「_id」的值,再據這個值到fs.chunks中查找全部「files_id」爲「_id」的chunk,並按「n」排序,最後依次讀取chunk中「data」對象的內容,還原成原來的文件。
github

安裝Install與配置

1.安裝mongoDbmongodb

增長MongoDB Repository,不清楚vim,請參考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的系統:

[mongodb]

name=MongoDB Repository

baseurl=http://downloads-distro.mongodb.org/repo/redhat/os/i686/

gpgcheck=0

enabled=1

而後安裝,會提示Y/N:

yum install mongo-10gen mongo-10gen-server

啓動:

service mongod start

查看狀態

service mongod status

中止

service mongod stop

更多,關於3.0以上版本,請參考官網。

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

注意藍色字符配置成對應nginx-gridfs的路徑

3. 配置nginx-gridfs

vim /usr/local/nginx/conf/nginx.conf

在 server 節點中添加 location 節點

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服務在IP 192.168.0.159。
若是不指定 field,默認爲 MongoDB 的自增ID,且type爲int

配置參數介紹:

gridfs:nginx識別插件的關鍵字
testdb:db名
[root_collection]: 選擇collection,如root_collection=blog, mongod就會去找blog.files與blog.chunks兩個塊,默認是fs
[field]: 查詢字段,保證mongdb裏有這個字段名,支持_id, filename, 可省略, 默認是_id
[type]: 解釋field的數據類型,支持objectid, int, string, 可省略, 默認是int
[user]: 用戶名, 可省略
[pass]: 密碼, 可省略
mongo: mongodb url

啓動nginx服務

# /usr/local/nginx/sbin/nginx

可能出現:
Nginx [emerg]: bind() to 0.0.0.0:80 failed (98: Address already in use)

這時可用使用命令關閉佔用80端口的程序

sudo fuser -k 80/tcp


簡單測試

用原生的命令行上傳一個文件

mongofiles put 937910.jpg --local ~/937910_100.jpg --host 192.168.0.159 --port 27017 --db testdb --type jpg

937910.jpg是咱們提早下載好一個圖片文件,注意咱們沒有指定collection,默認是fs

http://www.robomongo.org/安裝robomongo管理工具, 查看剛剛上傳的文件

image

最後咱們在瀏覽器訪問,若是看到圖片就OK了

http://192.168.0.159/img/937910.jpg

對於.net環境下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);

                //調用Write、WriteByte、WriteLine函數時須要手動設置上傳時間
                //經過Metadata 添加附加信息
                MongoGridFSCreateOptions option = new MongoGridFSCreateOptions();
                option.Id = ObjectId.GenerateNewId();
                var currentDate = DateTime.Now;
                option.UploadDate = currentDate;
                option.Aliases = alias;
                BsonDocument doc = new BsonDocument();
                //文檔附加信息存儲
                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;

                //建立文件,文件並存儲數據
                using (MongoGridFSStream gfs = fs.Create(fileUploadModel.FileName, option))
                {
                    gfs.Write(fileUploadModel.FileBytes, 0, nFileLen);
                    gfs.Close();
                }
                log.ErrorFormat("附件標識:{0} 文件名:{1} 上傳成功", alias, fileUploadModel.FileName);
                return option.Id.ToString();

注意,目前gridfs-ngnix不支持_id類型是GUID的,關於ObjectId參考官網,以下圖:

mongodb-objectid

mongodb產生objectid還有一個更大的優點,就是mongodb能夠經過自身的服務來產生objectid,也能夠經過客戶端的驅動程序來產生。

何時使用Gridfs

來自官方2.6.10版本 手冊內容

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 「latest」 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.

數據庫主從同步

原理圖

mongodbre

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

  • 紅色箭頭表示寫操做寫到Primary上,而後異步同步到多個Secondary上
  • 藍色箭頭表示讀操做能夠從Primary或Secondary任意一個上讀
  • 各個Primary與Secondary之間一直保持心跳同步檢測,用於判斷Replica Sets的狀態

數據分片機制

wlqvf

  • MongoDB的分片是指定一個分片key來進行,數據按範圍分紅不一樣的chunk,每一個chunk的大小有限制
  • 有多個分片節點保存這些chunk,每一個節點保存一部分的chunk
  • 每個分片節點都是一個Replica Sets,這樣保證數據的安全性
  • 當一個chunk超過其限制的最大致積時,會分裂成兩個小的chunk
  • 當chunk在分片節點中分佈不均衡時,會引起chunk遷移操做

分片時服務器角色

RArrX

上面講了分片的標準,下面是具體在分片時的幾種節點角色

  • 客戶端訪問路由節點mongos來進行數據讀寫
  • config服務器保存了兩個映射關係,一個是key值的區間對應哪個chunk的映射關係,另外一個是chunk存在哪個分片節點的映射關係
  • 路由節點經過config服務器獲取數據信息,經過這些信息,找到真正存放數據的分片節點進行對應操做
  • 路由節點還會在寫操做時判斷當前chunk是否超出限定大小,若是超出,就分列成兩個chunk
  • 對於按分片key進行的查詢和update操做來講,路由節點會查到具體的chunk而後再進行相關的工做
  • 對於不按分片key進行的查詢和update操做來講,mongos會對全部下屬節點發送請求而後再對返回結果進行合併

其它關於mongodb 的一些小提示:

  • 不要使用32位版本

MongoDB的32位版本也是不建議被使用的,由於你只能處理2GB大小的數據。還記得第一個限制麼?這是MongoDB關於該限制的說明

  • 瞭解官方的限制

讓我感到驚訝的是,不多有人會查詢關於他們將要使用的工具的限制。幸虧,MongoDB的開發人員發佈了一篇MongoDB全部限制的博客,你能夠提早了解相關信息,避免在使用過程當中難堪。

  • 主從複製不會確保高可用性

儘管已經不建議被使用了,不過MongoDB仍是提供了另一種複製策略,即主從複製。它解決了12個節點限制問題,不過卻產生了新的問題:若是須要改變集羣的主節點,那麼你必須得手工完成,感到驚訝?看看這個連接吧。

  • 經過複製集實現的數據複製效果很是棒,不過也有限制

MongoDB中數據複製的複製集策略很是棒,很容易配置而且使用起來確實不錯。但若是集羣的節點有12個以上,那麼你就會遇到問題。MongoDB中的複製集有12個節點的限制,這裏是問題的描述,你能夠追蹤這個問題看看是否已經被解決了。

結論

        Gridfs最適合大文件存儲 ,特別是視頻,音頻,大型圖片超過16MB大小的文件。小型文件也能夠存儲,不過須要付出2次查詢代價(metadata與file content) [Tip#18 50 Tips and Tricks for MongoDB Developers]。不要修改存儲文件的內容,而是更新文件元數據如版本,或上傳新版本的文件,刪除老版本的文件。對於大量文件存儲時,須要多個數據節點,複製,數據分片等。別基於nginx訪問圖片文件,瀏覽器沒有緩存。 從互聯網存儲圖片案例來看,圖片大都是jpg, png與縮略圖文件,分存式文件系統(DFS)會是更好的解決方案。

資源:

GridFS官方
Building MongoDB Applications with Binary Files Using GridFS



但願對您軟件開發有幫助。 其它您可能感興趣的文章:
能力素質模型諮詢工具(Part1)
企業應用之性能實時度量系統演變
雲計算參考架構幾例
智能移動導遊解決方案簡介
人力資源管理系統的演化

若有想了解更多軟件,系統 IT,企業信息化 資訊,請關注個人微信訂閱號:

MegadotnetMicroMsg_thumb1_thumb1_thu[1]


做者:Petter Liu
出處:http://www.cnblogs.com/wintersun/
本文版權歸做者和博客園共有,歡迎轉載,但未經做者贊成必須保留此段聲明,且在文章頁面明顯位置給出原文鏈接,不然保留追究法律責任的權利。
該文章也同時發佈在個人獨立博客中-Petter Liu Blog

相關文章
相關標籤/搜索