標籤:blog http io os ar 使用 java strong sp
最近研究了一下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發展勢頭最猛也最火熱。
圖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; } }
【Mongodb教程 第十六課 】 分享NO-SQL開發實戰