【Mongodb教程 第十六課 】 分享NO-SQL開發實戰

最近研究了一下NOSQL,現整理目錄以下:程序員

1、關係數據庫的瓶頸;web

2、NOSQL概述;sql

3、NOSQL中的熱門數據庫MongoDB介紹及安裝配置;mongodb

4、MongoDB開發模式及實戰;數據庫

1、關係數據庫的瓶頸

      從90年代到至今,關係數據庫扮演了最重要的角色,它的性能,可擴展性、穩定性、數據的備份和恢復機制等都很是好,關係數據庫發展到如今已經很是成熟,它提供給使用者的是一整套體系,包括數據存儲、數據備份恢復、數據加解密、應用開發驅動、圖形化配置維護工具、安全策略等等。圖1中展現了世界上各類數據庫的使用比例,從這個圖上咱們明顯看得出它的霸主地位。windows

數據庫使用比例

                  圖1:各類數據庫的使用比例數組

不過隨着信息技術的快速發展,Web發展也從Web1.0發展到了Web2.0時代,網站開始快速發展,博客、電子商務、微博、社區等Web應用已經引領了時代潮流,它們的網絡流量很是巨大,爲了解決這個問題,不少IT公司都採起了一系列優化措施,主要優化措施以下:緩存

一、Cache+SQL;安全

爲了提升網站的性能,咱們常常會把一些讀取訪問頻率比較高,更新頻率低的數據存儲在內存中,一方面能夠提升用戶的體驗,另一方面能夠減輕數據庫的訪問壓力;服務器

2 、讀寫分離;

還有一種好的方式就是讀寫分離,例如咱們能夠把內網應用系統產生的數據對稱的發佈到互聯網的數據庫中,這樣互聯網應用的訪問都是從外網數據庫中讀取,內網數據庫大部分都是增、刪、改等操做,這樣也能大幅度提升應用的性能;

三、分表分庫;

      隨着Web2.0的高速發展,在Cache+SQL、數據庫主從複製讀寫分離的優化的情形下,關係數據庫主庫的寫壓力出現瓶頸,數據量的持續猛增,訪問的高併發狀況之下,關係數據庫會出現嚴重的鎖問題,這時開始流行分表分庫的方式來緩解寫壓力和數據增加的擴展問題,很早以前我作的一個應用系統,就出現了這個需求,隨着數據量的沉澱,數據庫變得很是龐大,數據庫和日誌文件達到了10幾個G,有些表裏面有上千萬條數據,用戶在使用過程當中,進行操做時常常會卡住,有時候一等就是幾秒或幾十秒,客戶很是不滿意,後來咱們討論以後就採起了數據庫方面,一年一個庫進行分庫,某些數據量大的表採用拆分,例如一個月產生一個表,還有把一個表中的字段拆分到多個表中等;

      經過以上優化咱們系統的性能會提升很大一塊,每秒查詢率方面能夠達到:幾百qps到幾千qps不等,數據庫大小能夠達到1T左右,不過隨着訪問量和數據量的加大,關係數據庫很難繼續高效率的擔當,採用分表分庫能夠在必定程度上下降這個瓶頸,不過它下降了應用的可擴展性,帶來了巨大的技術和開發成本,例如一個需求的變動,可能就會致使一種新的分庫分表方式。

      關係數據庫中基本上都會存儲一些大文本和附件信息,致使數據庫很是的大,在作數據庫恢復的時候就會很是的慢,例如1000萬3KB的大文本就接近30G的大小、100萬200K的附件就是200G,若是能把這些大文本和大附件從關係數據庫中省去,咱們的關係數據庫將會變得很小從而很容易優化。

      綜上,關係數據庫很強大,可是它並不能很好的應付全部的應用場景。關係數據庫的擴展性差(須要複雜的技術來實現),大數據下IO壓力大,表結構更改困難,正是當前使用關係數據庫的開發人員面臨的問題。

2、NOSQL概述

一、什麼是NOSQL?

     隨着web2.0的快速發展,非關係型、分佈式數據存儲獲得了快速的發展,它們不保證關係數據的ACID特性。NoSQL概念在2009年被提了出來。NoSQL最多見的解釋是「non-relational」,「Not Only SQL」也被不少人接受。(「NoSQL」一詞最先於1998年被用於一個輕量級的關係數據庫的名字。)

     NoSQL被咱們用得最多的當數key-value存儲,固然還有其餘的文檔型的、列存儲、圖型數據庫、xml數據庫等,見圖2。在NoSQL概念提出以前,這些數據庫就被用於各類系統當中,可是卻不多用於web互聯網應用。

