已阅读5页,还剩7页未读, 继续免费阅读
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
内存级缓存技术红皮书NC-UAP 5.0用友NC-UAP2019-11-131第 页目 录第一章前言1第二章内存占用3第三章数据更新61.VersionSensitiveMap62.ElementVersionSensitiveMap72.1ICacheVersionMonitor8第四章结论10第 10 页第一章 前言NC中的效率问题的通常是由于程序所采用的算法中需要频繁地访问数据库所致.对于这类问题,通过使用合适的缓存往往能使问题得到根本解决. 这类方法的本质就是用空间来换取时间,把多次的数据库访问合成少数几次或者一次,批量地取回数据存放在内存中,把大部分数据库访问转换成对内存的访问,从而显著地提升程序运行效率.为了使程序运行得更快,通常会采用一定的索引机制,最常见的就是使用哈希表.下面的代码示例对同样的业务逻辑给出两种不同的实 现方法,显而易见,使用缓存的算法将比不使用缓存的算法要快很多. listing 1/* *需要频繁访问数据库的算法 * */private void doSomething(String pk_deptdocs) for(int i=0;ipk_deptdocs;i+) DeptdocVO vo = (DeptdocVO) getDeptdocVOFromDB(pk_deptdocsi); dealWithDeptdocVO(vo); /* *使用缓存的算法 * */private void doSomething(String pk_deptdocs) DeptdocVO vos = getDeptdocVOsFromDB(pk_deptdocs); Map cache = new HashMap(); for(int i=0;ivos.length;i+) cache.put(vosi.getPrimaryKey(),vosi) for(int i=0;ipk_deptdocs;i+) DeptdocVO vo = (DeptdocVO)cache.get(pk_deptdocsi); dealWithDeptdocVO(vo); 上面的例子对缓存的使用是有效但朴素的.如果doSomething不是频繁被调用的话,这样的做法就足够了.但如果这段代码是在服务器端运行,那就有可能频繁地被多个线程调用,那么就需要考虑更多的问题.上面的实现中缓存是在堆栈中的,每次调用都要建立缓存.如果每次调用的传入的数组中有相同的元素的话,那么还是 有很多的数据库访问是可以避免.因此这种情形下就不再适合采用堆栈级的缓存,而是需要更长生命周期的缓存.一般来说最容易想到的,有时也是唯一的办法就是 把缓存对象做静态的(static)的.NCV5,在服务器端,还有一种办法就是把缓存对象注册为一个组件(component),并把其 singleton属性设置为true. 但是对于这样长效的缓存又带来另外两个问题.首先是内存占用的问题.对于上面提到实现方法,缓存的内存无法被虚拟机自动回收,如果有一段时期,该缓存没有被用到,那么缓存占用的内存实质是一种浪费.而且如果缓存占用的内存过于巨大甚至会拖垮整个应用,导致性能急剧下降,甚至内存耗尽,产生OutofMemory异常.另一个问题是就是数据有效性问题.数据从数据库中加载到内存中之后,很可能又被修改了.如果缓存没有更新机制,经过一段时间之后,缓存中的数据可能大部分是错误的.这可能会产生灾难性的后果带来无法挽回的损失. 第二章 内存占用为了避免缓存无限制地占用内存,一个比较容易的想法是主动地限制内存的使用量.比如可以实现一个仅能存储一定数量元素的HashMap, 当超过数量限制时,就把一些元素删除掉,以容纳新加入进来的元素.这里需要考虑的问题就是:怎样选择要删除的元素?这个问题没有明确的答案.针对实际情况的不同可以采用不同的策略.比较常见的策略包括:FIFO,先进先出策略,即总是删除留在缓存中最久的数据;LRU,最近最少使用策略,即把最不常用的数据 优先删除掉.另外Java也提供了些方便缓存实现的机制,比如weak reference和soft reference.虚拟机发现当一个对象仅被通过weak reference和soft reference所引用时,便会在一定的时候回收这个对象。但是具体的回收策略会因不同的虚拟机实现而不同。 在这个写这个文档的第一个版本时,曾认为soft reference是缓存进行内存管理的首选策略。但经过在不同运行环境下的测试,发现soft reference的回收策略比较复杂。不仅在因虚拟机不同而不同,而且,至少在Sun的虚拟机下,还受堆内的自由内存大小的影响。因此soft reference的回收策略比较复杂,从而较难预料其行为。所以这里不再推荐其作为缓存的内存管理策略。NCV5 UAP实现了一套缓存框架,提供了基于各种内存管理策略的缓存,而且能适配包括OSCache在内的多种缓存实现.为了方便使用,提供了一个工具类 CacheToMapAdater. 从字面上的理解就可以知道这个类是使用了Adatper模式,把NC的缓存实现适配成一个Map接口.也就是说可以象使用HashMap一样使用NC的缓存机制,同时又可以选择合适的内存管理策略,避免缓存占用内存过大,或者无法被回收等问题. listing 2public final String CACHE_REGION_PREFIX = nc.vo.bd.access.AccessorSideFactory;String dsName = getDataSourceName();Map cache = CacheToMapAdapter.getInstance (CACHE_REGION_PREFIX+dsName, CachePolicyFactory.createFiFoCachePolicy (false, true, -1,1000) );CacheToMapAdapter getInstance(String regionName,ICachePolicy cachePolicy)其中regionName是一个对缓存实例的一个标示,这必须是虚拟机范围内唯一的。一般来说可以把调用这个方法的类的全名作为参数(如果该类中仅 使用了一个缓存实例的话),上面的例子里由于需要把数据按照数据源分开,因此把把类全名加上数据源名称作为缓存的标示。 cachePolicy是指缓存实例所使用的内存管理策略,可以通过一个工厂类来创建。上面的例子里,使用的策略是一个最多保存1000个元素的先进先出 策略。(createFiFoCachePolicy的其他三个参数可以参见相关的javadoc). CacheToMapAdapter还提供了一个仅有个一个参数的getInstance方法,返回一个即采用基于软引用内存管理策略的缓存实例。 通过上面的方法得到一个Map接口实例之后,就可以像使用普通的Map一样使用了.不过由于某些NC缓存的实现细节,这个Map接口实例并不能支持Map的所有方法,具体可以参考CacheToMapAdapter的javadoc. 当然最基本的put和get方法是支持的. 一般来说一个缓存的实现不会把由CacheToMapAdapter返回的Map接口直接暴露给其客户程序.因为内存管理策略的细节有时会让使用者很迷惑.通常会按如下的方式进行封装,使得客户程序完全不用关心缓存的实现细节. listing 3Map cache = CacheToMapAdapter.getInstance(nc.vo.cache.smaple.Deptcache);public DeptdocVO synchronized getDeptdocVOByData(String pk_deptdocvo) DeptdocVO vo = (DeptdcoVO)cache.get(pk_deptdocvo); if(vo=null) initCache(pk_deptdocvo); vo = (DeptdcoVO)cache.get(pk_deptdocvo); return vo;正如上面的代码所示,对缓存的封装一般是按照这样的方式实现的 1. 查询缓存,若数据不存在则2,反之则4 2. 从外部读取数据,并缓存之. 3. 查询缓存 4. 返回结果 其中步骤2,可以一次性把数据加载到内存中,也每次可以加载一部分,这需要根据具体的应用场景来选择.另外如果客户程序经常查询一些不存在的数据, 则每次都会执行步骤2,这可能会引起较严重的效率问题,尤其是采用一次性加载数据的算法时.对于这种情况,可以考虑设计一个黑名单,把这些数据加入到黑名 单,这样对于这样的数据就不再做查询了. 综上所述,利用CacheToMapAdapter可以象使用Map一样来使用NCV5提供的缓存框架,而不用另外再了解后者的API。唯一需要注意的问题是选择合适的内存管理策略。现在CacheToMapAdapter的 默认策略是基于软引用的策略,这可能不总是最合适的策略。为具体的应用场景选择合适的策略不象想象中那么简单,但也有一个好消息,那就是在开发阶段可以选 择默认的策略,然后再根据实际运行的情况再改成合适的策略,而需要做的仅仅是改变一下getInstance的参数而已. 第三章 数据更新1. VersionSensitiveMap如前文所述,缓存的另一个问题就是如何保证数据的有效性。也就说需要一种数据更新机制,当硬盘或数据库中的数据(以下统称为资源)发生变化后,能把 缓存在内存中的数据更新.为了解决这个问题NCV5提供了两个版本敏感的Map包装类(GOF Decorator pattern),VersionSensitiveMap和ElementVersionSensitiveMap。这两个类的所有读方法,比如get,values等等,都被设计成首先检查一下该Map中所保存的数据的版本是否是最新的,若不是最新版本,则把数据清空,相当于这些数据从未被加载到内存中。为了屏蔽不同类型资源的版本检查方式,抽象了一个ICacheVersionMonitor接口。该接口只有一个方法isCacheOutOfDate,可以通过调用该方法判断当前这个版本监视器所代表的内存数据的版本是否已经过期了. listing 4/* * 缓存版本监控接口 * */public interface ICacheVersionMonitor boolean isCacheOutOfDate();利用上述两个Map的包装类,可以实现能自动更新数据的缓存,解决数据有效性的问题.下面的代码是在listing 3 的基础上改写而成的.值得注意的是二者的区别仅在于cache的创建上.在这里cache指向了一个VersionSensitiveMap的实例,而后者则包装了一个HashMap.另外VersionSensitiveMap还安装了一个表版本监视器,TableVersionMonitor-这个类实现了ICacheVersionMonitor接口.这样当表bd_deptdoc中数据发生变化后,cache将会知道这个变化,并把其中的数据清空,使得缓存重新被初始化。这些动作是在VersionSensitiveMap内部进行的,缓存的实现甚至都不会觉察到这个过程. listing 5Map cache = new VersionSensitiveMap(new HashMap(), new TableVersionMonitor(new Stringbd_deptdoc),2*60*1000);public DeptdocVO synchronized getDeptdocVOByData(String pk_deptdocvo) DeptdocVO vo = (DeptdcoVO)cache.get(pk_deptdocvo); if(vo=null) initCache(pk_deptdocvo); vo = (DeptdcoVO)cache.get(pk_deptdocvo); return vo;VersionSensitiveMap是按照Decorator模式实现的,也就说它不是仅仅能包装HashMap,实际它包装的是Map接口.因此VersionSensitiveMap可以和CacheToMapAdapter组合起来使用.listing 3 解决了内存占用的问题,listing 5 解决了数据有效性的问题,为了同时解决这两个问题,只需把cache的创建方式改成如下的形式即可. listing 6Map cache = new VersionSensitiveMap( CacheToMapAdapter.getInstance(nc.vo.cache.smaple.Deptcache), new new TableVersionMonitor(new Stringbd_deptdoc),2*60*1000);2. ElementVersionSensitiveMapVersionSensitiveMap通过一个ICacheVersionMonitor把 特定的资源和其所包装的Map绑在一起,当资源的版本变化时,就把所包装的Map清空。这种方式对于某些应用场景来说控制粒度是太粗了。即使对资源的 修改仅仅使得缓存中的一小部分数据不再有效,却同样需要重新加载所有的数据. 如果缓存的数据较多,或者缓存的数据需要经过复杂的运算才能建立起来,这样的控制粒度显然是不妥的.ElementVersionSensitiveMap被设计用来解决这个问题,这个对元素的版本敏感的Map和VersionSensitiveMap的区别在于前者根据需要可以为Map的每一个元素安装一个版本监视器,当外界访问到某个元素时, ElementVersionSensitiveMap首先通过与该元素的所对应的版本监视器判断该元素所对应的资源是否已经发生了变化,然后决定是否删除该元素以便让缓存重新加载数据。 ElementVersionSensitiveMap可 以让缓存的版本监视做到很细的粒度,但同时也需付出相应的代价。首先是缓存的实现要复杂一些,其次对资源修改时的版本记录也需要控制到同等的粒度,再次缓 存需要更频繁地调用版本监视器检测版本的变化. (第一点比较容易理解,第二三点后面将做解释。)因此在实现缓存的时候需要权衡资源更新的频率,数据量的大小两个因素,以选择合适的版本控制粒度。资源更 新的频率越低则越倾向使用VesionSensitiveMap,数据量越大则越倾向使用ElementVersionSensitiveMap. 另外如果不是存在压倒性的效率问题,实现的复杂度也是需要考虑的因素. 下面代码的展示了如何使用ElementVersionSensitiveMap.与VersionSensitiveMap不同,ElementVersionSensitiveMap的构造方法不是接受一个ICacheVersionMonitor类型参数而是接受一个VersionMonitorFactory类型参数. 后者是一个创建版本监视器的工厂,它可以根据没有元素的key,为每个元素生成一个监视器。ElementVersionSensitiveMap负责维护每个监视器和元素的对应关系。为了让版本监视的粒度从整个bd_deptdoc表细化到每条记录,这里不能再使用TableVersionMonitor,而是换成了ObjectCacheVersionMonitor从而实现对每条记录进行版本监视。 listing 7Map cache = new ElementVersionSensitiveMap( CacheToMapAdapter.getInstance(nc.vo.cache.smaple.Deptcache), new DeptdocVersionMonitorFactory(); public final static long TIME_OUT = 2*60*1000;class DeptdocVersionMonitorFactory implements VersionMonitorFactory public ICacheVersionMonitor createVersionMonitor(Object key) String pk_deptdoc = (String)key; return new ObjectCacheVersionMonitor(pk_deptdoc,TIME_OUT); public DeptdocVO synchronized getDeptdocVOByData(String pk_deptdocvo) DeptdocVO vo = (DeptdcoVO)cache.get(pk_deptdocvo); if(vo=null) initCache(pk_deptdocvo); vo = (DeptdcoVO)cache.get(pk_deptdocvo); return vo;2.1 ICacheVersionMonitor到目前为止,已经介绍了接口ICacheVersionMonitor的两个实现,TableVersionMonitor和ObjectCacheVersionMonitor,以及如何利用它们监视不同资源的版本变化.但这只是事情的一个面.版本监视没有看上去那么简单.不是任意指定一个表就可以通过TabelVersionMonitor监视他的变化,而是需要在对表中的数据进行增删改操作的时候维护版本记录。 NCV5提供了一个新的接口ICacheVersionBS,可以简化维护资源版本的工作。首先要为需要维护版本的资源选一个唯一标示。对于数据库表来说表名就是一个好的选择。对于表中的记录来说,其主键就是一个现成的标识。当然前提是这个主键值不会同其他表的某个主键值相同,否则就需要采用类似表名加主键的方案。对于配置了多账套的情况下,表名也不再是唯一了,但是ICacheVersionBS的实现内部区分了账套,一般情况不用考虑多账套的问题了。 在为资源确定好了标示之后,接下来把所有对资源进行增删改操作的地方调用一次ICacheVersionBS.updateVersion方法即可,其参数就是选定的标识。比如listing 7 中就指定了2分钟的超时时间。 对资源的标识的选择,实际上就是在确定版本的监视控制粒度.粒度的选择实际可以是很灵活的,并不是只能选择表级和或者记录级.比如对于部门档案来 说,还可以选择按照公司作为缓存的控制粒度,也就是说如果修改了某个公司的某个部门,那么缓存将重新加载该公司的数据,而不会影响其他公司的数据. 下面的代码为bd_deptdoc表同时提供了表级,记录级以及公司级的的版本维护. listing 8protected void updateDeptdoc(De
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 伤口换药护理查房
- 应急物资台账管理保证措施
- 2026年演出经纪人考试题库及完整答案
- 2025-2026学年三年级上册数学应用题解析试卷(附答案)
- (正式版)DB13∕T 1223-2010 《化工产品的碘值测定方法》
- 2025-2026年四川省部编版高三生物第一章生物技术测试卷
- 2026年烟花爆竹安全作业特种作业测试题库(含答案)
- 2025-2026年中药学综合应用能力测试题库
- 2025-2026年项目合同管理实战习题
- 2026年学校食堂水产冻品入库质量验收工作流程
- 2026道德与法治新教材五年级上册全套单元试卷及参考答案
- 2026内蒙古地质矿产集团有限公司所属企业招聘226人笔试备考题库及答案详解
- 电力线路结构介绍(实物图)
- 职业技能大赛(水生物病害防治员赛项)考试题库(含答案)
- 临床医学专业概述
- 三节三爱主题教育班会
- 儿童特应性皮炎护理
- 2023年国家林业和草原局直属事业单位招聘笔试真题
- JBT 11270-2024 立体仓库组合式钢结构货架技术规范(正式版)
- 《元器件焊接》课件
- 锂电池专用湿法隔膜生产线项目实施方案
评论
0/150
提交评论