当前位置: 首页 > news >正文

Hibernate 缓存

2019独角兽企业重金招聘Python工程师标准>>> hot3.png

概述

Hibernate Cache 对于提高应用的性能是非常有用的。缓存的目标就是减少数据库的查询,从而减少应用的吞吐时间。Hibernate提供如下几种缓存类型

  • 一级缓存(First Level Cache): Hibernate一级缓存是和Session对象紧密关联的。Hibernate一级缓存是默认可用的,并且不能关闭它。但是Hibernate提供了可以删除指定object的方法。任何一个在一个session中缓存的对象在其他session中是不可见的,当session关闭后所有缓存的对象也会丢失。

一级缓存

  • 二级缓存(Second Level Cache): 一级缓存的作用域是Session,而二级缓存的作用域是SessionFactory,这就意味着缓存可以被同一个Session Factory的不同 Session 是可以同时共享缓存实例的。当一个二级缓存被设定,将会发生如下:
    • 如果一个是你实例已经出现在了一级缓存中,则直接返回该实例
    • 如果一个实例在一级缓存中并没有出现,但是相应的实例状态(instace state)被缓存在了二级缓存中,数据将从二级缓存中得到,实例将会被组装和返回。

二级缓存

  • 查询缓存: 将常用的、变化不频繁的查询结果缓存起来。

一级缓存

Session session = sessionFactory.openSession();

//第一次查询数据库
session.get(Employee.class, 1L);

Thread.sleep(2000);

//第二次不查询数据库
session.get(Employee.class, 1L);

session.close();

在同一个session中当对象的实例存在时将会直接返回实例,并不会再次执行查询数据库操作。但是hibernate提供了清除缓对象实例的方法,它们是:

  • clear() : 该方法会清除session一级缓存的所有对象实例
  • evict(Object object) :该方法清除一级缓存的指定对象
Session session = sessionFactory.openSession();

//第一次查询数据库
session.get(Employee.class, 1L);

Thread.sleep(2000);
        
//清除一级缓存
session.clear();
// or session.evict(employee);

//第二次查询数据库,因为一级缓存已经被清除
session.get(Employee.class, 1L);
session.close();

二级缓存

二级缓存的作用域是 Session Factory

可以一个事务产生的二级缓存可以在两一个事务中直接访问到。现在做如下测试(设置缓存策略为只读):

Session session = sessionFactory.openSession();
Session otherSession = sessionFactory.openSession();

Statistics statistics = sessionFactory.getStatistics();
statistics.setStatisticsEnabled(true);


//开启第一个事务
Transaction transaction = session.beginTransaction();

//第一次查询数据库
Employee employee= session.get(Employee.class, 1L);
print("第一次从数据库中查询",statistics);

//第二次从一级缓冲中查询
session.get(Employee.class, 1L);
print("第二次从一级缓冲中查询",statistics);

//清除一级缓存
session.evict(employee);

/从二级缓存中查询
session.get(Employee.class, 1L);
print("从二级缓存中查询",statistics);

//提交第一个事务
transaction.commit();
session.close();

        

//第二个事务
Transaction otherTransaction = otherSession.beginTransaction();

otherSession.get(Employee.class, 1L);
print("另一个事务",statistics);

otherSession.get(Employee.class, 2L);
print("查询另一条记录",statistics);

//提交第二个事务
otherTransaction.commit();
otherSession.close();

最终结果为:

执行结果

  • 第一次get的时候缓存中没有,那么从数据库中读一条记录;
  • 第二次get的时候是从一级中读取(不读数据库);
  • 当调用evict释放查出的数据后在get会从二级缓存中查(不读数据库);
  • 当开启另一个事务再get时依旧是从二级缓存中读取(二级缓存跨事务);
  • 当查询另一条记录时一、二级缓存中都没有,所以直接在数据库中查询。

Region Factory