非關係數據庫種類

圖2:非關係數據庫種類

二、NOSQL的發展情況如何?

目前NOSQL至關火爆,微博、電子商務、博客、社區、論壇等大數據量高併發的互聯網應用中基本都用到了它,大的IT巨頭們都在各自的互聯網架構中加入了NOSQL解決方案,甚至擁有本身的NOSQL產品,各類NOSQL產品百花齊放,如圖3,在2010年以後NOSQL達到井噴之勢,其中mongoDb發展勢頭最猛也最火熱。

Nosql發展趨勢

圖3:NOSQL發展趨勢

三、NOSQL和關係數據庫的關係?

我以爲關係數據庫和NOSQL是一種相輔相成緊密結合的關係,咱們須要根據具體的應用場景來選擇對應數據庫,若是你的應用的數據量很小,那麼關係數據庫就足夠了,並且性能、效率、穩定性、安全都是有保證的;若是你的應用場景中涉及超大的數據量(包含大文本、多附件),例如量級在幾百G或T級,那麼能夠考慮用關係數據庫和NOSQL結合的方式來解決,關係數據庫存儲相應的關係數據,NOSQL數據庫存儲像大文本、對象、附件等類型數據,這樣是一種最優的解決方案;

3、NOSQL中MongoDB介紹及安裝配置

一、概念

     MongoDB是一個高性能,開源,無模式的文檔型數據庫,是當前NoSql數據庫中比較熱門的一種。它在許多場景下可用於替代傳統的關係型數據庫或鍵/值存儲方式。Mongo使用C++開發,理解方面可參考圖3:
圖3:mongDB內部組成
     Mongo的官方網站地址是: http://www.mongodb.org /
     這裏給你們推薦一本MongoDB入門的書籍《MongoDB權威指南》,這個有中文版本。
二、特性
 
    面向集合存儲,易存儲對象類型的數據。
    模式自由。
    支持動態查詢。
    支持徹底索引,包含內部對象。
    支持查詢。
    支持複製和故障恢復。
    使用高效的二進制數據存儲,包括大型對象(如視頻等)。
    自動處理碎片,以支持雲計算層次的擴展性 .
    支持Python,PHP,Java,C#,Javascript等語言的驅動程序.
    文件存儲格式爲BSON(一種JSON的擴展)。
    可經過網絡訪問。

 三、功能

面向集合的存儲:適合存儲對象及JSON形式的數據。
動態查詢:Mongo支持豐富的查詢表達式。查詢指令使用JSON形式的標記,可輕易查詢文檔中內嵌的對象及數組。
完整的索引支持:包括文檔內嵌對象及數組。Mongo的查詢優化器會分析查詢表達式,並生成一個高效的查詢計劃。
查詢監視:Mongo包含一個監視工具用於分析數據庫操做的性能。
複製及自動故障轉移:Mongo數據庫支持服務器之間的數據複製,支持主-從模式及服務器之間的相互複製。複製的主要目標是提供冗餘及自動故障轉移。
高效的傳統存儲方式:支持二進制數據及大型對象(如照片或圖片)
自動分片以支持雲級別的伸縮性:自動分片功能支持水平的數據庫集羣,可動態添加額外的機器
四、使用場景
 
網站數據:Mongo很是適合實時的插入,更新與查詢,並具有網站實時數據存儲所需的複製及高度伸縮性。
緩存:因爲性能很高,Mongo也適合做爲信息基礎設施的緩存層。在系統重啓以後,由Mongo搭建的持久化緩存層能夠避免下層的數據源 過載。
大尺寸,低價值的數據:使用傳統的關係型數據庫存儲一些數據時可能會比較昂貴,在此以前,不少時候程序員每每會選擇傳統的文件進行存儲。
高伸縮性的場景:Mongo很是適合由數十或數百臺服務器組成的數據庫。Mongo的路線圖中已經包含對MapReduce引擎的內置支持。
用於對象及JSON數據的存儲:Mongo的BSON數據格式很是適合文檔化格式的存儲及查詢。
五、安裝過程
 

第一步:下載安裝包:官方下載地址←單擊此處,若是是win系統,注意是64位仍是32位版本的,請選擇正確的版本。

