SSH Ajax 页面缓存 页面刷新问题 以及缓存技术讲解

来源:互联网 发布:阿里云配置svn服务器 编辑:程序博客网 时间:2024/05/24 06:18

本文转自:http://ethen.iteye.com/blog/800242

    最近用SSH框架做个小应用,在页面上显示数据库的表数据,并且对每行数据都可以进行编辑和删除操作,编辑和删除提交后,利用Ajax发送请求到后台处理数据库的操作,并且更新页面的显示数据。现在问题就来了,删除提交后,页面由4条数据变为3条,但是如果按F5或手动刷新页面后,数据又变为4条,根本没变。问题出在哪里呢?

    首先:查看后台数据库,发现表里的数据确实少了一条,即删除操作是成功的,不是数据库操作的原因,那是什么问题呢?

    这时候自然地想到了缓存机制,因为应用的是SSH框架,对数据库的管理是通过Spring和Hibernate进行的,而Spring只是一个管理器而已,并非与数据库直接交流的,所以缓存机制是采用的Hibernate的缓存机制(hibernate缓存机制介绍见文章末尾),但是根据Hibernate缓存机制的描述,对程序和配置进行了检查之后,也相应的做了一些改动,但是结果并没有预想的那样,问题还是没有解决,这下彻底茫然了。

    当然思考仍在继续,我的工作也没有白做,因为前两个步骤至少证明了一个问题,那就是问题不是出现在服务器端,既然如此,那么肯定是客户端的问题了。

    直到这时,问题才初露端倪,客户端说到底就是一个浏览器,因为确定是缓存问题,那么首先就在页面上禁用缓存,设置方法如下:

      1、在服务端加 header("Cache-Control: no-cache, must-revalidate");

  2ajax发送请求前加上 xmlHttpRequest.setRequestHeader("If-Modified-Since","0");

  3ajax发送请求前加上 xmlHttpRequest.setRequestHeader("Cache-Control","no-cache");

可能不止这几种,具体的做法就是禁止缓存,或者设置内容过期时间等。当然,这时候问题就解决了。这时候回过头来看一下,这页面缓存是什么引起的呢?因为每次删除操作提交后,都是通过Ajax发送请求从数据库里直接拿数据的,应该每次都是不同的呀,为什么会有缓存现象呢?难道是Ajax的原因,就在网上搜了下Ajax缓存,这下就明白了,原来根源是在这里,先把Ajax缓存的原理贴出来:

      当Ajax第一次发送请求后,会把请求的URL和返回的响应结果保存在缓存内,当下一次调用Ajax发送相同的请求时,注意,这里相同的请求指的是URL完全相同,包括参数,浏览器就不会与服务器交互,而是直接从缓存中把数据取出来,这是为了提高页面的响应速度和用户体验。这种设计使客户端对一些静态页面内容的请求,比如图片,css文件,js脚本等,变得更加快捷,提高了页面的响应速度,也节省了网络通信资源。但是对于一些后台数据的提取时就需要做一些改变,否则虽然数据在后台已经发生改变,但是页面缓存中并没有改变,对于相同的URL,浏览器只是简单的从缓存中拿数据(有点罗嗦了)。问题找到了,怎么解决呢?其实办法很简单,只要让每次请求的URL不同就可以了。方法是在URL后面加一个参数,参数的值每次都不同,可以是一个随机数(Math.random()),也可以是当前时间(new Date().getTime()),只要满足条件即可。当然,以上这些只是针对Ajax发送的GET请求,如果你用POST方法发送请求,所有的工作都省了。但是你真的愿意这么做么?

