精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
|
|
---|---|
作者 | 正文 |
发表时间:2010-01-28
最后修改:2010-01-28
Hibernate supports several third-party caching services. After some research, the team has concluded that Ehcache would be suitable for our requirements, namely the caching of frequently-accessed, read-only tables.
Ehcache Evaluation
I spent the last couple of days evaluating Ehcache. I installed the following products locally:
I was able to create and test a standalone application using Hibernate/Ehcache in conjunction with SQL Server 2005. I populated the SQL server database with several thousand records.
My test application reads all of the records in the database in several ways:
1. Directly by primary key. 2. Using a single Query to return a large result set. 3. Using a series of individual Queries that return small result sets.
I used the test program to analyze performance with and without caching. Caching resulted in substantial improvement; however, I learned that optimal performance requires that:
1. Entities referenced in the transaction are designated as cached. 2. Associations that are traversed are designated as cached. 3. Queries are cached.
The remainder of this document details the installation and configuration steps needed to enable caching in the Dialogs product.
InstallationIt is necessary to add the Ehcache JAR to the classpath. Ehcache JAR is bundled inside the Hibernate distribution JAR in the /optional/ehcache folder:
Ehcache requires additional third-party JARs; however, it is likely that these additional JARs are already on the Dialogs classpath because they are also required by Hibernate and/or Spring Framework.
Here is the complete set of JARs I used for my local tests:
ConfigurationSuccessful implementation of Ehcache requires that:
Configuring Hibernate for CachingHibernate must be configured to enable caching. This can be done either through parameters in hibernate.properties or through alternative approaches offered by Spring Framework.
Regardless of how they are defined, the following additional properties must be added to enable caching:
cache.provider_class=org.hibernate.cache.EhCacheProvider hibernate.cache.use_query_cache=true
The following properties are optional, but I recommend that we add them for testing:
hibernate.generate_statistics=true hibernate.cache.use_structured_entries=true show_sql=true
In particular, property show_sql=true is helpful for tracing cache behavior. This setting causes Hibernate to display all SQL statements on the system console.
Configuring ehcache.xmlDocument ehcache.xml contains caching policies. For example, it allows you to specify how many instances of a given entity will be retained in cache, the conditions that will cause those instances to be evicted.
To obtain optimal caching performance, this file may need to be tuned over time, and its contents may vary based on individual customers’ needs to balance memory utilization and performance.
Here is a minimal ehcache.xml file to start:
<ehcache> <defaultCache maxElementsInMemory="10000" eternal="true" overflowToDisk="false" memoryStoreEvictionPolicy="FIFO" /> </ehcache>
This minimal setup will apply the same caching rules to all entities. Depending on the results of our testing, we may later enhance the ehcache.xml file with one entry for each reference class to be cached to allow granular control
Caching EntitiesModel classes can be divided into two broad categories as depicted below:
For the purpose of this initial exercise, caching should be limited to reference entities, which are read-only from the standpoint of the transaction being optimized. All reference entities that are accessed directly or indirectly via method QuestionnarieManager findDisplayQuestion should be designated as cached, particularly the following entities:
After reviewing the java logic and the imbedded queries, I believe that this is the complete set of reference entities addressed in method findDisplayQuestion. If there are any others that have escaped my attention, please designate them as cached as well.
Some of the queries in findDisplayQuestion join reference and instance tables. Instance tables that are joined in this manner include:
In this initial exercise, we intend to avoid caching instance entities . We may at a later time decide to enable caching instance entities, but read-write caching will require more research and testing.
In order to designate an entity as cached, it is necessary to add annotation @Cache to the model class definition. Example annotation for entity Questionnaire:
@Entity @Table(name = "q_dialog", uniqueConstraints...) @Cache(usage = CacheConcurrencyStrategy.READ_ONLY) public class Questionnaire extends QuestionBase implements Serializable
Usage CacheConcurrencyStrategy.READ_ONLY should be applied in all cases. This annotation will cause Hibernate/Ehcache to cache the entity.
Caching CollectionsEhcache has the ability to cache association collections, namely those collections that implement 1-to-many or many-to-many associations between entities. When associations are cached, the Ehcache will bypass the database when an association getter method is called; otherwise, invoking an association get method will trigger in a database SELECT.
I recommend that we designate the following associations as cached:
To enable collection caching, it is necessary to add annotation @Cache to the “getter” method for the association collection. Example for entity Questionnaire:
@OneToMany(cascade = CascadeType.ALL, fetch ...) @OrderBy("sequence asc") @Cache(usage = CacheConcurrencyStrategy.READ_ONLY) public Set<QuestionSetInNaire> getQuestionSetsInNaire()
This change will cause Ehcache to cache collection Questionnaire questionSetInNaire.
Caching QueriesFor our initial effort, I recommend that we cache all queries and verify that there are no adverse effects. In my testing, Query caching made a significant difference.
In order for Hibernate to cache a query it is necessary for the Hibernate Query instance to be flagged as cacheable via method Query.setCacheable(true).
I’ve learned that Spring Framework’s HibernateTemplate doesn’t enable caching by default. Some Google searches yielded this solution:
<bean id="hibernateTemplate" class="org.springframework.orm.hibernate3.HibernateTemplate"> <property name="sessionFactory"> <ref bean="sessionFactory" /> </property> <property name="cacheQueries"> <value>true</value> </property> </bean>
I believe that these specifications will cause the HibernateTemplate class to generate cached Query instances This should effectively enable query caching systemwide.
My knowledge of Spring Framework is limited, so please take this recommendation with a grain of salt. There are a variety of approaches to solve this problem. Whichever approach you ultimately use, please verify that Hibernate Query (i.e., QueryImpl) instances are in fact being created with the cacheable property set to true . You should be able to verify this by single-stepping through the HibernateTemplate with a debugger.
TestingYou can verify that caching is taking effect in a number of ways. When caching is enabled, you should notice a reduction in the number of SQL statements displayed on the server console via Hibernate property show_sql=true .
Alternatively, if you have a profiling tool such as YourKit, you can monitor the SQL that is sent to the database. We’re planning to perform such testing over the wire once the caching enhancements are deployed on the US Dialogs Server.
You can also verify that caching is in effect by analyzing data in the performance log:
1. Invoke a test questionnaire using the web client. 2. Enter any response to the first question on the test questionnaire. 3. Change the original response to something different. 4. Reinstate the original response.
Check the log file. If caching has been successfully enabled, there should be a measurable drop in the number of milliseconds required to find the display question (log entry [Finding display question]). Our most recent tests, show in the range of 250-300 milliseconds.We hope that caching will cause this number to drop significantly. Trial1,hibernate.properties #hibernate.dialect =org.hibernate.dialect.OracleDialect hibernate.dialect = org.hibernate.dialect.PostgreSQLDialect #hibernate.dialect = org.hibernate.dialect.SQLServerDialect #hibernate.hbm2ddl.auto = update #hibernate.generate_statistics = false #hibernate.show_sql =false hibernate.cache.provider_class=org.hibernate.cache.EhCacheProvider hibernate.cache.use_query_cache=true hibernate.generate_statistics=true hibernate.show_sql =false hibernate.cache.use_structured_entries=true 2,ehcache.xml
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="ehcache.xsd" updateCheck="true" monitoring="autodetect"> <!-- Mandatory Default Cache configuration. These settings will be applied to caches created programmtically using CacheManager.add(String cacheName). The defaultCache has an implicit name "default" which is a reserved cache name. --> <defaultCache maxElementsInMemory="10000" eternal="true" overflowToDisk="false" memoryStoreEvictionPolicy="FIFO"/> </ehcache> 3,model example,Questionnaire.java
@NamedQueries( { @NamedQuery(name = "Questionnaire.findMaxRefId", query = "select max(qn.refId) from Questionnaire qn"), @NamedQuery(name = "Questionnaire.findMaxVersionByRefId", query = "select max(qn.version) from Questionnaire qn where qn.refId = :refId") }) @Entity @Table(name = "q_dialog", uniqueConstraints = { @UniqueConstraint(columnNames = { "ID", "VERSION" }) }) @Cache(usage = CacheConcurrencyStrategy.READ_ONLY) public class Questionnaire extends QuestionBase implements Serializable { ............................................... } 4,Query cache,
Old:
public String getResponseLabel(String value) { List<ResponseEntry> lresponseEntity = this.getHibernateTemplate().find("from ResponseEntry as a"); for (int i = 0; i < lresponseEntity.size(); i++) { ResponseEntry r = lresponseEntity.get(i); if (r.getValue().equals(value)) { return r.getLabel(); } } return ""; } New:
public String getResponseLabel(String value) { String sql = "from ResponseEntry as a"; Query query = this.getSession().createQuery(sql); query.setCacheable(true); List<ResponseEntry> lresponseEntity = query.list(); for (int i = 0; i < lresponseEntity.size(); i++) { ResponseEntry r = lresponseEntity.get(i); if (r.getValue().equals(value)) { return r.getLabel(); } } return ""; } 5,Depth Optimization
Old:
public DisplayFlagRecord findByPageQuestionAndDialogInstance(QuestionInSet pageQuestion, Dialog dialogInstance) { String hql = "from DisplayFlagRecord where pageQuestion = :pageQuestion and dialogInstance = :dialogInstance"; Query query = this.getSession().createQuery(hql); query.setCacheable(true); List<DisplayFlagRecord> records = query.setEntity("pageQuestion", pageQuestion).setEntity("dialogInstance", dialogInstance).list(); if (records != null && records.size()>0) { return records.get(0); } return null; } New:
public DisplayFlagRecord findByPageQuestionAndDialogInstance(QuestionInSet pageQuestion, Dialog dialogInstance) { Iterator listDisplayFlagRecords = dialogInstance.getDisplayFlagRecords().iterator(); while (listDisplayFlagRecords.hasNext()) { DisplayFlagRecord lDisplayFlagRecord = (DisplayFlagRecord) listDisplayFlagRecords.next(); if (lDisplayFlagRecord.getPageQuestion() == pageQuestion) return lDisplayFlagRecord; } // Response could not be found: return null; } private List<Response> findRespose(Long pageInQuestionId, Set aSetResponses,String responseValue) { List<Response> lResponses = new ArrayList(); Iterator listResponses = aSetResponses.iterator(); while (listResponses.hasNext()) { Response lResponse = (Response) listResponses.next(); if (lResponse.getQuestion().getId() == pageInQuestionId && lResponse.getValue().equals(responseValue)){ lResponses.add(lResponse); } } // Response could not be found: return lResponses; }
If
any
question I will put in this thread.
声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |
浏览 3438 次