Hibernate的二级缓存是对真实的缓存提供者(cache provider)透明的,org.hibernate.cache.spi.RegionFactory接口囊括了缓存提供者应该实现的各种细节,该接口其实是Hibernate和缓存提供者的一个桥梁。比较知名的缓存提供者有:

  • Ehcache : 成熟、使用范围广,本地缓存,单节点
  • Hazelcast :分布式、多节点
  • Infinispan : 分布式、多节点
  • Hibernate Redis : Redis实现的Hibernate二级缓存框架

Hibernate二级缓存策略

  • 只读(READ_ONLY): 当实体数据从来不会改变的时候使用(当尝试更新该实体对象时会抛出异常),该策略比较简单,但性能最佳,适用于那些不需要改动的静态数据。

  • 不严格读写(ONSTRICT_READ_WRITE): 当事务提交后受影响的数据发生变化后缓存才会被更新,因此策略无法保证强一致性,存在一个短暂的窗口期,在这个期间可能从缓存中获取的数据已经是脏数据了。如果应用知识偶尔需要更新数据,并且不需要严格的事务隔离,可以考虑使用该种策略。缓存实例没有被加锁,如果两个事务同时修改数据,我们不知道我们到底拿到了什么数据,这种策略不能保证缓存中的数据和数据库中的数据一致。最好设置缓存的有效期。

  • 读写(READ_WRITE): 这种策略通过加锁保持了数据的强一致性,当一个被缓存的实体数据发生变化,锁将会被添加到缓存中,直到事务被提交才释放该锁,此时当事务访问加锁的实体数据时将会直接访问数据库。

  • 事务(TRANSACTIONAL): 这种策略应用于JTA环境中

缓存管理

如果没有设置缓存过期(expiration)和缓存释放(eviction)策略,缓存将会一致增加直到耗尽所有内存。通常情况下Hibernate将缓存管理的职责转交给缓存提供者(Cache Provider)。

集合的缓存

默认情况下集合是不会被缓存的,只有明确地标注为可缓存,该集合对象才能被缓存。

缓存状态内部的内部表征

实体并没有缓存实体的对象实例到二级缓存中,而是拆散(disassembled)的状态,返回缓存的时候需要重新组装(assembly)。

  • 二级缓存中没有保存ID(主键),主键被以cache key的身份保存。
  • 被@Transient修饰的属性不会被缓存
  • 集合没有被缓存
  • 没有关联关系的属性会被以他原来的形式缓存
  • 在多对一、 一对一的关联关系的只有外键会缓存

这种设计只是反映了潜在的对应关系,这样提高了空间利用率,同时也方便关联的实体类的协调。

集合缓存的内部表征

在一对多,多对多的集合需要显式地指出才会被缓存。其实Hibernate将每一个集合的缓存放在其他的缓存域中,而不是该实体对象所在的缓存域中,集合缓存域的名称由类名+集合的属性名组成,通过域的名字可以查看到缓存的数据。

还有需要指出的是,只有集合中各个对象的主键会被缓存,然后通过这些主键再去查找对象的其他属性,因此最好把集合中的实体类也设置成可被缓存。

查询缓存

可以HQL 的查询结果也缓存起来,当要经常查询很少发生变大的数据集时做查询缓存是很有用的。要启动查询缓存只要设置hibernate.cache.use_query_cache属性为true即可。

查询缓存的最佳实践

  • 进行查询缓存的时候只有实体的主键会被缓存,所以强烈推荐使用二级缓存。

  • Hibernate会为每一个查询参数设置一个缓存,因此如果一个查询有很多的查询参数组合,那么这个查询不适合做查询缓存。

  • 如果实体对应的数据库的数据会经常发生变动,那么该查询不适合做查询缓存,因为当数据库中的数据发生变化,缓存将会无效,不管已经发生变化的实例是否已经缓存。

  • 所有的查询缓存被缓存在rg.hibernate.cache.internal.StandardQueryCache缓存域下。当实体或者集合将要被缓存的时候,可以根据需要自定义缓存参数。可以为每个查询缓存设置域名称,以便为不同的查询缓存提供不同的设置。

  • Hibernate在域org.hibernate.cache.spi.UpdateTimestampsCache保存着数据库的最后一个更新的时间戳。当在使用查询缓存的时候这个作用域非常重要,因为Hibernate通过这个来判断缓存的实体是否已经过期。在该缓存域中的属于不应该被释放或者过期处理,因为这里的缓存的数据是为了和在表中数据查询做协调的。最好为这个缓存域设置关闭自动释放和过期处理,因为它也消耗不了太多空间。

