項目演化系列--深刻查詢

前言算法

  對於數據層的全部操做而言,查詢是最經常使用的,以前的文章中只開有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接口的時候,獲取的數據就會通過過濾,這裏也能夠加上列數據的過濾,只要在結果集的基礎上再對列進行過濾,沒有權限的列能夠直接過濾掉。

結束語

  以上列舉了幾種數據層查詢的擴展方案,因而優勢便體現出來了,數據層開發人員能夠對不一樣的表使用不一樣的數據優化策略,不會影響到開發人員的業務開發,而業務開發人員只須要將全部的精力投入到業務中去,無需關注數據的處理。

  文章就到這裏,若是有什麼疑問、建議、錯誤的話,請給我留言,謝謝。

相關文章
相關標籤/搜索