性能優化之Hibernate緩存講解、應用和調優


JavaMelody——一款性能監控、調優工具,

通過它讓我覺得項目優化是看得見摸得着的,優化有了針對性。而無論是對於分布式,還是非分布,緩存是提示性能的有效工具。

數據層是EJB3.0實現的,而EJB3.0內部也是通過Hibernate實現的,而Hibernate本身提供了很好的緩存機制,我們只需要學會使用它駕馭它就夠了。

緩存的機能可以簡單理解為將從數據庫中訪問的數據放在內存中,在以后再次使用到這些數據時可以直接從內存中讀取而不必要再次訪問數據庫,盡量減少和數據庫的交互提高性能。

概念講解

在hibernate中提供了二種緩存機制:一級緩存、二級緩存,因為二級緩存策略是針對於ID查詢的緩存策略,對於條件查詢則毫無作用,為此,Hibernate提供了針對條件查詢的Query Cache(查詢緩存)。

一、一級緩存:

一級緩存是hibernate自帶的,不受用戶干預,其生命周期和session的生命周期一致,當前session一旦關閉,一級緩存就會消失,因此,一級緩存也叫session緩存或者事務級緩存,一級緩存只存儲實體對象,不會緩存一般的對象屬性,即:當獲得對象后,就將該對象緩存起來,如果在同一個session中再去獲取這個對象時,它會先判斷緩存中有沒有這個對象的ID,如果有,就直接從緩存中取出,否則,則去訪問數據庫,取了以后同時會將這個對象緩存起來。

二、二級緩存:

二級緩存也稱為進程緩存或者sessionFactory級的緩存,它可以被所有的session共享,二級緩存的生命周期和sessionFactory的生命周期一致,二級緩存也是只存儲實體對象

二級緩存的一般過程如下:

①:條件查詢的時候,獲取查詢到的實體對象

②:把獲得到的所有數據對象根據ID放到二級緩存中

③:當Hibernate根據ID訪問數據對象時,首先從sesison的一級緩存中查,查不到的時候如果配置了二級緩存,會從二級緩存中查找,如果還查不到,再查詢數據庫,把結果按照ID放入到緩存中

④:進行delete、update、add操作時會同時更新緩存

三、查詢緩存:

查詢緩存是對普通屬性結果集的緩存,對實體對象的結果集只緩存id,對於經常使用的查詢語句,如果啟用了查詢緩存,當第一次執行查詢語句時,Hibernate會把查詢結果存放在二級緩存中,以后再次執行該查詢語句時,只需從緩存中獲得查詢結果,從而提高查詢性能,查詢緩存中以鍵值對的方式存儲的,key鍵為查詢的條件語句(具體的key規則應該是:類名+方法名+參數列表),value為查詢之后等到的結果集的ID列表

查詢緩存的一般過程如下:

①:Query Cache保存了之前查詢執行過的SelectSQL,以及結果集等信息組成一個Query Key

②:當再次遇到查詢請求的時候,就會根據QueryKey從QueryCache中找,找到就返回,但當數據表發生數據變動的話,hbiernate就會自動清除QueryCache中對應的Query Key

我們從查詢緩存的策略中可以看出,Query Cache只有在特定的條件下才會發揮作用,而且要求相當嚴格:

①:完全相同的SelectSQL重復執行

②:重復執行期間,QueryKey對應的數據表不能有數據變動

啟用緩存的配置

EJB中配置查詢緩存和二級緩存

1、在persistence.xml中啟用緩存

<persistence-unitname="gxpt-qx-entity" transaction-type="JTA" >
<!--對jpa進行性能測試 -->
<provider>net.bull.javamelody.JpaPersistence</provider>
 
                 <jta-data-source>java:/MySqlDS</jta-data-source>
                 <!--<jta-data-source>java:/MyOracleDS</jta-data-source>   -->
                 
                 <properties> 
                          <!-- <propertyname="hibernate.dialect"value="org.hibernate.dialect.Oracle10gDialect"/> -->
                          <propertyname="hibernate.dialect"value="org.hibernate.dialect.MySQLDialect"/>
<propertyname="hibernate.hbm2ddl.auto" value="update" />
<propertyname="hibernate.show_sql" value="true" />
 
<!--指定二級緩存產品的提供商 -->
<propertyname="hibernate.cache.provider_class"
value="net.sf.ehcache.hibernate.SingletonEhCacheProvider"/>
<!--                        <propertyname="hibernate.cache.region.factory_class"
value="org.hibernate.cache.ehcache.EhCacheRegionFactory"/>-->
<!--開啟二級緩存 -->
<propertyname="hibernate.cache.use_second_level_cache"value="true"/>
<!--開啟查詢緩存 -->
<propertyname="hibernate.cache.use_query_cache"value="true"/> 
<!--指定緩存配置文件位置   -->
<propertyname="hibernate.cache.provider_configuration_file_resource_path"
value="/ehcache.xml"/>
 
                 </properties>
</persistence-unit>

2、通過注解指定User類使用二級緩存

