1、缓存:缓存是什么,解决什么问题?

位于速度相差较大的两种硬件/软件之间的,用于协调两者数据传输速度差异的结构,均可称之为 Cache(摘自Robbin的《缓存技术浅谈》)。目的:让数据更接近于应用程序,协调速度不匹配,使访问速度更快。(请参考http://baike.baidu.com/view/907.htm 了解更多缓存知识)

高速缓存不属于Hibernate等,属于独立产品或框架,可单独使用。

常见缓存算法:

a)         LFU(Least Frequently Used):最近不常被使用(命中率低),一定时间段内使用次数最少的

b)        LRU(Least Recently Used):最近很少使用(LinkedHashMap),没有被使用时间最长的

c)        FIFO(First In First Out):先进先出

2、缓存策略

1.对象缓存

2.查询缓存

3.页面缓存

1.动态页面缓存

2.Servlet缓存

3.页面片段缓存

3、缓存分类

1.         Web缓存:

i.              浏览器缓存:ajax(在客户端缓存)、HTTP协议

ii.          代理服务器缓存

2.       操作系统缓存:如用于减少磁盘操作

3.        数据库缓存:

i.              结果缓存:

ii.             排序缓存

iii.            插入缓存

iv.            日志缓存

v.              ………………

4.         应用程序缓存

i.              对象缓存

ii.              查询缓存

iii.              页面缓存

1.         动态页面静态化:网页静态化、独立图片服务器

2.         页面局部缓存:

3.        请求回应缓存:

4、常见Java缓存框架

?           EHCache

?           OSCache

?           JBossCache

?           SwarmCache

5、通用缓存产品

?           Memcached:在大规模互联网应用下使用,可用于分布式环境,每秒支撑1.5万~2万次请求

?           Tokyo Tyrant:兼容memcached协议,可以持久化存储,支持故障切换,对缓存服务器有高可靠性要求可以使用,每秒支撑0.5万~0.8万次请求

6、基于Web应用的缓存应用场景:

