分享NO-SQL開發實戰


最近研究了一下NOSQL,現整理目錄如下:

一、關系數據庫的瓶頸;

二、NOSQL概述;

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

四、MongoDB開發模式及實戰;

一、關系數據庫的瓶頸

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

數據庫使用比例

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

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

1、Cache+SQL;

為了提高網站的性能,我們經常會把一些讀取訪問頻率比較高,更新頻率低的數據存儲在內存中,一方面可以提高用戶的體驗,另外一方面可以減輕數據庫的訪問壓力;

2 、讀寫分離;

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

3、分表分庫;

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

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

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

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

二、NOSQL概述

1、什么是NOSQL?

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

     NoSQL被我們用得最多的當數key-value存儲,當然還有其他的文檔型的、列存儲、圖型數據庫、xml數據庫等,見圖2。在NoSQL概念提出之前,這些數據庫就被用於各種系統當中,但是卻很少用於web互聯網應用。

非關系數據庫種類

圖2:非關系數據庫種類

2、NOSQL的發展狀況如何?

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

Nosql發展趨勢

圖3:NOSQL發展趨勢

3、NOSQL和關系數據庫的關系?

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

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

1、概念

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

 3、功能

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

第一步:下載安裝包:官方下載地址←單擊此處,如果是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命令框的煩惱;

四、MongoDB開發模式及實戰

1、開發模式

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

然后在領域層采用小三層架構模式調用基礎構件層的數據服務,展現層在通過AJAX+JSON+Filter方式通過服務的形式調用業務層,展現層就可以很好的利用返回的JSON串實現頁面的功能。

圖5:開發模式

2、開發實戰

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;
        }
    }

 


 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM