`
jiulingchen
  • 浏览: 44734 次
  • 性别: Icon_minigender_1
  • 来自: 深圳
社区版块
存档分类
最新评论

项目中Hibernate的优化:Cache

    博客分类:
  • JAVA
阅读更多
二、hibernate二级缓存避免查询Cache需要先获得db连接
hibernate自身管理一级缓存,如果需要使用二级缓存,则要自己来实现相应的代码,这个实现起来并不复杂只需要实现
hibernate提供的相应的接口即可。我们在项目中选用了最为通用的memcached,具体配置如下:
在spring中配置hibernateProperties是增加一项配置
Java代码
<prop key="hibernate.cache.provider_class">com.*.frame.cache.memcached.MemcachedCacheProvider</prop>

MemcachedCacheProvider需要实现hibernate提供的CacheProvider接口:
Java代码 
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接口:
Java代码
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,其次去掉一些无用的信息,比如初始化、加载配置文件等等。准备工作完成后,咱们模拟用户的行为进行一次操作。这个时候输出的日志就会记录一个请求执行的所有的过滤器,拦截器,方法等堆栈信息。   
Java代码
[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是增加一项配置Java代码
<prop key="hibernate.cache.use_structured_entries">true</prop> 


这样存入二级缓存的对象是一个保存属性名和属性值的map,解析map即可得到相应数据。
    虽然这样一来,针对二级缓存的使用有些侵入性,但是可以保证了hibernate对二级缓存的读取不会像默认的那样需要保持着一个数据库连接。
分享到:
评论

相关推荐

    Hibernate性能优化:一级缓存

    本文将深入探讨Hibernate性能优化中的一个重要概念——一级缓存,并结合给出的压缩包文件“hibernate_cache_level1”,来详细解析一级缓存的工作原理及其优化策略。 一级缓存是Hibernate内置的一种缓存机制,它存在...

    Hibernate性能优化:二级缓存

    选择合适的缓存提供商并根据项目需求进行配置是优化的关键步骤。 ### 配置二级缓存 在Hibernate配置文件中,我们需要开启二级缓存,并指定缓存提供商。例如,启用Ehcache的配置如下: ```xml &lt;property name="...

    hibernate_cache_level_1

    本篇文章将深入探讨“hibernate_cache_level_1”这一主题,即Hibernate的二级缓存,以及在项目中的应用。 一级缓存是Hibernate内置的Session级别的缓存,它是事务性的,每个Session都有自己的缓存,当Session关闭时...

    java.lang.NoClassDefFoundError: Lorg/hibernate/cache/CacheProvider

    1. **缺失的依赖**:最常见的情况是项目的类路径或Maven/Gradle的依赖配置中缺少了Hibernate的缓存相关模块。例如,可能缺少了Hibernate的缓存实现库,如Ehcache或Infinispan。 2. **版本不兼容**:如果项目中其他...

    Hibernate 2nd-level cache: JBoss Caching 配置与注意事项

    &lt;cache-config xmlns="urn:jboss:cache:config:1.0"&gt; &lt;tree-cache name="my-entity-cache" statistics-enabled="true"&gt; &lt;replicated-cache/&gt; &lt;/tree-cache&gt; &lt;/cache-config&gt; ``` 这里配置了一个名为"my...

    Hibernate Jboss cache

    - **依赖项**:首先确保项目中包含了Hibernate和Jboss Cache的相关库文件。这通常可以通过Maven或Gradle等构建工具来实现。 - **JTA事务支持**:如果应用服务器支持JTA事务管理,则需要配置相应的事务管理器以便与...

    springboot+jpa(hibernate配置redis为二级缓存) springboot2.1.4

    在本文中,我们将深入探讨如何在Spring Boot 2.1.4.RELEASE项目中结合JPA(Java Persistence API)和Hibernate实现Redis作为二级缓存。首先,我们需要理解这些技术的基本概念。 Spring Boot 是一个用于简化Spring...

    hibernate2.1相关jar包

    请注意,Hibernate 2.1是一个较旧的版本,现代开发中通常使用更先进的Hibernate 5.x或更高版本,它们提供了更多的特性、优化和对新Java及JPA标准的支持。尽管如此,理解Hibernate 2.1的基本工作原理对于学习ORM框架...

    spring hibernate cache

    1. 配置 Spring Cache:在 Spring 配置文件中启用缓存支持,并指定使用的缓存管理器(如 EhCacheCacheManager)。 2. 绑定 Hibernate 二级缓存:Spring Cache 可以与 Hibernate 二级缓存集成,通过配置让 Spring 的...

    Hibernate连接数据库的注册的项目

    在项目中,还需要注意优化配置,比如缓存策略、批处理大小、连接池设置等,以提高性能。例如,使用二级缓存可以显著提升读取效率: ```xml &lt;property name="hibernate.cache.use_second_level_cache"&gt;true ...

    Struts2+spring+hibernate项目模板例子

    7. **优化与扩展**:随着项目的演进,可能需要进行性能优化,例如使用缓存技术(如Spring的Cache Abstraction),调整Hibernate的二级缓存策略,或者利用Spring Boot简化配置和部署。 总之,“Struts2+spring+...

    Hibernate 缓存 实例

    了解并熟练使用Hibernate的缓存机制是优化应用程序性能的关键步骤。通过Ehcache和Spring的集成,我们可以实现高效的数据库缓存策略。一级缓存适用于单个Session的操作,而二级缓存则能在更大范围内提升性能。在实际...

    hibernate二级缓存(包括注解方式)

    **hibernate二级缓存详解(包括注解方式)** 在Java企业级开发中,Hibernate作为一款强大的ORM框架,极大地简化了数据库操作。...在实际项目中,结合具体业务需求,灵活调整缓存策略,才能最大化发挥其优势。

    hibernate-extensions-2.1.3.zip ,middlegen for hibernate

    - Cache管理:提供更细粒度的缓存控制,包括第二级缓存和查询缓存的优化。 - Batch处理:支持批量插入、更新和删除操作,提高大数据量操作的性能。 - Event监听器:允许在特定的持久化操作前后执行自定义逻辑,如...

    hibernate 优化方案

    ### Hibernate优化方案详解 在企业级应用开发中,Hibernate作为一款优秀的对象关系映射(ORM)框架,极大地简化了数据库操作。然而,在处理大量数据或复杂查询时,Hibernate的性能问题逐渐凸显。本文将深入探讨...

    hibernate_cache_level_2.rar_java_staredb4u

    本压缩包“hibernate_cache_level_2.rar”聚焦于Hibernate框架中的第二级缓存,这是一个关键特性,用于提高应用程序的性能。在这个主题下,我们将深入探讨Hibernate的二级缓存机制、其工作原理以及如何在项目中有效...

    hibernate_cache_level_1.rar_alreadydcc_java

    "hibernate_cache_level_1.rar"文件很可能包含了一个示例项目,用于演示如何在实际开发中配置和使用Hibernate的一级缓存。可能包括了以下内容: 1. Hibernate配置文件(hibernate.cfg.xml):展示了如何启用和配置...

Global site tag (gtag.js) - Google Analytics