附:hibernate缓存机制介绍(传送门:http://tenn.iteye.com/blog/120559):

Html代码  收藏代码
  1. 1:Hibernate缓存概述   
  2.   
  3.     首先在介绍Hibernate缓存之前,笔者在这里做一个小小的比喻,让大家先知道利用缓存的好处。   
  4.   
  5.     这个比喻设计的事物有四个,一个是消费者,一个是该消费者附近的电脑城,一个是联想笔记本,一个是联想公司。笔记本是现在普及的商品,消费者想要去买一台联想笔记本,大家想想看,是去附近的电脑城买得快?还是去联想公司买得快?..当然是在电脑城买得快咯,总不能跑到联想公司去买吧?在这里消费者被比做应用程序,电脑城被比做缓存,联想笔记本被比做数据,联想公司被比做数据库。正像我们比喻的那样,应用程序查找我们需要的数据是从缓存中找得快,还是去数据库找得快?答案应该就不用我讲了吧!   
  6.   
  7.     缓存是介于物理数据源与应用程序之间,是数据库数据在内存中的存放临时copy的容器,是其作用是为了减少应用程序对物理数据源访问的次数,从而提高了应用的运行性能。   
  8.   
  9.     Hibernate在进行读取数据的时候,根据缓存机制在相应的缓存中查询,如果在缓存中找到了需要的数据(我们把这称做“缓存命中”),则就直接把命中的数据作为结果加以利用,避免的了建立数据库查询的性能损耗。说白话点就是,数据放在缓存中,当应用程序还需要他们的时候,就不必再去查数据库了,根据缓存策略从内存中查找速度就会快很多了。   
  10.   
  11. 2:Hibernate缓存分类   
  12.   
  13.     Hibernate缓存我们通常分两类,一类称为一级缓存也叫内部缓存,另一类称为二级缓存。Hibernate的这两级缓存都位于持久化层,存放的都是数据库数据的拷贝,那么它们之间的区别是什么呢?为了理解二者的区别,需要深入理解持久化层的缓存的一个特性:缓存的范围。   
  14.   
  15.     缓存的范围决定了缓存的生命周期以及可以被谁访问。缓存的范围分为三类。   
  16.   
  17.     (1) 事务范围:缓存只能被当前事务访问。缓存的生命周期依赖于事务的生命周期,当事务结束时,缓存也就结束生命周期。在此范围下,缓存的介质是内存。事务可以是数据库事务或者应用事务,每个事务都有独自的缓存,缓存内的数据通常采用相互关联的的对象形式, 一级缓存就属于事务范围。   
  18.   
  19.     (2) 应用范围:缓存被应用范围内的所有事务共享。这些事务有可能是并发访问缓存,因此必须对缓存采取必要的事务隔离机制。缓存的生命周期依赖于应用的生命周期,应用结束时,缓存也就结束了生命周期,二级缓存存在于应用范围。   
  20.   
  21.     (3) 集群范围:在集群环境中,缓存被一个机器或者多个机器的进程共享。缓存中的数据被复制到集群环境中的每个进程节点,进程间通过远程通信来保证缓存中的数据的一致性,缓存中的数据通常采用对象的松散数据形式,二级缓存也存在与应用范围。   
  22.   
  23.     注意:对大多数应用来说,应该慎重地考虑是否需要使用集群范围的缓存,因为访问它的速度不一定会比直接访问数据库数据的速度快多少,再加上集群范围还有数据同步的问题,所以应当慎用。   
  24.   
  25.     持久化层可以提供多种范围的缓存。如果在事务范围的缓存中没有查到相应的数据,还可以到应用范围或集群范围的缓存内查询,如果还是没有查到,那么只有到数据库中查询了。   
  26.   
  27. 3:Hibernate缓存运用与管理   
  28.   
  29.     本小节,我们来看看Hibernate的缓存管理,除了我们通常分的两类缓存外,笔者再介绍“查询缓存”,它依赖于二级缓存。   
  30. 内部缓存:前面我们提到内部缓存是属于事物级缓存,在正常的情况下是由Hibernate自动维护的。当然在特殊的情况下需要我们进行手动维护,Hibernate就提供了以下几种方法供开发者选择:   
  31.   
  32.     (1)Session.evict(XXX)   
  33.     将某个特定的对象从内部缓存中清除,上述的XXX 为对象的实例名。使用此方法有两种适用情形,一是在特定的操作(如批量处理),需要及时释放对象占用的内存维持系统的稳定性,笔者的关于批处理的文章中就运用了此方法,有兴趣的朋友可关注IT168“国庆加油站”技术栏目。二是不希望当前Session继续运用此对象的状态变化来同步更新数据库。 (2)Session.clear()   
  34. 清除缓存中的所有持久化对象。   
  35.     二级缓存:在第2节的论述中我们知道,二级缓存涵盖了应用范围与集群范围。这里问题就来了,我们什么情况下要使用二级缓存?如果满足以下条件,则可以将其纳入二级缓存:(1)数据不会被第三放修改   
  36.   
  37.     (2)同一数据系统经常引用   
  38.     (3)数据大小在可接受范围之内   
  39.     (4)非关键数据,或不会被并发的数据   
  40.   
  41.     Hibernate本身并不提供二级缓存的产品化实现,而是为众多支持Hibernate的第三方缓存组件提供整和接口。笔者这里仅仅介绍现在主流的EHCache,它更具备良好的调度性能。   
  42. 首先,Hibernate启用二级缓存,需要的在主配置文件hibernate.cfg.xml中配置以下参数(以EHCache为例子,使用Hibernate3   
  43.   
  44. <hibernate-configuration>   
  45. <session-factory>   
  46. …………   
  47. <property name=”hibernate.cache.provider_class”>   
  48. org.ehcache.hibernate.Provider   
  49. </property>   
  50. …………   
  51. </session-factory>   
  52. </hibernate-configuration>   
  53.   
  54. 另外还需要对ehcache.xml进行配置,这是一个单独的xml文件,示例如下:   
  55.     ehcache.xml   
  56. <defaultCache   
  57. maxElementsInMemory="10000"   
  58. //缓存中最大允许创建的对象数   
  59. eternal="false"   
  60. //缓存中对象是否为永久的,如果是,超时设置将被忽略,对象从不过期   
  61. timeToIdleSeconds="120"   
  62. //缓存数据钝化时间(设置对象在它过期之前的空闲时间)   
  63. timeToLiveSeconds="120"   
  64. //缓存数据的生存时间(设置对象在它过期之前的生存时间)   
  65. overflowToDisk="true"   
  66. //内存不足时,是否启用磁盘缓存   
  67. />   
  68.   
  69. 然后呢,我们还需要在映射文件中指定的映射实体的缓存同步策略(以下只列出核心配置,以供大家参考):   
  70. …………   
  71. <class name=”com.tenly.bean.Student”>   
  72. <cache usage=”read-write”>   
  73. …………   
  74. <set name=”classroom”……>   
  75. <cache usage=”read-only”>   
  76. …………   
  77. </set>   
  78. </class>   
  79. …………   
  80.   
  81.     上面提到read-write、read-only是什么?这就是缓存的同步策略,下面我们来仔细的看看Hibernate提供的几钟缓存策略:   
  82.   
  83.     (1) read-only   
  84.     只读。对于不会发生改变的数据,可使用只读型缓存。   
  85.   
  86.     (2)nonstrict-read-write   
  87.     不严格可读写缓存。如果应用程序对并发访问下的数据同步要求不是很严格的话,而且数据更新操作频率较低。采用本项,可获得良好的性能。   
  88.   
  89.     (3) read-write   
  90.     对于经常被读但很少修改的数据,可以采用这种隔离类型,因为它可以防止脏读这类的并发问题.   
  91.   
  92.     (4)transactional(事物型)   
  93.     在Hibernate中,事务型缓存必须运行在JTA事务环境中。   
  94.   
  95. 查询缓存:我们前面提到查询缓存(Query Cache)依赖二级缓存,这到底是怎么回事呢?我看看二级缓存策略的一般过程:   
  96.     (1) Hibernate进行条件查询的时候,总是发出一条select * from XXX where …(XXX为 表名,类似的语句下文统称Select SQL)这样的SQL语句查询数据库,一次获得所有的符合条件的数据对象。   
  97.   
  98.     (2) 把获得的所有数据对象根据ID放入到第二级缓存中。   
  99.   
  100.     (3) 当Hibernate根据ID访问数据对象的时候,首先从内部缓存中查找,如果在内部缓存中查不到就配置二级缓存,从二级缓存中查;如果还查不到,再查询数据库,把结果按照ID放入到缓存。   
  101.   
  102.     (4)添加数据、删除、更新操作时,同时更新二级缓存。这就是Hibernate做批处理的时候效率不高的原因,原来是要维护二级缓存消耗大量时间的缘故。   
  103.   
  104.     我们看到这个过程后,可以明显的发现什么?那就是Hibernate的二级缓存策略是针对ID查询的策略,和对象ID密切相关,那么对于条件查询就怎么适用了。对于这种情况的存在,Hibernate引入了“查询缓存”在一定程度上缓解这个问题。   
  105.   
  106.     那么我们先来看看我们为什么使用查询缓存?首先我们来思考一个问题,假如我们对数据表Student进行查询操作,查找age>20的所有学生信息,然后纳入二级缓存;第二次我们的查询条件变了,查找age>15的所有学生信息,显然第一次查询的结果完全满足第二次查询的条件,但并不是满足条件的全部数据。这样的话,我们就要再做一次查询得到全部数据才行。再想想,如果我们执行的是相同的条件语句,那么是不是可以利用之前的结果集呢?   
  107.   
  108.     Hibernate就是为了解决这个问题的而引入Query Cache的。   
  109.     查询缓存策略的一般过程如下:   
  110.     (1)Query Cache保存了之前查询的执行过的Select SQL,以及结果集等信息,组成一个Query Key。(2)当再次遇到查询请求的时候,就会根据Query Key 从Query Cache找,找到就返回。但 是两次查询之间,数据表发生数据变动的话,Hibernate就会自动清除Query Cache中对应的Query Key。   
  111.   
  112.     我们从查询缓存的策略中可以看出,Query Cache只是在特定的条件下才会发挥作用,而且要求相当严格:   
  113.    (1)完全相同的Select SQL重复执行。   
  114.    (2)重复执行期间,Query Key对应的数据表不能有数据变动(比如添、删、改操作)   
  115.     为了启用Query Cache,我们需要在hibernate.cfg.xml中进行配置,参考配置如下(只列出核心配置项):   
  116. <hibernate-configuration>   
  117. <session-factory>   
  118. …………   
  119. <property name=”hibernate.cache.user_query_cache”>true</property>   
  120. …………   
  121. </session-factory>   
  122. </hibernate-configuration>   
  123. 应用程序中必须在查询执行之前,将Query.Cacheable设置为true,而且每次都应该这样。比如:   
  124. ………   
  125. Query query=session.createQuery(hql).setInteger(0.15);   
  126. query.setCacheable(true);   
  127. ………   
0 0