XXXX项目缓存方案总结

XXXX项目缓存方案总结

XXXX项目是目前在实际工作中正在做的事情,该项目是一个大型系统的内容管理内核,负责最核心的meta data的集中管理,性能有较高的要求,设计初期就要求能够支持cluster。项目使用hibernate 3.2,针对开发过程中对于各种缓存的不同看法,撰写了本文。重点在于澄清一些hibernate的缓存细节,纠正一些错误的缓存用法。

一、hibernate的二级缓存
如果开启了二级缓存,hibernate在执行任何一次查询的之后,都会把得到的结果集放到缓存中,缓存结构可以看作是一个hash table,key是数据库记录的id,value是id对应的pojo对象。当用户根据id查询对象的时候(load、iterator方法),会首先在缓存中查找,如果没有找到再发起数据库查询。但是如果使用hql发起查询(find, query方法)则不会利用二级缓存,而是直接从数据库获得数据,但是它会把得到的数据放到二级缓存备用。也就是说,基于hql的查询,对二级缓存是只写不读的。

针对二级缓存的工作原理,采用iterator取代list来提高二级缓存命中率的想法是不可行的。Iterator的工作方式是根据检索条件从数据库中选取所有目标数据的id,然后用这些id一个一个的到二级缓存里面做检索,如果找到就直接加载,找不到就向数据库做查询。因此假如iterator检索100条数据的话,最好情况是100%全部命中,最坏情况是0%命中,执行101条sql把所有数据选出来。而list虽然不利用缓存,但是它只会发起1条sql取得所有数据。在合理利用分页查询的情况下,list整体效率高于iterator。

二级缓存的失效机制由hibernate控制,当某条数据被修改之后,hibernate会根据它的id去做缓存失效操作。基于此机制,如果数据表不是被hibernate独占(比如同时使用jdbc或者ado等),那么二级缓存无法得到有效控制。

由于hibernate的缓存接口很灵活,cache provider可以方便的切换,因此支持cluster环境不是大问题,通过使用swarmcache、jboss cache等支持分布式的缓存方案,可以实现。但是问题在于:
1、 分布式缓存本身成本偏高(比如使用同步复制模式的jboss cache)
2、 分布式环境通常对事务控制有较高要求,而目前的开源缓存方案对事务缓存(transaction cache)支持得不够好。当jta事务发生会滚,缓存的最后更新结果很难预料。这一点会带来很大的部署成本,甚至得不偿失。

结论:XXXX不应把hibernate二级缓存作为优化的主要手段,一般情况下建议不要使用。

原因如下:
1、 XXXX的DAO类大部分是从1.0升级过来,由于1.0采用的是hibernate 2.1,所以在批量删除数据的时候采用了native sql的方式。虽然XXXX2.0已经完全升级到hibernate 3.2,支持hibernate原生的批量删改,但是由于hibernate批量操作的性能不如sql,而且为了兼容1.0的dao类,所以很多地方保留了sql操作。哪些数据表是单纯被hibernate独占无法统计,而且随着将来业务的发展可能会有很大变数。因此不宜采用二级缓存。
2、 针对系统业务来说,基于id检索的二级缓存命中率极为有限,hql被大量采用,二级缓存对性能的提升很有限。
3、 hibernate 3.0在做批量修改、批量更新的时候,是不会同步更新二级缓存的,该问题在hibernate 3.2中是否仍然存在尚不确定。


二、hibernate的查询缓存

查询缓存的实现机制与二级缓存基本一致,最大的差异在于放入缓存中的key是查询的语句,value是查询之后得到的结果集的id列表。表面看来这样的方案似乎能解决hql利用缓存的问题,但是需要注意的是,构成key的是:hql生成的sql、sql的参数、排序、分页信息等。也就是说如果你的hql有小小的差异,比如第一条hql取1-50条数据,第二条hql取20-60条数据,那么hibernate会认为这是两个完全不同的key,无法重复利用缓存。因此利用率也不高。

