自:http://blog.csdn.net/qianling3439/article/details/5772187
写这个的目的不是为了说明哪种cache的效率高,或者哪种cache更适合hibernate。只是为了阐明hibernate在使用cache时的机制,和自己碰到的实际问题。
二、hibernate二级缓存避免查询Cache需要先获得db连接
hibernate自身管理一级缓存,如果需要使用二级缓存,则要自己来实现相应的代码,这个实现起来并不复杂只需要实现
hibernate提供的相应的接口即可。我们在项目中选用了最为通用的memcached,具体配置如下:
在spring中配置hibernateProperties是增加一项配置
<prop key="hibernate.cache.provider_class">com.*.frame.cache.memcached.MemcachedCacheProvider</prop>
MemcachedCacheProvider需要实现hibernate提供的CacheProvider接口:
public class MemcachedCacheProvider implements CacheProvider {
public final static String DEFAULT_REGION_NAME="____DEFAULT_CACHE_REGION";
public void start(Properties props) throws CacheException {
CachePoolManager pool = CachePoolManager.getInstance();
}
public Cache buildCache(String name, Properties props) throws CacheException {
if(StringUtils.isEmpty(name))
name = DEFAULT_REGION_NAME;
//项目中已经实现并且再用的的Memacached工具类
MemCache mCache = (MemCache)CachePoolManager.getInstance().getCache(name);
return mCache;
}
public void stop() {
// CachePoolManager.getInstance().finalize();
}
public boolean isMinimalPutsEnabledByDefault() {
return false;
}
public long nextTimestamp() {
return Timestamper.next();
}
}
Memcached实体类也要实现hibernate提供的Cache接口:
public class MemCache implements Cache {
private static final Log log = LogFactory.getLog(MemCache.class);
private static final int SIXTY_THOUSAND_MS = 60000;
private MemCachedClient mc;
private int secondToLive;
private String cache_name;
private String poolName;
public MemCache(String poolName, String regionName, int secondToLive){
*****部分代码省略
/**
* Get an item from the cache
* @param key
* @return the cached object or <tt>null</tt>
* @throws CacheException
*/
public Object read(Object key) throws CacheException;
/**
* Get an item from the cache, nontransactionally
* @param key
* @return the cached object or <tt>null</tt>
* @throws CacheException
*/
public Object get(Object key) throws CacheException;
/**
* Add an item to the cache, nontransactionally, with
* failfast semantics
* @param key
* @param value
* @throws CacheException
*/
public void put(Object key, Object value) throws CacheException;
/**
* Add an item to the cache
* @param key
* @param value
* @throws CacheException
*/
public void update(Object key, Object value) throws CacheException;
/**
* Remove an item from the cache
*/
public void remove(Object key) throws CacheException;
/**
* Clear the cache
*/
public void clear() throws CacheException;
/**
* Clean up
*/
public void destroy() throws CacheException;
/**
* If this is a clustered cache, lock the item
*/
public void lock(Object key) throws CacheException;
/**
* If this is a clustered cache, unlock the item
*/
public void unlock(Object key) throws CacheException;
/**
* Generate a timestamp
*/
public long nextTimestamp();
/**
* Get a reasonable "lock timeout"
*/
public int getTimeout();
/**
* Get the name of the cache region
*/
public String getRegionName();
/**
* The number of bytes is this cache region currently consuming in memory.
*
* @return The number of bytes consumed by this region; -1 if unknown or
* unsupported.
*/
public long getSizeInMemory();
/**
* The count of entries currently contained in the regions in-memory store.
*
* @return The count of entries in memory; -1 if unknown or unsupported.
*/
public long getElementCountInMemory();
/**
* The count of entries currently contained in the regions disk store.
*
* @return The count of entries on disk; -1 if unknown or unsupported.
*/
public long getElementCountOnDisk();
/**
* optional operation
*/
public Map toMap();
}
至于Memcached实体类里的MemCachedClient 的实现,可以更加网上流传的不同版本进行修改和优化。
第四步,在需要使用缓存的类对应的映射文件中加入缓存策略的配置,如:<cache usage="nonstrict-read-write" />,还有其他选项read-only,read-write,transactional等。
大功告成,打开hibernate.show_sql重启应用。刷新列表,第一次看到一大堆的sql语句,再次刷新只出现两条,第三次还是两条,我们配置的二级缓存生效了。
做一次压力测试,具体压力测试的报告丢失没法拿出具体的数字。当时大概的现象是,应用响应比较慢,数据库连接几乎耗尽,队列等待情况严重。
我们知道hibernate从cache中查找的时候,先从数据库拿到相应的id然后根据id去cache中取到相应的数据后返回。压力测试只是模拟的同一个请求,意味着每次要查询的东西必然已经存在于cache里了,为什么dbConnection会不够用呢?
一个分析系统问题的利器该出场了——log4j。顺便说句,java开发不可避免的采用框架,当然有人坚持原生态,这个另说。几乎所有的框架都采用log4j输出日志,仔细分析日志不难发现很多问题。首先,设置日志级别Debug,其次去掉一些无用的信息,比如初始化、加载配置文件等等。准备工作完成后,咱们模拟用户的行为进行一次操作。这个时候输出的日志就会记录一个请求执行的所有的过滤器,拦截器,方法等堆栈信息。
[DEBUG] 2010-07-28 11:28:31 org.hibernate.transaction.JDBCTransaction - begin
[DEBUG] 2010-07-28 11:28:31 org.hibernate.jdbc.ConnectionManager - opening JDBC connection
[DEBUG] 2010-07-28 11:28:31 org.hibernate.transaction.JDBCTransaction - current autocommit status: true
[DEBUG] 2010-07-28 11:28:31 org.hibernate.transaction.JDBCTransaction - disabling autocommit
[DEBUG] 2010-07-28 11:28:31 org.hibernate.cache.NonstrictReadWriteCache - Cache lookup: com.**#3827849
[DEBUG] 2010-07-28 11:28:31 com.**.frame.cache.memcached.MemCache - key: com.**#3827849
[DEBUG] 2010-07-28 11:28:31 com.**.frame.cache.memcached.SockIOPool$SockIO - ++++ marking socket (Socket[addr=/127.0.0.1,port=11211,localport=56135]) as closed and available to return to avail pool
[DEBUG] 2010-07-28 11:28:31 org.hibernate.cache.NonstrictReadWriteCache - Cache hit//已经命中
[DEBUG] 2010-07-28 11:28:31 org.hibernate.engine.StatefulPersistenceContext - initializing non-lazy collections
[DEBUG] 2010-07-28 11:28:31 org.hibernate.transaction.JDBCTransaction - commit
[DEBUG] 2010-07-28 11:28:31 org.hibernate.transaction.JDBCTransaction - re-enabling autocommit
[DEBUG] 2010-07-28 11:28:31 org.hibernate.transaction.JDBCTransaction - committed JDBC Connection
[DEBUG] 2010-07-28 11:28:31 org.hibernate.jdbc.ConnectionManager - aggressively releasing JDBC connection
[DEBUG] 2010-07-28 11:28:31 org.hibernate.jdbc.ConnectionManager - releasing JDBC connection
通过对log4j日志分析发现,hibernate每次先get一个jdbcConnection,从数据库查询出一堆id,然后去cache查找对象。如果数据在cache中存在则取出,并提交事务释放连接;如果cache中没有,则查询数据库并将结果保存的cache中,提交事务释放连接。问题来了:既然cache中有想要查询的数据,为什么要先获取一个数据库连接、开启事务然后再去缓存中查找呢?假如所有用户查询的内容在cache中都存在,每个请求都要获取一个jdbc连接才能去查找cache,那么系统瓶颈必然会出现的连接池的连接数上。这也就不难解释为什么做压力测试时出现数据库连接耗尽的情况。由此我们得出一个结论:hibernate需要维持着数据库连接去cache中查找数据,只是减少了对数据库的查找次数,并没有减少应用的db连接数。
如何解决这个问题?理想的情况是,cache中有的直接去cache中查找,cache中没有的去db查询然后放入cache中。将代码做些修改,只从数据库中查到id,根据id去cache中查找相应的数据,cache中没有的数据则调用hibernate的查询方法。由于之前我们已经配置了缓存策略,所以hibernate查询到相应的数据后会自动放入cache中。这样一来,当第一次请求的时候先去db中拿到id,然后把所有的数据装入到cache中,第二次请求时先去查询出id,然后去cache中查找数据。以后的每次操作,只有查询id时取得数据库连接,结果集返回连接就被释放掉。这样避免了hibernate需要维持着db连接去查询cache的问题。
在从cache中获取数据的地方遇到了些麻烦,hibernate写入cache中的对象,我们能写代码取到但是无法进行解析。相关的资料介绍,hibernate对存入二级缓存的东西默认进行了封装且不提供对外的接口进行数据的解析,至于原因和封装成什么类型这里不再赘述。我们可以通过配置改变hibernate保存的对象在cache中的数据结构。具体办法:在配置hibernateProperties是增加一项配置
<prop key="hibernate.cache.use_structured_entries">true</prop>
,这样存入二级缓存的对象是一个保存属性名和属性值的map,解析map即可得到相应数据。
虽然这样一来,针对二级缓存的使用有些侵入性,但是可以保证了hibernate对二级缓存的读取不会像默认的那样需要保持着一个数据库连接。第二个问题解决。
分享到:
相关推荐
在这个特定的情况下,错误信息 "NoClassDefFoundError: Lorg/hibernate/cache/CacheProvider" 指示的是 `org.hibernate.cache.CacheProvider` 类无法被找到。 `org.hibernate.cache.CacheProvider` 是Hibernate框架...
spring.jpa.hibernate.cache.region.factory_class=org.hibernate.cache.redis.hibernate5.SingletonRedisRegionFactory ``` 4. **实体类注解**: 对于需要缓存的实体类,可以使用`@Cacheable`、`@CacheEvict`等...
2. 在Hibernate配置文件(如hibernate.cfg.xml)中开启二级缓存,通过设置`<property name="hibernate.cache.use_second_level_cache">true</property>`。 3. 指定缓存提供商,例如`<property name="hibernate.cache...
<property name="hibernate.cache.region.factory_class">org.hibernate.cache.ehcache.EhCacheRegionFactory ``` 接下来是实体类的映射信息。虽然实体类的映射通常通过`@Entity`注解完成,但在配置文件中也可以...
本文将深入探讨Hibernate性能优化中的一个重要概念——一级缓存,并结合给出的压缩包文件“hibernate_cache_level1”,来详细解析一级缓存的工作原理及其优化策略。 一级缓存是Hibernate内置的一种缓存机制,它存在...
<property name="hibernate.cache.region.factory_class">org.hibernate.cache.ehcache.EhCacheRegionFactory <property name="hibernate.cache.use_second_level_cache">true <property name="hibernate.cache.use_...
10. **性能优化**:掌握如何优化Hibernate应用,包括批处理、延迟加载(Lazy Loading)、预加载(Eager Loading)以及查询优化策略。 11. **Hibernate工具**:了解Hibernate提供的各种工具,如逆向工程(Reverse ...
<property name="hibernate.cache.region.factory_class">org.hibernate.cache.ehcache.EhCacheRegionFactory ``` 同时,为特定的实体或查询开启缓存: ```xml <class name="com.example.YourEntity" table="YOUR_...
Hibernate,作为Java领域中的一个著名对象关系映射(ORM)框架,极大地简化了数据库操作,使得开发者可以更加专注于业务逻辑而非繁琐的数据处理。在4.3.7.Final版本中,Hibernate进一步提升了性能和稳定性,为开发者...
2. **配置Hibernate**:在Hibernate的配置文件(如`persistence.xml`)中,设置`hibernate.cache.region.factory_class`为`org.hibernate.cache.redis.hibernate4.SingletonRedisRegionFactory`,并提供Redis的连接...
<property name="hibernate.cache.region.factory_class">org.hibernate.cache.jbc2.JBossCacheRegionFactory ``` 3. 配置JBoss Caching:创建一个配置文件(如treecache-pessimistic.xml),定义缓存策略和缓存...
8. Cache机制:解释Hibernate的缓存策略,包括第一级缓存和第二级缓存,以及如何配置和优化缓存以提高系统性能。 9. 性能优化:讨论如何通过合理的设计和配置提升Hibernate应用的性能,包括延迟加载、批处理和连接...
<property name="hibernate.cache.region.factory_class">org.hibernate.cache.ehcache.EhCacheRegionFactory <property name="hibernate.cache.use_second_level_cache">true <property name="hibernate.cache.use_...
- Cache管理:提供更细粒度的缓存控制,包括第二级缓存和查询缓存的优化。 - Batch处理:支持批量插入、更新和删除操作,提高大数据量操作的性能。 - Event监听器:允许在特定的持久化操作前后执行自定义逻辑,如...
这些配置参数可以帮助开发者根据具体项目需求调整Hibernate的行为,从而优化性能和数据库交互。正确配置这些参数是确保Hibernate正常工作和高效运行的关键步骤。理解并合理使用这些参数,可以显著提升应用的性能和...
10. 实战案例:通过实际的项目案例,演示如何在实际开发中应用Hibernate,解决常见的持久化问题。 总的来说,《Hibernate实战第2版》是一本全面而深入的Hibernate指南,无论你是初学者还是有经验的开发者,都能从中...
8. **org.hibernate.cache** 包:包含了Hibernate的缓存机制,如二级缓存的实现和配置。 9. **org.hibernate.annotations** 包:这里的注解定义了如何在Java类上声明ORM元数据,例如@Entity、@Table、@Column等。 ...
### Hibernate程序性能优化 #### 一、概述 Hibernate作为一款流行的Java持久层框架,为开发者提供了简化数据库操作的强大工具。然而,在实际应用中,不少开发者遇到了性能问题:使用Hibernate实现的功能与直接使用...
请注意,Hibernate 2.1是一个较旧的版本,现代开发中通常使用更先进的Hibernate 5.x或更高版本,它们提供了更多的特性、优化和对新Java及JPA标准的支持。尽管如此,理解Hibernate 2.1的基本工作原理对于学习ORM框架...