(摘自bluedavy的《大型网站架构演化》

8、缓存实战:

8.4、ORM缓存

8.4.1、目的:

Hibernate缓存:使当前数据库状态的表示接近应用程序,要么在内存中,要么在应用程序服务器机器的磁盘上。高速缓存是数据的一个本地副本,处于应用程序和数据库之间,可用来避免数据库的命中。

8.4.2、避免数据库命中:

应用程序根据标识符到缓存查,有就返回,没有再去数据库.

8.4.3、ORM缓存分类

一级缓存、二级缓存

8.4.4、缓存范围

1、事务范围高速缓存,对应于一级缓存(单Session)

2、过程(JVM)范围高速缓存,对应于二级缓存(单SessionFactory)

3、集群范围高速缓存,对应于二级缓存(多SessionFactory)

8.4.5、缓存哪些数据

1、很少改变的数据;

2、不重要的数据,如论坛帖子,无需实时的数据;

3、应用程序固有的而非共享的。

4、读大于写有用

8.4.6、Hibernate缓存架构

图摘自《Hibernate in Action》

?           Hibernate中的二级缓存是可插拔的。

?           Hibernate二级缓存支持对象缓存、集合缓存、查询结果集缓存,对于查询结果集缓存可选。

?           查询缓存:需要两个额外的物理高速缓存区域:一个用于存放查询的结果集;另一个用于存储表上次更新的时间戳

8.4.6.2、高速缓存实战(ehcache)

8.4.6.2.1、全局配置(hibernate.cfg.xml)

Java代码  
  1. <!-- 开启二级缓存 -->
  2. <property name="hibernate.cache.use_second_level_cache">true</property>
  3. <!-- 开启查询缓存 -->
  4. <property name="hibernate.cache.use_query_cache">true</property>
  5. <!-- 二级缓存区域名的前缀 -->
  6. <!--<property name="hibernate.cache.region_prefix">h3test</property>-->
  7. <!-- 高速缓存提供程序 -->
  8. <property name="hibernate.cache.region.factory_class">
  9. net.sf.ehcache.hibernate.EhCacheRegionFactory
  10. </property>
  11. <!-- 指定缓存配置文件位置 -->
  12. <property name="hibernate.cache.provider_configuration_file_resource_path">
  13. ehcache.xml
  14. </property>
  15. <!-- 强制Hibernate以更人性化的格式将数据存入二级缓存 -->
  16. <property name="hibernate.cache.use_structured_entries">true</property>
  17. <!-- Hibernate将收集有助于性能调节的统计数据 -->
  18. <property name="hibernate.generate_statistics">true</property>

8.4.6.2.2、ehcache配置(ehcache.xml)

Java代码  
  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <ehcache name="h3test">
  3. <defaultCache
  4. maxElementsInMemory="100"
  5. eternal="false"
  6. timeToIdleSeconds="1200"
  7. timeToLiveSeconds="1200"
  8. overflowToDisk="false">
  9. </defaultCache>
  10. </ehcache>

8.4.6.2.3、实体只读缓存

1、修改FarmModel.hbm.xml,添加如下红色部分配置,表示实体缓存并只读

Java代码  
  1. <hibernate-mapping>
  2. <class name="cn.javass.h3test.model.FarmModel" table="TBL_FARM">
  3. <cache usage="read-only"/>
  4. ……
  5. </hibernate-mapping>

2、测试代码

Java代码  
  1. public static void readonlyTest() {
  2. SessionFactory sf =
  3. new Configuration().configure().buildSessionFactory();
  4. Session session1 = sf.openSession();
  5. Transaction t1 = session1.beginTransaction();
  6. //确保数据库中有标识符为1的FarmModel
  7. FarmModel farm = (FarmModel) session1.get(FarmModel.class, 1);
  8. //如果修改将报错,只读缓存不允许修改
  9. //farm.setName("aaa");
  10. t1.commit();
  11. session1.close();
  12. Session session2 = sf.openSession();
  13. Transaction t2 = session2.beginTransaction();
  14. farm = (FarmModel) session2.get(FarmModel.class, 1);
  15. t2.commit();
  16. session2.close();
  17. sf.close();
  18. }

?           只读缓存不允许更新,将报错Can't write to a readonly object。

?           允许新增,(现在2。0 新增直接添加到二级缓存)

8.4.6.2.4、实体非严格读/写缓存

1、修改FarmModel.hbm.xml,添加如下红色部分配置,表示实体缓存并非严格读/写

Java代码  
  1. <hibernate-mapping>
  2. <class name="cn.javass.h3test.model.FarmModel" table="TBL_FARM">
  3. <cache usage="nonstrict-read-write"/>
  4. ……
  5. </hibernate-mapping>

2、测试代码

Java代码  
  1. public static void nonstrictReadWriteTest () {
  2. SessionFactory sf =
  3. new Configuration().configure().buildSessionFactory();
  4. Session session1 = sf.openSession();
  5. Transaction t1 = session1.beginTransaction();
  6. //确保数据库中有标识符为1的FarmModel
  7. FarmModel farm = (FarmModel) session1.get(FarmModel.class, 1);
  8. t1.commit();
  9. session1.close();
  10. Session session2 = sf.openSession();
  11. Transaction t2 = session2.beginTransaction();
  12. farm = (FarmModel) session2.get(FarmModel.class, 1);
  13. t2.commit();
  14. session2.close();
  15. sf.close();
  16. }

?           允许更新,更新后缓存失效,需再查询一次。

       ?           允许新增,新增记录自动加到二级缓存中。

       ?           整个过程不加锁,不保证。

8.4.6.2.5、实体读/写缓存

1、修改FarmModel.hbm.xml,添加如下红色部分配置,表示实体缓存并读/写

Java代码  
  1. <hibernate-mapping>
  2. <class name="cn.javass.h3test.model.FarmModel" table="TBL_FARM">
  3. <cache usage="read-write"/>
  4. ……
  5. </hibernate-mapping>

2、测试代码

Java代码  
  1. public static void readWriteTest() {
  2. SessionFactory sf =
  3. new Configuration().configure().buildSessionFactory();
  4. Session session1 = sf.openSession();
  5. Transaction t1 = session1.beginTransaction();
  6. //确保数据库中有标识符为1的FarmModel
  7. FarmModel farm = (FarmModel) session1.get(FarmModel.class, 1);
  8. farm.setName("as");
  9. t1.commit();
  10. session1.close();
  11. Session session2 = sf.openSession();
  12. Transaction t2 = session2.beginTransaction();
  13. farm = (FarmModel) session2.get(FarmModel.class, 1);
  14. t2.commit();
  15. session2.close();
  16. sf.close();
  17. }

?          允许更新,更新后自动同步到缓存。

?          允许新增,新增记录后自动同步到缓存。

?           保证read committed隔离级别及可重复读隔离级别(通过时间戳实现)

?           整个过程加锁,如果当前事务的时间戳早于二级缓存中的条目的时间戳,说明该条目已经被别的事务修改了,此时重新查询一次数据库,否则才使用缓存数据,因此保证可重复读隔离级别。

8.4.6.2.6、实体事务缓存

需要特定缓存的支持和JTA事务支持,此处不演示。

8.4.6.2.7、集合缓存

此处演示读/写缓存示例,其他自测

1、修改FarmModel.hbm.xml,添加如下红色部分配置,表示实体缓存并读/写

Java代码  
  1. <hibernate-mapping>
  2. <class name="cn.javass.h3test.model.UserModel" table="TBL_USER">
  3. <cache usage="read-write" />
  4. <set name="farms" cascade="all" inverse="true" lazy="false">
  5. <cache usage="read-write"/>
  6. <key column="fk_user_id"/>
  7. <one-to-many class="cn.javass.h3test.model.FarmModel"/>
  8. </set>
  9. </class>
  10. </hibernate-mapping>

2、测试代码

Java代码  
  1. public static void collectionReadWriteTest() {
  2. SessionFactory sf =
  3. new Configuration().configure().buildSessionFactory();
  4. Session session1 = sf.openSession();
  5. Transaction t1 = session1.beginTransaction();
  6. //确保数据库中有标识符为118的UserModel
  7. UserModel user = (UserModel) session1.get(UserModel.class, 118);
  8. user.getFarms();
  9. t1.commit();
  10. session1.close();
  11. Session session2 = sf.openSession();
  12. Transaction t2 = session2.beginTransaction();
  13. user = (UserModel) session2.get(UserModel.class, 118);
  14. user.getFarms();
  15. t2.commit();
  16. session2.close();
  17. sf.close();
  18. }

?           和实体并发策略有相同含义;

?           但集合缓存只缓存集合元素的标识符,在二级缓存中只存放相应实体的标识符,然后再通过标识符去二级缓存查找相应的实体最后组合为集合返回。

8.4.6.2.8、查询缓存

1、保证全局配置中有开启了查询缓存。

2、修改FarmModel.hbm.xml,添加如下红色部分配置,表示实体缓存并读/写

Java代码  
  1. <hibernate-mapping>
  2. <class name="cn.javass.h3test.model.FarmModel" table="TBL_FARM">
  3. <cache usage="read-write"/>
  4. ……
  5. </hibernate-mapping>

  3、测试代码

Java代码  
  1. public static void queryCacheTest() {
  2. SessionFactory sf =
  3. new Configuration().configure().buildSessionFactory();
  4. Session session1 = sf.openSession();
  5. Transaction t1 = session1.beginTransaction();
  6. Query query = session1.createQuery("from FarmModel");
  7. //即使全局打开了查询缓存,此处也是必须的
  8. query.setCacheable(true);
  9. List<FarmModel> farmList = query.list();
  10. t1.commit();
  11. session1.close();
  12. Session session2 = sf.openSession();
  13. Transaction t2 = session2.beginTransaction();
  14. query = session2.createQuery("from FarmModel");
  15. //即使全局打开了查询缓存,此处也是必须的
  16. query.setCacheable(true);
  17. farmList = query.list();
  18. t2.commit();
  19. session2.close();
  20. sf.close();
  21. }

?           和实体并发策略有相同含义;

      ?           和集合缓存类似,只缓存集合元素的标识符,在二级缓存中只存放相应实体的标识符,然后再通过标识符 去二级缓存查找相应的实体最后组合为集合返回。

 

8.4.6.2.9、高速缓存区域

Hibernate在不同的高速缓存区域保存不同的类(实体)/集合,如果不配置区域默认都保存到“默认缓存”(defaultCache)中。

  每一个区域可以设置过期策略、缓存条目大小等等。

  对于类缓存,默认区域名是全限定类名,如cn.javass.h3test.model.UserModel。

  对于集合而言,默认区域名是全限定类名+属性名,如cn.javass.….UserModel.farms。

  可通过hibernate.cache.region_prefix指定特定SessionFactory的区域前缀,如前缀是h3test,则如类缓存的区域名就是h3test. cn.javass.h3test.model.UserModel。如果应用程序使用多个SessionFactory 这可能是必须的。

 

    可通过<cache usage="read-write" region="区域名"/>自定义区域名,不过默认其实就可以了。

8.4.6.2.10、ehcache配置详解:

1、默认cache:如果没有对应的特定区域的缓存,就使用默认缓存。

Java代码  
  1. <defaultCache
  2. maxElementsInMemory="100"
  3. eternal="false"
  4. timeToIdleSeconds="1200"
  5. timeToLiveSeconds="1200"
  6. overflowToDisk="false">
  7. </defaultCache>

2、指定区域cache:通过name指定,name对应到Hibernate中的区域名即可。

Java代码  
  1. <cache name="cn.javass.h3test.model.UserModel"
  2. maxElementsInMemory="100"
  3. eternal="false"
  4. timeToIdleSeconds="1200"
  5. timeToLiveSeconds="1200"
  6. overflowToDisk="false">
  7. </cache>

3、cache参数详解:

name:指定区域名

maxElementsInMemory :缓存在内存中的最大数目

maxElementsOnDisk:缓存在磁盘上的最大数目

eternal :缓存是否持久

overflowToDisk : 硬盘溢出数目

timeToIdleSeconds :当缓存条目闲置n秒后销毁

timeToLiveSeconds :当缓存条目存活n秒后销毁

memoryStoreEvictionPolicy:缓存算法,有LRU(默认)、LFU、FIFO

  4、StandardQueryCache

用于查询缓存使用,如果指定了该缓存,那么查询缓存将放在该缓存中。

Java代码  
  1. <cache
  2. name="org.hibernate.cache.StandardQueryCache"
  3. maxElementsInMemory="5"
  4. eternal="false"
  5. timeToLiveSeconds="120"
  6. overflowToDisk="true"/>

  如果不给查询设置区域名默认缓存到这,可以通过“query.setCacheRegion("区域名");”来设置查询的区域名。

5、UpdateTimestampsCache

    时间戳缓存,内部使用,用于保存最近更新的表的时间戳,这是非常重要的,无需失效,关闭时间戳缓存区域的过期时间。

Java代码  
  1. <cache
  2. name="org.hibernate.cache.UpdateTimestampsCache"
  3. maxElementsInMemory="5000"
  4. eternal="true"
  5. overflowToDisk="true"/>

    Hibernate使用时间戳区域来决定被高速缓存的查询结果集是否是失效的。当你重新执行了一个启用了高速缓存的查询时,Hibernate就在时间戳缓存中查找对被查询的(几张)表所做的最近插入、更新或删除的时间戳。如果找到的时间戳晚于高速缓存查询结果的时间戳,那么缓存结果将被丢弃,重新执行一次查询。

 

8.4.6.2.11、什么时候需要查询缓存

  大多数时候无法从结果集高速缓存获益。必须知道:每隔多久重复执行同一查询。

  对于那些查询非常多但插入、删除、更新非常少的应用程序来说,查询缓存可提升性能。但写入多查询少的没有用,总失效。

 

8.4.6.2.12、管理一级缓存

无论何时,当你给save()、update()或 saveOrUpdate()方法传递一个对象时,或使用load()、 get()、list()、iterate() 或scroll()方法获得一个对象时, 该对象都将被加入到Session的内部缓存中。

当随后flush()方法被调用时,对象的状态会和数据库取得同步。 如果你不希望此同步操作发生,或者你正处理大量对象、需要对有效管理内存时,你可以调用evict() 方法,从一级缓存中去掉这些对象及其集合。

ScrollableResult cats = sess.createQuery("from Cat as cat").scroll(); //a huge result set

while ( cats.next() ) {

    Cat cat = (Cat) cats.get(0);

    doSomethingWithACat(cat);

    sess.evict(cat);

}

Session还提供了一个contains()方法,用来判断某个实例是否处于当前session的缓存中。

如若要把所有的对象从session缓存中彻底清除,则需要调用Session.clear()。

 

CacheMode参数用于控制具体的Session如何与二级缓存进行交互。

CacheMode.NORMAL - 从二级缓存中读、写数据。

CacheMode.GET - 从二级缓存中读取数据,仅在数据更新时对二级缓存写数据。

CacheMode.PUT - 仅向二级缓存写数据,但不从二级缓存中读数据。

CacheMode.REFRESH - 仅向二级缓存写数据,但不从二级缓存中读数据。通过 hibernate.cache.use_minimal_puts的设置,强制二级缓存从数据库中读取数据,刷新缓存内容。

 

8.4.6.2.12、管理二级缓存

对于二级缓存来说,在SessionFactory中定义了许多方法, 清除缓存中实例、整个类、集合实例或者整个集合。

sessionFactory.evict(Cat.class, catId); //evict a particular Cat

sessionFactory.evict(Cat.class);  //evict all Cats

sessionFactory.evictCollection("Cat.kittens", catId); //evict a particular collection of kittens

sessionFactory.evictCollection("Cat.kittens"); //evict all kitten collections

sessionFactory.evictQueries()//evict all queries

8.4.6.2.13、监控二级缓存

如若需要查看二级缓存或查询缓存区域的内容,你可以使用统计(Statistics) API。

通过sessionFactory.getStatistics();获取Hibernate统计信息。

此时,你必须手工打开统计选项。

hibernate.generate_statistics true

hibernate.cache.use_structured_entries true

具体详见“hibernate监控.rar”(需要自己稍微改改才能用)

需要修改head.jsp中的如下代码获取sessionFactory

Java代码  
  1. if(sessionFactory == null) {
  2. WebApplicationContext applicationContext = WebApplicationContextUtils.getWebApplicationContext(request.getSession().getServletContext());
  3. sessionFactory = (SessionFactory)applicationContext.getBean("userSessionFactory");
  4. }

参考资料:

Robbin的《缓存技术浅谈

百度百科的高速缓存知识 http://baike.baidu.com/view/907.htm

bluedavy的《大型网站架构演化》http://www.blogjava.net/BlueDavy/archive/2008/09/03/226749.html

《Hibernate in Action》

Hibernate 之Hibernate缓存的更多相关文章

  1. Hibernate的一级缓存

    Hibernate的一级缓存 什么是缓存:缓存将数据库/硬盘上文件中数据,放入到缓存中(就是内存中一块空间).当再次使用的使用,可以直接从内存中获取 缓存的好处:提升程序运行的效率.缓存技术是Hibe ...

  2. Hibernate之二级缓存

                                                            Hibernate之二级缓存 一.简介 Gaving King曾经对别人说,hibern ...

  3. hibernate(九) 二级缓存和事务级别详讲

    序言 这算是hibernate的最后一篇文章了,下一系列会讲解Struts2的东西,然后说完Struts2,在到Spring,然后在写一个SSH如何整合的案例.之后就会在去讲SSM,在之后我自己的个人 ...

  4. hibernate的二级缓存

    缓存(Cache): 计算机领域非常通用的概念.它介于应用程序和永久性数据存储源(如硬盘上的文件或者数据库)之间,其作用是降低应用程序直接读写永久性数据存储源的频率,从而提高应用的运行性能.缓存中的数 ...

  5. hibernate(二)一级缓存和三种状态解析

    序言 前一篇文章知道了什么是hibernate,并且创建了第一个hibernate工程,今天就来先谈谈hibernate的一级缓存和它的三种状态,先要对着两个有一个深刻的了解,才能对后面我要讲解的一对 ...

  6. Hibernate中一级缓存和二级缓存使用详解

    一.一级缓存二级缓存的概念解释 (1)一级缓存就是Session级别的缓存,一个Session做了一个查询操作,它会把这个操作的结果放在一级缓存中,如果短时间内这个 session(一定要同一个ses ...

  7. hibernate中的缓存机制

    一.为什么要用Hibernate缓存? Hibernate是一个持久层框架,经常访问物理数据库. 为了降低应用程序对物理数据源访问的频次,从而提高应用程序的运行性能. 缓存内的数据是对物理数据源中的数 ...

  8. 不要依赖hibernate的二级缓存

    一.hibernate的二级缓存   如果开启了二级缓存,hibernate在执行任何一次查询的之后,都会把得到的结果集放到缓存中,缓存结构可以看作是一个hash table,key是数据库记录的id ...

  9. Hibernate学习之缓存机制

    转自:http://www.cnblogs.com/xiaoluo501395377/p/3377604.html 一.N+1问题 首先我们来探讨一下N+1的问题,我们先通过一个例子来看一下,什么是N ...

  10. SSH整合缓存之-Memcached作为hibernate的二级缓存

    Hibernate本身不提供二级缓存,所以需要使用第三方插件来作为二级缓存:本次使用memcached作为Hiberbate的二级缓存:添加步骤如下: 一.需要安装memcached服务端 1. 下载 ...

随机推荐

  1. 基于PaddleNLP信息抽取,uie微调打造自己专属的信息抽取模型

    基于PaddleNLP信息抽取,uie微调打造自己专属的信息抽取模型 UIE模型简介 UIE优势 应用示例 UIE开箱即用 UIE适用抽取示例 命名实体识别(Named Entity Recognit ...

  2. 【图文安装教程】在docker中安装kibana

    在上一篇中,我们已经在docker里面安装了ES. kibana可以给我们提供一个elasticsearch的可视化界面,便于我们学习. 所以,本篇咱们就在docker里面安装kibana图文教程: ...

  3. 记 Android 部分布局忽然无法显示

    总结:这是一个一开始方向错误的问题 某次,APK在测试手机上正常使用,故换了个荣耀X20的设备,想着兼容性应该没有问题, 结果,忽然发现A页面,一个底部布局无法显示,其它页面这个布局可以显示(使用的i ...

  4. mysql vs mongodb

    Comments MongoDB 是NoSQL 数据库,适合存JSON格式数据,MySQL是关系型数据库,适合存table格式数据 MongoDB扩展性更好,MySQL支持主从和cluster但是感觉 ...

  5. (零) React Native 项目开发拾遗

    一位离职的前端同事,最近接了个 React Native 的活儿,遇到许多搞不定的问题,于是找到我帮忙"补课"(没有系统的学习 React Native,也不具备原生 Androi ...

  6. reinstall nodejs 后跑不到 command

    现象 : node -v 可以跑 , ng new 这些就跑不到 (确保已经安装了 global cli) 那多半是 path 的问题 https://stackoverflow.com/questi ...

  7. Go runtime 调度器精讲(五):调度策略

    原创文章,欢迎转载,转载请注明出处,谢谢. 0. 前言 在 第四讲 我们介绍了 main goroutine 是如何运行的.其中针对 main goroutine 介绍了调度函数 schedule 是 ...

  8. python08_05day

    #!/usr/bin/python# -*- coding: UTF-8 -*-from _ast import Param #查询数据库'''import MySQLdb conn = MySQLd ...

  9. UEFI原理与编程(三)

    1 开发UEFI服务 本质Protocol 就是包含属性和函数指针的结构体,功能上来说就是提供者和使用者对服务的一种约定. 2 开发UEFI驱动 一个设备/总线驱动程序在安装时首要找到对应的硬件设备( ...

  10. TLB一致性维护

    TLB 是页表项的物理 cache,用于加速虚拟地址到物理地址的转换.CPU 在访问一个虚拟地址时,首先会在 TLB 中查找,如果找不到对应的表项,那么就称之为 TLB miss,此时就需要去内存里查 ...