智能字典缓存 ISmartDictionaryCache-- ESBasic 可复用的.NET类库(18)

1.缘起:

   假设我们有一个会员管理系统,需要向各方提供查询会员基础资料的功能。会员一经注册,其基础资料就将不再发生变化(如会员帐号、身份证ID、注册时间等等)。

   基于这样的需求,我们可以将会员的基础资料“永久地”缓存在内存中,从而提升对任何一个会员基础资料的查询速度。

   我设计了ESBasic.ObjectManagement.Cache.ISmartDictionaryCache来对这种性质的对象进行缓存。

    职能字典缓存的形象示意图如下:

 

2.适用场合:

   在使用ISmartDictionaryCache之前,必须满足以下条件:

(1)将要被缓存的每个对象都有唯一的ID。

(2)被缓存的对象一旦“出生”就是恒定不变的。

(3)对象永远不会被销毁,或者说即使被销毁了在缓存中仍然存在也无关紧要。

 

3.设计思想与实现

ISmartDictionaryCache的接口定义如下:

    public interface ISmartDictionaryCache<Tkey ,TVal>
    {
        IObjectRetriever<Tkey, TVal> ObjectRetriever { set; }
        int Count { get; }

        void Initialize();        

        /// <summary>
        /// Get 如果缓存中不存在id对应的object,则采用ObjectRetriever提取一次,如果仍然提取不到则返回null。
        /// </summary>       
        TVal Get(Tkey id);

        void Clear();

        /// <summary>
        /// HaveContained 当前容器是否已经存在目标对象。
        /// </summary>       
        bool HaveContained(Tkey id);                 

        IList<TVal> GetAllValListCopy();

        IList<Tkey> GetAllKeyListCopy();
    }

 

 

ISmartDictionaryCache依赖于我们前面介绍的IObjectRetriever接口,这就表示智能字典缓存是通过IObjectRetriever来将对象加载到缓存中来的。

      Initialize方法执行时,会将所有对象都加载到内存中。

   当外部调用Get方法时,如果目标对象在缓存中不存在,缓存则会调用IObjectRetriever加载目标对象。如果目标对象在持久化存储中都不存在,就会返回null。

   注意,ISmartDictionaryCache并没有提供任何将对象移除缓存的方法,这表明对象一旦加载到缓存中,就将一直存在直到系统停止运行。

      ISmartDictionaryCache接口的实现是简单的。同样的,SmartDictionaryCache可以在多线程的环境中使用,因为我们在实现时对内部集合ditionary进行了加锁控制。

 

4. 使用时的注意事项

(1)在使用智能字典缓存时,要考虑到对象的数量和对象的尺寸大小的问题。因为如果对象的数量巨大,而且对象的个头也很大,那将占用很多的内存空间。

(2)如果缓存中的对象被访问的概率不是一样的,而是由明显的差别,比如有绝大部分对象从来不被访问,有的对象被密集访问,那这个时候可以考虑使用我们后面即将介绍的“热缓存”IHotCache来代替智能字典缓存。

(3)智能字典缓存在初始化时调用了IObjectRetriever接口的RetrieveAll方法,但是正如我们在介绍IObjectRetriever时提到的,在实现IObjectRetriever接口的RetrieveAll方法,不一定要真正的返回所有的对象,你可以只返回那些你认为将会被使用到的对象。甚至,你可以返回一个不包含任何元素的集合,这样,智能字典缓存就只会缓存那些至少被访问过一次的对象了。在某些系统中,这也许可以有效地节省一些内存空间。

 

5.扩展

   智能字典缓存ISmartDictionaryCache只能缓存恒定不变的对象?嗯,这一点是千真万确的。但是,有些情况我们可以灵活处理。

   还是以我们在缘起部分提到的那个例子继续讲解,让我们在其基础上,再假设一个会员(Member)的绝大部分资料是在注册时就确定下来不会发生改变的,但是有小部分资料(比如对应这数据库中Member表中的几个列)是会发生变化的,比如会员的额度、密码、最后一次访问时间等这样的信息。当外部向系统请求这些非恒定信息时,我们就无法从智能字典缓存中获取了,因为智能字典缓存中的目标会员的这些字段可能已经不是正确的值了。

   基于这样的情况,我们仍然是可以使用智能字典缓存的。这里我提出一个SubObject(“子对象”)的概念。所谓“子对象”就是由某对象的一部分属性构成的一个新的对象。

所以,我们可以将Member中恒定不变的部分抽出来形成一个子对象,如SubMember,那么SubMember就符合了使用ISmartDictionaryCache进行缓存条件。当外部要查询会员的基础资料属于恒定部分时,我们就可以非常快速地从ISmartDictionaryCache获取返回。

   对于像Member这样由恒定部分和非恒定部分构成的对象来说,除了使用这种方式进行缓存之外,还有其它的办法。但由于超出了本节的主题,我们会在其它小节进行介绍。

 

注: ESBasic已经开源,点击这里下载源码。
    ESBasic开源前言

 

 

 

时间: 2024-10-24 05:33:56

智能字典缓存 ISmartDictionaryCache-- ESBasic 可复用的.NET类库(18)的相关文章

层级结构缓存IHiberarchyCache -- ESBasic 可复用的.NET类库(24)