参考资料

  • http://www.baeldung.com/hibernate-second-level-cache

  • https://www.journaldev.com/2969/hibernate-caching-first-level-cache

  • https://www.journaldev.com/2980/hibernate-ehcache-hibernate-second-level-cache

最后

感谢阅读,写作能力有限,如有需要可以扫描下方二维码并关注公众账号与作者取得联系。

扫码关注

转载于:https://my.oschina.net/liuyatao19921025/blog/1603436

相关文章:

  • ESXi 5.0 环境下安装部署Cisco Nexus 1000v
  • Python之内置函数
  • Lucene知识小总结7:评分设置
  • Rust语言:安全地并发
  • python基础===python中文手册
  • 便是管理,不是管理
  • 6-1 接口的特性
  • 驱动和应用层的三种通信方式
  • 《Java编程思想》笔记03------访问权限控制
  • 记笔记与博客
  • 树的遍历
  • 菜鸟配置SAMBA服务之1
  • Qt动态设置布局中的控件
  • CISCO无线AP胖瘦升级
  • dotcms总结
  • SegmentFault for Android 3.0 发布
  • 《微软的软件测试之道》成书始末、出版宣告、补充致谢名单及相关信息
  • Android 初级面试者拾遗(前台界面篇)之 Activity 和 Fragment
  • CSS进阶篇--用CSS开启硬件加速来提高网站性能
  • flask接收请求并推入栈
  • github指令
  • JavaScript 基础知识 - 入门篇(一)
  • Javascript弹出层-初探
  • JavaScript函数式编程(一)
  • Java教程_软件开发基础
  • Laravel Telescope:优雅的应用调试工具
  • miniui datagrid 的客户端分页解决方案 - CS结合
  • Redis在Web项目中的应用与实践
  • 后端_MYSQL
  • 技术发展面试
  • 面试遇到的一些题
  • 如何胜任知名企业的商业数据分析师?
  • 提醒我喝水chrome插件开发指南
  • 微信支付JSAPI,实测!终极方案
  • 吴恩达Deep Learning课程练习题参考答案——R语言版
  • 用jquery写贪吃蛇
  • 云栖大讲堂Java基础入门(三)- 阿里巴巴Java开发手册介绍
  • 怎么把视频里的音乐提取出来
  • 这几个编码小技巧将令你 PHP 代码更加简洁
  • 关于Android全面屏虚拟导航栏的适配总结
  • 曜石科技宣布获得千万级天使轮投资,全方面布局电竞产业链 ...
  • # centos7下FFmpeg环境部署记录
  • #1015 : KMP算法
  • #每天一道面试题# 什么是MySQL的回表查询
  • $().each和$.each的区别
  • $(function(){})与(function($){....})(jQuery)的区别
  • (1)(1.19) TeraRanger One/EVO测距仪
  • (39)STM32——FLASH闪存
  • (C语言版)链表(三)——实现双向链表创建、删除、插入、释放内存等简单操作...
  • (板子)A* astar算法,AcWing第k短路+八数码 带注释
  • (定时器/计数器)中断系统(详解与使用)
  • (附源码)计算机毕业设计ssm-Java网名推荐系统
  • (幽默漫画)有个程序员老公,是怎样的体验?
  • (转) 深度模型优化性能 调参
  • (转)c++ std::pair 与 std::make