另外一个需要注意的问题是,查询缓存和二级缓存是有关联关系的,他们不是完全独立的两套东西。假如一个查询条件hql_1,第一次被执行的时候,它会从数据库取得数据,然后把查询条件作为key,把返回数据的所有id列表作为value(请注意仅仅是id)放到查询缓存中,同时整个结果集放到class缓存(也就是二级缓存),key是id,value是pojo对象。当你再次执行hql_1,它会从缓存中得到id列表,然后根据这些列表一个一个的到class缓存里面去找pojo对象,如果找不到就向数据库发起查询。也就是说,如果二级缓存配置了超时时间(或者发呆时间),就有可能出现查询缓存命中了,获得了id列表,但是class里面相应的pojo已经因为超时(或发呆)被失效,hibernate就会根据id清单,一个一个的去向数据库查询,有多少个id,就执行多少个sql。该情况将导致性能下降严重。

查询缓存的失效机制也由hibernate控制,数据进入缓存时会有一个timestamp,它和数据表的timestamp对应。当hibernate环境内发生save、update等操作时,会更新被操作数据表的timestamp。用户在获取缓存的时候,一旦命中就会检查它的timestamp是否和数据表的timestamp匹配,如果不,缓存会被失效。因此查询缓存的失效控制是以数据表为粒度的,只要数据表中任何一条记录发生一点修改,整个表相关的所有查询缓存就都无效了。因此查询缓存的命中率可能会很低。

结论:XXXX不应把hibernate二级缓存作为优化的主要手段,一般情况下建议不要使用。

原因如下:
1、 XXXX的上层业务中检索条件都比较复杂,尤其是涉及多表操作的地方。很少出现重复执行一个排序、分页、参数一致的查询,因此命中率很难提高。
2、 查询缓存必须配合二级缓存一起使用,否则极易出现1+N的情况,否则性能不升反降
3、 使用查询缓存必须在执行查询之前显示调用Query.setCacheable(true)才能激活缓存,这势必会对已有的hibernate封装类带来问题。



