前言算法
對於數據層的全部操做而言,查詢是最經常使用的,以前的文章中只開有Find的查詢接口,接口以下:數據庫
public DbResult Find(Dictionary<string, object> query);
因爲只開放了一個Find接口,所以在業務開發過程中,會出現以下缺點:緩存
一、業務靠多個表數據組合展現的時候,須要業務開發人員屢次使用Find方法查找不一樣的表來組合數據,僞代碼以下:數據結構
var orders = orderDb.FindByUserNo("no001"); var orderProdutcs = orderProductDb.FindByOrderIds( orders.Select(o => o.Id).ToArray()); //組合數據
二、一些類似的業務須要重複編寫代碼(其實只是屢次相同的Find,可是結果數據可能不一樣),例如:pc端須要顯示訂單中5個產品的基礎信息,而移動端僅僅只顯示圖片而已優化
三、開發人員須要關注多表間的關係(其實使用Orm自動映射仍然要關注這些關係)this
接下來對Find進行擴展,雖然上面的問題沒法獲得解決,可是好處卻更多。搜索引擎
普通查詢spa
對於某一個Find而言,好比:根據用戶號查詢用戶的訂單(FindByUserNo),請求數據結構以下:索引
var qs = new Dictionary<string, object> { { "userNo", "no001" } };
除了no001會發生變化之外,查詢數據的結構是不會發生變化的,無論no001查詢多少次,除非有編輯(CUD)的行爲產生,否則結果集是不會有變化的。接口
所以能夠將查詢請求數據爲key,結果集爲value,緩存起來,那麼下次進行相同查詢的時候,即可以直接使用緩存中的數據了,Find代碼大體以下:
public DbResult Find(Dictionary<string, object> query) { var qsKey = JsonConvert.SerializeObject(query); if (cache.Exists(qsKey)) { //從緩存中獲取並返回 } var qsResult = this.FindByQuery(query); cache.Set( qsKey, JsonConvert.SerializeObject(query)); return new DbResult { Error = false, Data = qsResult }; }
每一個相應的表都有相對應的緩存,那麼只要在涉及相應表的方法中清除該表的全部緩存便可。
從以上的代碼看出,功能上還有能夠優化的空間,能夠將緩存的內容調整一下,只緩存結果集中的主鍵,代碼以下:
var qsKey = JsonConvert.SerializeObject(query); var isCached = cache.Exists(qsKey); if (isCached) { //從緩存中取出 string[] ids = null; query = new Dictionary<string, object>{ { "id", new Dictionary<string, object>{ { "$in", ids } } } }; } var qsResult = this.FindByQuery(query); if (!isCached) { PropertyInfo idProp = null; var index = 0; var ids = new string[(qsResult as ICollection).Count]; var enumator = (qsResult as IEnumerable).GetEnumerator(); while (enumator.MoveNext()) { if (idProp == null) idProp = enumator.Current.GetType().GetProperty("Id"); ids[index++] = idProp.GetValue(enumator.Current, null).ToString(); } cache.Set( qsKey, JsonConvert.SerializeObject(ids)); }
這樣能夠減小緩存佔用的空間,而且能夠利用數據庫的索引查詢(若是使用緩存服務,那麼索引指向的就是緩存中的Key),加速數據的獲取。
這裏還能夠再深刻下去,那就是對不一樣表的緩存設定不一樣的緩存時間,對於訪問比較頻繁的表,提供較短的緩存時間,而較少訪問的則設定較長的時間,因而就可在項目內增長緩存的配置管理,後期可增長算法自行管理(提供基礎緩存時間,隨着訪問的頻繁遞增或遞減緩存時間)。
條件查詢
當條件查詢只涉及單個表的時候是很是簡單的,不須要作任何處理,直接使用Find便可,可是關聯到多個表的時候就會比較複雜了,例如:查詢訂單中會員名稱包含「李」且商品名包含「互聯網」的訂單數據。
一種方案是建立一個額外的表,而後將關聯到的字段存儲到該張額外表中去,那麼Find的時候,將轉移調用該表方法,而且在相關編輯方法中對該表進行維護,也可使用數據庫觸發器來進行維護,僞代碼以下:
public class Order : IDb { public DbResult Find(Dictionary<string, object> query) { IDb queryDb; if (查詢個人訂單) queryDb = new MyOrderQuery(); else queryDB = new ShopOrderQuery(); return queryDb.Find(query); } }
另外一種方案是使用ElasticSearch或其餘Lucene搜索服務來實現,跟上面的原理差很少,先提早將須要搜索的數據存儲到服務中去(編輯時同步維護),而後利用搜索引擎來查詢。
前者依賴於數據庫,後期數據庫只做爲存儲介質,而不用來查詢時須要重構相關的代碼解除依賴。
數據權限
項目中存在數據權限的狀況下,只要根據權限系統獲取操做用戶的權限條件,並添加到query上便可,好比:區域管理員只能獲取該區域的訂單數據,僞代碼以下:
public DbResult Find(Dictionary<string, object> query) { query = new Dictionary<string, object>{ { "$and", query } }; query.Add("$and", new Dictionary<string, object> { { "areaId", "廈門市" } }); return this.FindByQuery(query); }
那麼開發人員調用Find接口的時候,獲取的數據就會通過過濾,這裏也能夠加上列數據的過濾,只要在結果集的基礎上再對列進行過濾,沒有權限的列能夠直接過濾掉。
結束語
以上列舉了幾種數據層查詢的擴展方案,因而優勢便體現出來了,數據層開發人員能夠對不一樣的表使用不一樣的數據優化策略,不會影響到開發人員的業務開發,而業務開發人員只須要將全部的精力投入到業務中去,無需關注數據的處理。
文章就到這裏,若是有什麼疑問、建議、錯誤的話,請給我留言,謝謝。