第二步:新建目錄「D:\MongoDB」,解壓下載到的安裝包,找到bin目錄下面所有.exe文件,拷貝到剛建立的目錄下。

第三步:在「D:\MongoDB」目錄下新建「data」文件夾,它將會做爲數據存放的根文件夾。

  配置Mongo服務端:

  打開CMD窗口,按照以下方式輸入命令:
  > d:
  > cd D:\MongoDB
  > mongod --dbpath D:\MongoDB\data

  配置成功後會看到以下圖4:

圖4:啓動成功界面

第四步:安裝爲windows服務

mongod --install --serviceName MongoDB --serviceDisplayName MongoDB --logpath D:\MongoDB\log\MongoDB.log --dbpath D:\MongoDB\data --directoryperdb,執行成功以後在windows服務中能夠看到名稱爲MongoDB的服務,開啓就能夠了,這樣能避免exe CMD命令框的煩惱;

4、MongoDB開發模式及實戰

一、開發模式

對於MongoDB的開發模式,咱們能夠採用相似高速服務框架HSF的模式進行架構,見圖5,首先在基礎構件層中咱們把MongoDB的驅動封裝到基類庫Inspur.Finix.DAL中,

而後在領域層採用小三層架構模式調用基礎構件層的數據服務,展示層在經過AJAX+JSON+Filter方式經過服務的形式調用業務層,展示層就能夠很好的利用返回的JSON串實現頁面的功能。

圖5:開發模式

二、開發實戰

C#驅動有不少種比較經常使用的是官方驅動和samus驅動。samus驅動除了支持通常形式的操做以外,還支持linq方式操縱數據

(1)基礎構件層封裝咱們採用samus驅動進行封裝,代碼以下:

 

複製代碼
 public class MongoDBAccess : IDisposable
    {
        /// <summary>
        /// 數據庫別名
        /// </summary>
        private string _dataBaseAlias = "Noah.MongoDB";
        /// <summary>
        /// 集合名
        /// </summary>
        public string _collectionName { get; set; }
        // 定義mongo服務
        private Mongo _mongo = null;
        // 獲取databaseName對應的數據庫,不存在則自動建立
        private IMongoDatabase _mongoDatabase = null;
        public MongoCollection<Document> MongoCollection;
        /// <summary>
        /// 構造函數
        /// </summary>
        /// <param name="dataBaseAlias"></param>
        /// <param name="collectionName"></param>
        public MongoDBAccess(string dataBaseAlias, string collectionName)
        {
            _dataBaseAlias = dataBaseAlias;
            _collectionName = collectionName;
            init();
        }
        /// <summary>
        /// 初始化
        /// </summary>
        private void init()
        {
            DatabaseConfigManager dcm = DatabaseConfigManager.Create();
            // 根據別名獲得鏈接串
            string connStr = dcm.GetPrimaryConnection(_dataBaseAlias);
            // 把conn進行拆分
            StringTokenizer st = new StringTokenizer(connStr, ";");
            string conn = st.GetValueByIndex(0);
            // 定義mongo服務
            _mongo = new Mongo(conn);
            _mongo.Connect();
            st = new StringTokenizer(st.GetValueByIndex(1), "=");
            string databaseName = st.GetValueByIndex(1);
            // 獲取databaseName對應的數據庫,不存在則自動建立
            if (string.IsNullOrEmpty(databaseName) == false)
                _mongoDatabase = _mongo.GetDatabase(databaseName);
            //獲取collectionName對應的集合,不存在則自動建立
            MongoCollection = _mongoDatabase.GetCollection<Document>(_collectionName) as MongoCollection<Document>;

        }

        /// <summary>
        /// 切換到指定的數據庫
        /// </summary>
        /// <param name="dbName"></param>
        /// <returns></returns>
        public IMongoDatabase UseDb(string dbName)
        {
            if (string.IsNullOrEmpty(dbName))
                throw new ArgumentNullException("dbName");

            _mongoDatabase = _mongo.GetDatabase(dbName);
            return _mongoDatabase;
        }

        /// <summary>
        /// 獲取當前鏈接的數據庫
        /// </summary>
        public IMongoDatabase CurrentDb
        {
            get
            {
                if (_mongoDatabase == null)
                    throw new Exception("當前鏈接沒有指定任何數據庫。請在構造函數中指定數據庫名或者調用UseDb()方法切換數據庫。");

                return _mongoDatabase;
            }
        }

        /// <summary>
        /// 獲取當前鏈接數據庫的指定集合【依據類型】
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        public IMongoCollection<T> GetCollection<T>() where T : class
        {
            return this.CurrentDb.GetCollection<T>();
        }

        /// <summary>
        /// 獲取當前鏈接數據庫的指定集合【根據指定名稱】
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="name">集合名稱</param>
        /// <returns></returns>
        public IMongoCollection<T> GetCollection<T>(string name) where T : class
        {
            return this.CurrentDb.GetCollection<T>(name);
        }
        /// <summary>
        /// 使用GridFS保存附件
        /// </summary>
        /// <param name="byteFile"></param>
        /// <returns></returns>
        public string GridFsSave(byte[] byteFile)
        {
            string filename = Guid.NewGuid().ToString();
            //這裏GridFile構造函數有個重載,bucket參數就是用來替換那個建立集合名中默認的"fs"的。    
            GridFile gridFile = new GridFile(_mongoDatabase);
            using (GridFileStream gridFileStream = gridFile.Create(filename))
            {
                gridFileStream.Write(byteFile, 0, byteFile.Length);
            }
            return filename;
        }
        /// <summary>
        /// 讀取GridFs附件
        /// </summary>
        /// <param name="filename"></param>
        /// <returns></returns>
        public byte[] GridFsRead(string filename)
        {
            GridFile gridFile = new GridFile(_mongoDatabase);
            byte[] bytes;
            using (GridFileStream gridFileStream = gridFile.OpenRead(filename))
            {
                bytes = new byte[gridFileStream.Length];
                gridFileStream.Read(bytes, 0, bytes.Length);
            }
            return bytes;
        }
        public void GridFsDelete(string filename)
        {
            GridFile gridFile = new GridFile(_mongoDatabase);
            gridFile.Delete(new Document("filename", filename));
        }
        /// <summary>
        /// 資源釋放
        /// </summary>
        public void Dispose()
        {
            if (_mongo != null)
            {
                _mongo.Dispose();
                _mongo = null;
            }

        }
    }
