1. hibernate自带缓存的情况介绍

hibernate提供了两种缓存,一个是基于对象主ID的一级缓存和一个为缓存查询结果而设计的二级缓存(姑且叫作查询缓存),一级缓存是默认开启的, 而且似乎也是很难关闭的;查询缓存默认是关闭的,而在我的印象中则一直觉得这个东西是没有价值的,至今没有看到典型的使用案例和使用模式。

hibernate提供的查询缓存,默认会去cache所有的查询请求,而这对于常用的系统而言是完全没有必要的。大多数真正需要缓存的应用场景,往往是 数据量庞大,交互操作较多的系统,而一般真正需要缓存的数据只有少量的查询,如果所有的查询都缓存,缓存的效率会大大降低而且会浪费很多内存,可能效率还 不如不用缓存。例如javaeye论坛的版面帖子列表,我们希望缓存的往往也就是每一个版的第12页帖子,如果用户翻老帖子,则不去缓存。这种场景应该 是经常用到的缓存场景,而hibernate查询缓存恰恰不能自动做到,这个就是我们将要讨论解决的问题。

  1. 我们会用多少的查询缓存?

一级缓存HibernateTemplate只有load()get()方法才会使用,如果您用的是list(..;)或者其他的方法都是不缓存的。如果您的系统有内容审核这么一说(如只有审核通过的才能对外显示,删除的不对外显示等等),即使是提取一个帖子(可以用get()的情形),你们组程序员提取数据的语句一般也会写成list(from XXXX where id = … and 状态 = 审核通过的),以图方便或者为了数据库连接可以为readonly事务,这样hibernate仅有的一级缓存也会被跳过,您的查询语句都是没有缓存的!这时候一个有效的查询缓存将会给你的系统带来巨大的性能提升。

  1. 一般的应用场景与通用的查询缓存应该具备的功能

缓存的使用目的是为了提供性能,缓存的使用后果是数据将会有延迟。如果我们决心使用缓存,一般的应用场景应该具有如下特点:事务要求低,数据实时性要求低,并发量要求高,系统可扩展性要求高,机器硬件成本要求低,总之非常适合大面积的空间换时间设计方法并且对数据的完整/准确度要求不高。

缓存还应该是通用的,应用系统只要配置下即可使用;不能每个系统都单独开发,这样也就没有讨论的意义了。

对于要缓存的东西,往往应用本身而且只有应用本身了解什么情况下需要缓存,所以查询缓存应该提供接口,由应用提供解释,来描述哪些查询是需要缓存的,哪些是不需要缓存的。如对于论坛的版面帖子列表,应用可以告诉缓存模块:Java版缓存第12页的帖子,回收站不做缓存,其他版面缓存第1页帖子列表,如果用户是登录用户则所有版面的帖子列表都不做缓存;只有这样才能真正的提高缓存的效率,降低缓存成本。

  1. 一种基于域对象的缓存实现方式

针对前面的描述,偶做了一个实现,基于annotation的定义(当然可以基于xml配置或者基于数据库,然后可以在线的调试配置参数达到最优)。定义如下:

java 代码
  1. @SuppressWarnings("serial")
  2. @Entity
  3. @Cachable(interval=5)
  4. @UniqueCondition(combine = "id=?::limit=2000;word=?::limit=2000")
  5. @QueryCondition(combine = "categoryId=?::limit=30::orderBy=id desc, parentCategoryId=?&&status=10")
  6. public class SomeObject {
  7. private int id ;
  8. /**状态*/
  9. private int status ;
  10. private double symbolLength ;
  11. private String word ;
  12. private String symbol ;
  13. private int categoryId ;
  14. private int clickCount ;
  15. private int parentCategoryId ;
  16. @Id
  17. @GeneratedValue(strategy=GenerationType.AUTO)
  18. public int getId() {
  19. return id;
  20. }
  21. public void setId(int id) {
  22. this.id = id;
  23. }
  24. public String getSymbol() {
  25. return symbol;
  26. }
  27. public void setSymbol(String ipa) {
  28. this.symbol = ipa;
  29. }
  30. @QueryConditionIgnoreParseField
  31. public double getSymbolLength() {
  32. return symbolLength;
  33. }
  34. public void setSymbolLength(double length) {
  35. this.symbolLength = length;
  36. }
  37. public String getWord() {
  38. return word;
  39. }
  40. public void setWord(String word) {
  41. this.word = word;
  42. }
  43. public String authSignature() {
  44. return null;
  45. }
  46. public int getCategoryId() {
  47. return categoryId;
  48. }
  49. public void setCategoryId(int categoryId) {
  50. this.categoryId = categoryId;
  51. }
  52. @CacheIngoreUpdateField
  53. public int getClickCount() {
  54. return clickCount;
  55. }
  56. public void setClickCount(int clickCount) {
  57. this.clickCount = clickCount;
  58. }
  59. public int getParentCategoryId() {
  60. return parentCategoryId;
  61. }
  62. public void setParentCategoryId(int parentCategoryId) {
  63. this.parentCategoryId = parentCategoryId;
  64. }
  65. public int getStatus() {
  66. return status;
  67. }
  68. public void setStatus(int status) {
  69. this.status = status;
  70. }
  71. }

各项定义解释:

  • Cachable ,指定这个实体是要进行缓存的。interval=5,表示对象cache 5秒失效。
  • QueryCondition为一般的查询缓存,指定那些条件下进行结果集cache,如此处就是说当条件为cateogory=某一个值,并且按照id desc进行排序时cache查询记录的前30条(如果一页显示15条,则缓存第12页,第345。。。不做缓存)。或者当条件为parentCategoryId=?并且status=10进行缓存,缓存不考虑排序字段,并且缓存所有结果。
  • UnqiueConditon,指定对象的唯一性条件,当满足这个条件时可以保证数据库只有一个对象,常见情形如用户表的username, nickNameid等。此处按照id查询时进行缓存,最多缓存2000条记录;如果按照word字段进行查询,也进行缓存,最多缓存2000条记录。如对于查询条件 id = 134343 and status = 10 ;cache分析出含有记录唯一保证的字段idstatus就可以暂时忽略,先通过id=134343cache中读取记录,然后在利用好比反射的方 法查看对象的status是否为10,如果是,就直接对象,不是返回null;保证同一个对象尽量占用同样的cache位置。
  • CacheIngoreUpdateField 这个表示忽略的更新字段。因为clickCount用户每次打开都会更新+1,如果此时刷新cachecache也就可以撤消了。所以我们通过定义 这个标记,如果某一个update操作只是更新了clickCount字段,只更新数据库,cache依然有效不做更新。等到Cachable标记的 interval让对象失效以后再更新,保证cache的低刷新频率。
  • QueryConditionIgnoreParseField 表示此字段不参与是否需要cache的匹配,如此处的symbolLength,只要标记了@ QueryConditionIgnoreParseField,无论查询条件中有没有symbolLength条件,都不影响查询缓存决定当前查询是否需要做缓存。
  • Entityhibernate的标记。

----编辑器看到的效果好像和显示的不一样,一直排不对版面:(
评论
ydw9527 2008-01-05
楼主,第4点中的如何使用呢?难道在POJO里加上annotation就行了? 初学中,请指教
发表评论

提醒: 该博客已发表在公共论坛,博客所有留言会成为论坛回贴,留言请注意遵守论坛发贴规则

您还没有登录,请登录后发表评论

myreligion
搜索本博客
最近加入圈子
存档
最新评论