zoukankan      html  css  js  c++  java
  • 项目演化系列--深入查询

    前言

      对于数据层的所有操作而言,查询是最常用的,之前的文章中只开有Find的查询接口,接口如下:

    public DbResult Find(Dictionary<string, object> query);
    

      由于只开放了一个Find接口,因此在业务开发过程当中,会出现如下缺点:

      1、业务靠多个表数据组合展示的时候,需要业务开发人员多次使用Find方法查找不同的表来组合数据,伪代码如下:

    var orders = orderDb.FindByUserNo("no001");
    var orderProdutcs = orderProductDb.FindByOrderIds(
        orders.Select(o => o.Id).ToArray());
    //组合数据
    

      2、一些相似的业务需要重复编写代码(其实只是多次相同的Find,但是结果数据可能不同),例如:pc端需要显示订单中5个产品的基础信息,而移动端仅仅只显示图片而已

      3、开发人员需要关注多表间的关系(其实使用Orm自动映射仍然要关注这些关系)

      接下来对Find进行扩展,虽然上面的问题无法得到解决,但是好处却更多。

    普通查询

      对于某一个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接口的时候,获取的数据就会经过过滤,这里也可以加上列数据的过滤,只要在结果集的基础上再对列进行过滤,没有权限的列可以直接过滤掉。

    结束语

      以上列举了几种数据层查询的扩展方案,于是优点便体现出来了,数据层开发人员可以对不同的表使用不同的数据优化策略,不会影响到开发人员的业务开发,而业务开发人员只需要将所有的精力投入到业务中去,无需关注数据的处理。

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

  • 相关阅读:
    如何处理消息堆积
    如何避免消息的重复发送
    内存泄漏和内存溢出的关系
    数据挖掘
    servlet
    数据驱动安全需三大核心新技术
    JS 入门经典 第三章 判断、循环和函数
    JS 高级程序设计 第三章
    JS入门经典
    JS高级程序设计1-2章
  • 原文地址:https://www.cnblogs.com/ahl5esoft/p/5151400.html
Copyright © 2011-2022 走看看