複製代碼

(2)領域層部分代碼

複製代碼
public class KNOWLEDGE_SOCKDAL
    {
        public KNOWLEDGE_SOCKDAL()
        {
        }
        /// <summary>
        /// 保存一個對象
        /// </summary>
        /// <param name="model"></param>
        public void Add(KNOWLEDGE_SOCK model)
        {
            try
            {
                using (MongoDBAccess mm = new MongoDBAccess(cConfig.Noah_MongoDB, ""))
                {
                    mm.GetCollection<KNOWLEDGE_SOCK>().Insert(model);
                }
            }
            catch (Exception ex)
            {
                ExceptionManager.Handle(ex);
            }
        }
        /// <summary>
        /// 保存附件
        /// </summary>
        /// <param name="file"></param>
        /// <returns></returns>
        public string  SaveAttach(byte[] file)
        {
            string fileName = string.Empty;
            try
            {
                using (MongoDBAccess mm = new MongoDBAccess(cConfig.Noah_MongoDB, ""))
                {
                    fileName = mm.GridFsSave(file);
                }
            }
            catch (Exception ex)
            {
                ExceptionManager.Handle(ex);
            }
            return fileName;
        }
        /// <summary>
        /// 讀取附件
        /// </summary>
        /// <param name="fileName"></param>
        /// <returns></returns>
        public byte[] ReadAttach(string fileName)
        {
            try
            {
                using (MongoDBAccess mm = new MongoDBAccess(cConfig.Noah_MongoDB, ""))
                {
                    mm.GetCollection<KNOWLEDGE_SOCK>();
                    return  mm.GridFsRead(fileName);
                }
            }
            catch (Exception ex)
            {
                ExceptionManager.Handle(ex);
            }
            return null;
        }
        /// <summary>
        /// 刪除附件
        /// </summary>
        /// <param name="fileName"></param>
        public void DeleteAttach(string fileName)
        {
            try
            {
                using (MongoDBAccess mm = new MongoDBAccess(cConfig.Noah_MongoDB, ""))
                {
                    mm.GetCollection<KNOWLEDGE_SOCK>();
                    mm.GridFsDelete(fileName);
                }
            }
            catch (Exception ex)
            {
                ExceptionManager.Handle(ex);
            }
        }
        /// <summary>
        /// 更新
        /// </summary>
        /// <param name="model"></param>
        public void Update(KNOWLEDGE_SOCK model)
        {
            try
            {
                using (MongoDBAccess mm = new MongoDBAccess(cConfig.Noah_MongoDB, ""))
                {
                    var query = new Document("Know_Code", model.Know_Code);
                    mm.GetCollection<KNOWLEDGE_SOCK>().Update(model, query);
                }
            }
            catch (Exception ex)
            {
                ExceptionManager.Handle(ex);
            }
        }
        /// <summary>
        /// 刪除
        /// </summary>
        /// <param name="id"></param>
        public void Delete(string id)
        {
            try
            {
                using (MongoDBAccess mm = new MongoDBAccess(cConfig.Noah_MongoDB, ""))
                {
                    var query = new Document("Know_Code", id);
                    mm.GetCollection<KNOWLEDGE_SOCK>().Remove(query);
                }
            }
            catch (Exception ex)
            {
                ExceptionManager.Handle(ex);
            }
        }
        /// <summary>
        /// 查詢特定一條
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public KNOWLEDGE_SOCK FindOne(string id)
        {
            KNOWLEDGE_SOCK catalog = new KNOWLEDGE_SOCK();
            try
            {
                using (MongoDBAccess mm = new MongoDBAccess(cConfig.Noah_MongoDB, ""))
                {
                    var query = new Document("Know_Code", id);
                    catalog = mm.GetCollection<KNOWLEDGE_SOCK>().FindOne(query);
                }
            }
            catch (Exception ex)
            {
                ExceptionManager.Handle(ex);
            }
            return catalog;
        }
        /// <summary>
        /// 根據條件查詢
        /// </summary>
        /// <param name="js"></param>
        /// <returns></returns>
        public List<KNOWLEDGE_SOCK> Find(string js)
        {
            List<KNOWLEDGE_SOCK> catalogs = new List<KNOWLEDGE_SOCK>();
            try
            {
                using (MongoDBAccess mm = new MongoDBAccess(cConfig.Noah_MongoDB, ""))
                {
                    string jsStr = @" 
                    function(){ 
                        return " + js + ";}";
                    catalogs = mm.GetCollection<KNOWLEDGE_SOCK>().Find(Op.Where(jsStr)).Documents.ToList();
                }
            }
            catch (Exception ex)
            {
                ExceptionManager.Handle(ex);
            }
            return catalogs;
        }
        /// <summary>
        /// 查詢所有
        /// </summary>
        /// <returns></returns>
        public List<KNOWLEDGE_SOCK> FindAll()
        {
            List<KNOWLEDGE_SOCK> catalogs = new List<KNOWLEDGE_SOCK>();
            try
            {
                using (MongoDBAccess mm = new MongoDBAccess(cConfig.Noah_MongoDB, ""))
                {
                    catalogs = mm.GetCollection<KNOWLEDGE_SOCK>().FindAll().Documents.ToList();
                }
            }
            catch (Exception ex)
            {
                ExceptionManager.Handle(ex);
            }
            return catalogs;
        }
        /// <summary>
        /// 返回數量
        /// </summary>
        /// <param name="js"></param>
        /// <returns></returns>
        public int GetCount(string js)
        {
            int count = 0;
            try
            {
                using (MongoDBAccess mm = new MongoDBAccess(cConfig.Noah_MongoDB, ""))
                {
                    string jsStr = @" 
                    function(){ 
                        return " + js + ";}";
                    count = mm.GetCollection<KNOWLEDGE_SOCK>().Find(Op.Where(jsStr)).Documents.Count();
                }

            }
            catch (Exception ex)
            {
                ExceptionManager.Handle(ex);
            }
            return count;
        }
        public List<KNOWLEDGE_SOCK> Find(string js, int pageSize, int pageIndex)
        {
            List<KNOWLEDGE_SOCK> list = new List<KNOWLEDGE_SOCK>();
            try
            {
                using (MongoDBAccess mm = new MongoDBAccess(cConfig.Noah_MongoDB, ""))
                {
                    string jsStr = @" 
                    function(){ 
                        return " + js + ";}";
                    list = mm.GetCollection<KNOWLEDGE_SOCK>().Find(Op.Where(jsStr)).Documents.OrderBy(x=>x.Know_CreateTime).Skip(pageSize * (pageIndex-1)).Take(pageSize).ToList();
                }
            }
            catch (Exception ex)
            {
                ExceptionManager.Handle(ex);
            }
            return list;
        }
    }
複製代碼
相關文章
相關標籤/搜索