1.缘起:     从IMultiTree到IAgileMultiTree,一切进展得都不错.但是,还有改进的地方.多叉树的一个优点在于,根据指定的节点能够非常迅速地找到其所有的子节点.但是缺点在于,根据节点值的ID定位到目标节点不够快,因为需要对所有的节点进行遍历操作.当节点非常多.层次非常深时,这种定位操作可能会严重的影响效率.     我设计了层级结构缓存ESBasic.ObjectManagement.Cache.IHiberarchyCache来加速这种根据节点值ID定位节点的访问.所

Round缓存管理器RoundCacheManager--ESBasic 可复用的.NET类库(26)

1.缘起:     在增量自动获取器章节的缘起部分,我们曾提到增量缓存,本节我们将深入探讨它以及用于管理增量缓存的管理器.我们还是以增量自动获取器章节提到的例子作为基础,并做更进一步的讨论.       OK,现在让我们开始这有趣的旅程. 首先,基于前面例子给出的上下文,我们知道IIncreaseAutoRetriever获取的增量是用于累积当天的已成交订单报表的."当天已成交报表"就是一个典型的增量缓存,每当有新的增量到来,都会累加到上面. 我们假设今天是2009.07.08,那么我

热缓存 IHotCache --ESBasic 可复用的.NET类库(19)

1.缘起:     假设我们有一个订单系统,现在这个系统要增加一个功能――允许客人查核他认为有问题的订单的详细信息.当客人觉得自己的某个订单不对劲时,他首先会从订单系统查询这个订单的详细信息,然后打电话告诉我们的客服有问题的订单的编号,客服再去查核,如果属实,客服还要进一步上报,如果该订单非常重要,则可能需要更进一步上报复查等.     从这个需求我们看到,同一个订单可能会在比较短的时间内查询数次甚至数十次,所以我们可以称这个订单为"热点"订单.而其它的成千上万的订单可能在一个月内都不

定时刷新缓存管理器 IRefreshableCacheManager --ESBasic 可复用的.NET类库(16)

1.缘起:     为了提升系统的性能或减轻数据库的压力等原因,我们经常在系统中使用缓存来把那些经常使用的数据保留在内存中.如果因为某些原因,缓存中这些经常使用的数据不能及时与数据源进行同步更新,那么采用定时刷新缓存中的数据有可能就是一种合适的选择.     如果你的缓存是定时刷新,那么你就需要自己为其维护一个定时器或循环引擎.如果你的系统中像这样定时刷新的缓存有多个,而且每个缓存定时刷新的时间间隔又要求不一样,那么,使这些缓存按照你预想的情况进行运转,你就需要花费一些气力.     我设计了定

循环任务切换器 CircleTaskSwitcher -- ESBasic 可复用的.NET类库(06)

 1.缘起:     假设我的订单处理系统有这样的需求:将一天24小时分为4个时段,凌晨2:15到8:30采用A类型的处理器处理接收到的订单,8:30到14:00采用B类型的处理器,14:00到20:00采用C类型的处理器,20:00到第二天凌晨2:15采用D类型的处理器.     即我们的订单处理器需要在任一天的2:15.8:30.14:00.20:00这四个时刻发生切换,这就是一个循环切换器所要做的工作.     我设计了ESBasic.Threading.Application. ICir

对象获取器IObjectRetriever -- ESBasic 可复用的.NET类库(17)

1.缘起: ESBasic中许多管理对象的容器都用到了这个ESBasic.ObjectManagement.IObjectRetriever接口,所以单独将其提出来介绍一下. 当我们向对象容器(Container)请求某个对象时,也许目标对象还未加载到容器中,这可能是因为容器在初始化的时候就没有加载这个对象,也有可能是因为这个对象是容器初始化以后新增到数据库(当然也有可能是其它的持久化存储)的.在这种情况下,对象容器就可以借助IObjectRetriever来将目标对象从数据库等持久化存储中加载

ESBasic 可复用的.NET类库(00) -- 开源前言(附下载)

自从03年正式使用.NET开发以来,已经走过了6个年头,这期间我积累了几套类库和框架,ESBasic便是其中最基础的一个类库.ESBasic是Enterprise Service Basic的缩写,虽然也简写为ESB,但是它和Enterprise Service Bus(企业服务总线)没有任何关系.ESBasic是我能够快速和高效开发应用程序的利器之一,开这个专门的blog是想将它介绍给大家,希望能对大家有所启发. ESBasic覆盖的内容包括:对象管理.插件.网络(Socket).多线程.Em

双向映射 IBidirectionalMapping -- ESBasic 可复用的.NET类库(11)

1.缘起:     假设我们的用户管理系统要求用户的ID和Name都必须是唯一的,并且用户的ID和Name一经确定就不能被修改.而且管理系统经常需要根据ID来查找Name,也经常需要根据Name来查找ID.根据这样的需求,我们可以考虑使用一个Dictionary来将ID和Name缓存起来,通常ID作为Key,Name作为Value.这样便可实现通过ID查询Name的快速查找,但是,通过Name查找ID就不是那么快了,因为涉及到对Dictionary的Values做遍历的操作.那么,有可能使得通过

对象管理器 IObjectManager -- ESBasic 可复用的.NET类库(12)

1.缘起: 我们经常需要对一些动态对象进行管理,最常见的例子就是在线用户管理.当一个用户成功登陆到服务器后,我们就需要将其管理起来:当他退出后,就不再需要再管理他了.这就是所谓动态对象的含义,这些对象并不是一直需要被管理,只有当其被激活后,才需要被管理.它们总是在"激活"状态和"非激活"状态之间不断地切换. 我设计了对象管理器ESBasic.ObjectManagement.Managers.IObjectManager来管理类似的动态对象.这个类是ESBasic提