总结
详细分析hibernate的二级缓存和查询缓存之后,针对XXXX项目的具体情况做出结论,在底层使用通用缓存方案的想法基本上是不可取的。比较好的做法是在高层次中(业务逻辑层面),针对具体的业务逻辑状况手动使用数据缓存,不仅可以完全控制缓存的生命周期,还可以针对业务具体调整缓存方案提交命中率。Cluster中的缓存同步可以完全交给缓存本身的同步机制来完成。比如开源缓存swarmcache采用invalidate的机制,可以根据用户指定的策略,在需要的时候向网络中的其他swarmcache节点发送失效消息,这一机制和XXXX1.0中已经采用的MappingCache的同步方案基本一致。建议采用。
Hibernate中的query 博客分类: • Hibernate HibernateSQLSQL ServerMySQLJDBC Hibernate中的query.setFirstResult(),query.setMaxResults(); 一、query.scroll()和query.setFirstResult(),query.setMaxResults();这两种方法都可以取到一定范围内的数据,用来数据分页显示。那么两者区别,以及两者的效率如何? 答:1.scroll是用JDBC2.0的可滚动结果集实现;query.setMaxResults();query.setFirstResult()是数据库SQL语句实现。 2.你说是在数据库就分页好呢?还是把结果集都取到内存再分页好呢?(应该是在数据库就分了好些吧,但是如果在内存分页的话,换页的时候是不是更快一些呢?) 3.在数据库进行分页是首选的方式。数据库分页实际上是利用数据库本身SQL扩展的功能进行分页,例如MySQL的 limit 0,50这样的SQL语句。不但速度快,而且非常节省内存。不过不是每种数据库的都有这种分页支持的SQL,例如SQL Server就不支持。 4.scroll是利用JDBC2.0的功能做分页的,那么就完全取决于特定数据库的JDBC Driver的实现了。事实上大部分JDBC Driver都是把所有的结果集都一次取到内存,然后再分页的。如果这个结果集非常大,例如几万条,不但程序执行速度会很慢,而且很容易导致out of memory。当然个别JDBC Driver使用了服务器端游标来实现,那么就不会导致这种问题,例如jTDS。 二、Hibernate可以使用Query.setMaxResults方法简单地设置需要查询的最大结果集。 然后Hibernate会自动根据所设置的数据库方言翻译成相应的SQL语句提交给数据库。比如如果数据库是Oracle,SQL Server等,则翻译为类似select ... top 10之类的SQL语句,若是MySQL,则翻译为select ... limit 10之类的SQL。 三、举例: Java代码 1. import org.hibernate.Query; 2. 3. query.setFirstResult(0),query.setMaxResults(4);相当于MySQL中的limit 0, 4; 4. public void testQuery() { 5. Session session = null; 6. try { 7. session = HibernateUtils.getSession(); 8. session.beginTransaction(); 9. Query query = session.createQuery("from User"); 10. query.setFirstResult(0);//从第一条记录开始 11. query.setMaxResults(4);//取出四条记录 12. List userList = query.list(); 13. for (Iterator iter=userList.iterator(); iter.hasNext();) { 14. User user = (User)iter.next(); 15. System.out.println(user.getId()); 16. System.out.println(user.getName()); 17. } 18. session.getTransaction().commit(); 19. }catch(Exception e) { 20. e.printStackTrace(); 21. session.getTransaction().rollback(); 22. }finally { 23. HibernateUtils.closeSession(session); 24. } 25. }
<think>好的,我需要解决用户在IntelliJ IDEA中打包失败,提示程序包xxxx不存在的问题。根据用户提供的引用内容,我需要综合不同的解决方案来给出清晰的步骤。首先,用户提到可能是依赖未正确下载或损坏。引用[2]中提到,当依赖未完整下载时会出现这个问题,所以需要检查Maven依赖。我应该建议用户检查本地仓库中的相关jar包是否存在,或者尝试重新下载依赖。此外,引用[3]和[4]提到清理缓存和重新安装本地依赖的方法,这也需要包含在步骤中。接下来,用户可能遇到缓存问题。引用[1]和[3]提到清除IDEA缓存的方法,这通常可以解决很多奇怪的问题。因此,步骤中应该包括清理缓存的指导。 另外,Maven的clean和install操作是常见的解决方式,引用[2]和[3]都提到了这一点。需要指导用户执行这些命令,并确保项目正确构建。 还有可能的问题包括JDK版本不一致,引用[1]中提到的源值过时警告,这可能影响编译。用户需要检查项目中的JDK设置,确保所有模块使用相同的版本。 最后,可能需要手动重新导入Maven项目,或者检查模块依赖关系。引用[4]中的错误提示可能与项目结构有关,因此需要确认依赖是否正确添加,并排除冲突。总结下来,解决方案应该分步骤进行,从最简单的清理缓存开始,逐步检查依赖、重新构建,最后处理项目配置问题。同时,要提醒用户注意网络问题导致的依赖下载失败,以及多模块项目的本地安装需求。需要确保每个步骤清晰易懂,帮助用户逐步排查问题。</think>### IntelliJ IDEA打包失败“程序包xxxx不存在”解决方案 #### 步骤1:强制重新下载Maven依赖 1. 打开IDEA终端,执行以下命令: ```bash mvn dependency:purge-local-repository mvn clean install -U ``` 这会强制删除本地缓存的依赖并重新下载[^2][^3]。 #### 步骤2:清理IDEA缓存 1. 点击菜单栏 **File -> Invalidate Caches / Restart** 2. 选择 **Invalidate and Restart**(将重建索引)[^1][^3] #### 步骤3:检查依赖完整性 1. 找到本地Maven仓库路径(默认在`C:\Users\用户名\.m2\repository`) 2. 定位到报错程序包对应的文件夹,检查是否存在以下问题: - 文件夹为空(未完整下载) - 存在`.lastUpdated`文件(下载失败标记) 3. 手动删除不完整的依赖文件夹,重新执行`mvn install`[^2][^4] #### 步骤4:验证JDK配置 1. 检查所有模块的JDK版本是否一致: - **File -> Project Structure** - 确认Project SDK和Language Level - 检查Modules中每个模块的编译输出路径[^1] #### 步骤5:多模块项目处理 如果是微服务项目,需先安装父模块: ```bash mvn clean install -pl 父模块名称 -am ``` #### 步骤6:重建项目结构 1. 右键点击项目 -> **Maven -> Reimport** 2. 执行菜单栏 **Build -> Rebuild Project**[^4] #### 常见错误场景排查 | 现象 | 解决方案 | |-------------------------------|----------------------------------| | 依赖能导入但编译报错 | 检查依赖作用域(scope是否正确) | | 多模块互相引用失败 | 先对依赖模块执行`mvn install` | | 突然出现大量程序包不存在 | 清理缓存+重建索引 |
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值