项目演化系列--深刻查询

前言算法

  对于数据层的全部操做而言,查询是最经常使用的,以前的文章中只开有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接口的时候,获取的数据就会通过过滤,这里也能够加上列数据的过滤,只要在结果集的基础上再对列进行过滤,没有权限的列能够直接过滤掉。

结束语

  以上列举了几种数据层查询的扩展方案,因而优势便体现出来了,数据层开发人员能够对不一样的表使用不一样的数据优化策略,不会影响到开发人员的业务开发,而业务开发人员只须要将全部的精力投入到业务中去,无需关注数据的处理。

  文章就到这里,若是有什么疑问、建议、错误的话,请给我留言,谢谢。

相关文章
相关标签/搜索