@Entity
@Table(name="tq_user")
@Cache(usage=CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
publicclass User

3、開啟查詢緩存,除了在persistence.xml中有以上配置外,還需要在底層代碼手動開啟查詢緩存

query.setHint("org.hibernate.cacheable", true);

或者query.setCacheable(true);

性能測試

通過sql語句 

1、二級緩存關閉時,查詢緩存開啟和關閉情況對比*****普通屬性查詢

public String myCache() {
        List<String> strings = this.userServiceImpl
                .search("selectu.username from User u where id<4 order by id asc");
        for (String str : strings){
           System.out.println("username:" + str);
        }
 
        System.out.println("===================================");
        List<String> strings2 =this.userServiceImpl
                .search("select u.usernamefrom User u where id<4 order by id asc");
        for (String str : strings2){
           System.out.println("username:" + str);
        }
        return "/mycache";
    } 

當前二級緩存為關閉狀態,看看查詢緩存關閉時的查詢結果:

  public List<String> search(Stringhql) {
        List<String> rtnStrs = newArrayList<String>();
        try {
           Session session = this.sessionFactory.openSession();
            session.beginTransaction();
 
           Query query = session.createQuery(hql);
            //query.setCacheable(true);//手動開啟查詢緩存
            rtnStrs =(List<String>) query.list();
 
           session.getTransaction().commit();
        } catch (Exception e){
            System.out.println("DAO層根據HQL語句查詢失敗");
        }
        return rtnStrs;
    }

上面代碼中屏蔽了query.setCacheable(true)。

關閉二級緩存、關閉查詢緩存 運行如下:

\

 

開啟查詢緩存、關閉二級緩存運行如下

\

結論:對於查詢普通屬性,無論二級緩存是否開啟,只要開啟了查詢緩存,當兩次執行的sql語句相同時,第二次不會發出sql語句,直接從內存中獲取。

2、查詢緩存開啟時,二級緩存打開和關閉情況對比*******查詢實體對象

/**
     * 查詢緩存開啟,二級緩存關閉*******查詢實體對象
     *
     *運行結果:如果關閉查詢緩存和二級緩存,在兩次查詢時都發出sql語句,此時為兩條查詢語句
     *
     *運行結果:如果開啟查詢緩存,關閉二級緩存,第二次會發出根據ID查詢實體的n條查詢語句
     *
     *運行結論:第一次執行list時,會把查詢對象的ID緩存到查詢緩存中,第二次執行list時(兩次的查詢SQL語句相同),會遍歷查詢緩存中的ID到
     * (一級、二級)緩存里找實體對象, 此時沒有,則發出查詢語句到數據庫中查詢
     *
    */        
publicString mycache3() {
List<User>users1 = this.userServiceImpl.search();
           for(User u : users1) {
              System.out.println("users1:username:"+ u.getUsername());
          }
   System.out.println("===============");
 
        List<User> users2 =this.userServiceImpl.search();
        for (User u : users2) {
           System.out.println("users2:usersname:" + u.getUsername());
        }
return"/mycache";
    }

開啟查詢緩存、關閉二級緩存 運行如下:(兩次都發出sql,而且第二次發出n條語句)

\

\

 

開啟查詢緩存、關閉二級緩存 運行如下(只發出一條語句)

\

 

總結:

(1)、當只是用hibernate查詢緩存,而關閉二級緩存的時候:

①如果查詢的是部分屬性結果集,那么當第二次查詢的時候就不會發出SQL語句,直接從Hibernate查詢緩存中取數據

②如果查詢的是實體結果集(eg.from User)這個HQL,那么查詢出來的實體,首先hibernate查詢緩存存放實體的ID,第二次查詢的時候,就到hibernate查詢緩存中取出ID一條一條的到數據庫查詢,這樣將發出N條SQL語句,造成SQL泛濫。所以,在使用查詢緩存的時候,最好配合開啟二級緩存。

(2)、當開啟Hibernate查詢緩存和二級緩存的時候:

①如果查詢的是部分屬性結果集,這個和上面只用hbiernate查詢緩存而關閉二級緩存的時候一致,因為不涉及實體,不會用到二級緩存。

②如果查詢的是實體結果集,那么查詢出來的實體首先在查詢緩存中存放實體的ID,並將實體對象保存到二級緩存中,第二次查詢的時候,就到hibernate查詢緩存中取ID,根據ID去二級緩存中匹配數據,如果有數據就不會發出sql語句,如果都有,第二次查詢一條SQL語句都不會發出,直接從二級緩存中取數據。

通過Javamelody

JavaMelody能夠在運行環境中監測Java或Java EE應用程序服務器。並以圖表的形式顯示:Java內存和Java CPU使用情況,用戶Session數量,JDBC連接數,和http請求、sql請求、jsp頁面與業務接口方法(EJB3、Spring、Guice)的執行數量,平均執行時間,錯誤百分比等。

坤哥博客有介紹Java項目性能監控和調優工具-Javamelody

監控項目緩存個數

這是本次開發的權限項目中的緩存,共有12個,其中紅色部分分別為二級緩存和查詢緩存

\

對spring的監控

加緩存情況

\

不加緩存情況

\

根據統計結果,發現緩存的確可以提高性能。

但是有時候使用了緩存反而性能會降低,比如update方法,因為數據發生變更后,hibernate需要保持緩存和數據庫兩份的數據同步,所以加上緩存后,update性能降低,add、delete操作也是相同的道理。

所以緩存適用於在項目中存在大量查詢的情況,否則是沒必要適用的。

小結

在想項目之所以很吸引自己,很重要的一點是因為我們在使用各種各樣的工具,包括在此提到的緩存、javamelody,正如“君子生非異也,善假於物也”,更多工具的使用,會在后面詳細介紹。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM