知识大全 如何提高hibernate性能

Posted

篇首语:知识的价值不在于占有,而在于使用。本文由小常识网(cha138.com)小编为大家整理,主要介绍了知识大全 如何提高hibernate性能相关的知识,希望对你有一定的参考价值。

  文章出处

  在一个拥有单独业务层的应用中 业务层必须在返回之前 为web层 准备 好其所需的数据集合 这就意味着 业务层应该载入所有表现层/web层所需的数据 并将这些已实例化完毕的数据返回 通常 应用程序应该 为web层所需的每个集合调用Hibernate initialize() (这个调用必须发生咱session关闭之前) 或者使用带有FETCH 从句 或FetchMode JOIN 的Hibernate查询 事先取得所有的数据集合 如果你在应用中使用了Command模式 代替Session Facade 那么这项任务将会变得简单的多

  你也可以通过merge() 或lock() 方法 在访问未实例化的集合(或代理)之前 为先前载入的对象绑定一个新的Session 显然 Hibernate将不会 也不应该自动完成这些任务 因为这将引入一个特殊的事务语义

  有时候 你并不需要完全实例化整个大的集合 仅需要了解它的部分信息(例如其大小) 或者集合的部分内容

  你可以使用集合过滤器得到其集合的大小 而不必实例化整个集合

  ( (Integer) s createFilter( collection select count(*) ) list() get( ) ) intValue()

  这里的createFilter() 方法也可以被用来有效的抓取集合的部分内容 而无需实例化整个集合

  s createFilter( lazyCollection ) setFirstResult( ) setMaxResults( ) list();

    使用批量抓取(Using batch fetching)

  Hibernate可以充分有效的使用批量抓取 也就是说 如果仅一个访问代理(或集合) 那么Hibernate将不载入其他未实例化的代理 批量抓取是延迟查询抓取的优化方案 你可以在两种批量抓取方案之间进行选择 在类级别和集合级别

  类/实体级别的批量抓取很容易理解 假设你在运行时将需要面对下面的问题 你在一个Session 中载入了 个 Cat 实例 每个Cat 实例都拥有一个引用成员owner 其指向Person 而Person 类是代理 同时lazy= true 如果你必须遍历整个cats集合 对每个元素调用getOwner() 方法 Hibernate将会默认的执行 次SELECT 查询 得到其owner的代理对象 这时 你可以通过在映射文件的Person 属性 显式声明batch size 改变其行为

  <class name= Person batch size= > </class>

  随之 Hibernate将只需要执行三次查询 分别为

  你也可以在集合级别定义批量抓取 例如 如果每个Person 都拥有一个延迟载入的Cats 集合 现在 Sesssion 中载入了 个person对象 遍历person集合将会引起 次SELECT 查询 每次查询都会调用getCats() 方法 如果你在Person 的映射定义部分 允许对cats 批量抓取 那么 Hibernate将可以预先抓取整个集合 请看例子

  <class name= Person > <set name= cats batch size= > </set></class>

  如果整个的batch size 是 (笔误?) 那么Hibernate将会分四次执行SELECT 查询 按照 的大小分别载入数据 这里的每次载入的数据量还具体依赖于当前Session 中未实例化集合的个数

  如果你的模型中有嵌套的树状结构 例如典型的帐单-原料结构(bill of materials pattern) 集合的批量抓取是非常有用的 (尽管在更多情况下对树进行读取时 嵌套集合(nested set)或原料路径(materialized path)(××)是更好的解决方法 )

    使用子查询抓取(Using subselect fetching)

  假若一个延迟集合或单值代理需要抓取 Hibernate会使用一个subselect重新运行原来的查询 一次性读入所有的实例 这和批量抓取的实现方法是一样的 不会有破碎的加载

    使用延迟属性抓取(Using lazy property fetching)

  Hibernate 对单独的属性支持延迟抓取 这项优化技术也被称为组抓取(fetch groups) 请注意 该技术更多的属于市场特性 在实际应用中 优化行读取比优化列读取更重要 但是 仅载入类的部分属性在某些特定情况下会有用 例如在原有表中拥有几百列数据 数据模型无法改动的情况下

  可以在映射文件中对特定的属性设置lazy 定义该属性为延迟载入

  <class name= Document > <id name= id > <generator class= native /> </id> <property name= name not null= true length= /> <property name= summary not null= true length= lazy= true /> <property name= text not null= true length= lazy= true /></class>

  属性的延迟载入要求在其代码构建时加入二进制指示指令(bytecode instrumentation) 如果你的持久类代码中未含有这些指令 Hibernate将会忽略这些属性的延迟设置 仍然将其直接载入

  你可以在Ant的Task中 进行如下定义 对持久类代码加入 二进制指令

  <target name= instrument depends= pile > <taskdef name= instrument classname= hibernate tool instrument InstrumentTask > <classpath path= $jar path /> <classpath path= $classes dir /> <classpath refid= lib class path /> </taskdef> <instrument verbose= true > <fileset dir= $testclasses dir//hibernate/auction/model > <include name= * class /> </fileset> </instrument></target>

  还有一种可以优化的方法 它使用HQL或条件查询的投影(projection)特性 可以避免读取非必要的列 这一点至少对只读事务是非常有用的 它无需在代码构建时 二进制指令 处理 因此是一个更加值得选择的解决方法

  有时你需要在HQL中通过抓取所有属性 强行抓取所有内容

    二级缓存(The Second Level Cache)

  Hibernate的Session 在事务级别进行持久化数据的缓存操作 当然 也有可能分别为每个类(或集合) 配置集群 或JVM级别(SessionFactory级别 )的缓存 你甚至可以为之插入一个集群的缓存 注意 缓存永远不知道其他应用程序对持久化仓库(数据库)可能进行的修改 (即使可以将缓存数据设定为定期失效)

  默认情况下 Hibernate使用EHCache进行JVM级别的缓存(目前 Hibernate已经废弃了对JCS的支持 未来版本中将会去掉它) 你可以通过设置Hibernate cache provider_class 属性 指定其他的缓存策略 该缓存策略必须实现 hibernate cache CacheProvider 接口

  表    缓存策略提供商(Cache Providers)

Cache Provider class Type Cluster Safe Query Cache Supported Hashtable (not intended for production use) hibernate cache HashtableCacheProvider memory   yes EHCache hibernate cache EhCacheProvider memory disk   yes OSCache hibernate cache OSCacheProvider memory disk   yes SwarmCache hibernate cache SwarmCacheProvider clustered (ip multicast) yes (clustered invalidation)   JBoss TreeCache hibernate cache TreeCacheProvider clustered (ip multicast) transactional yes (replication) yes (clock sync req )

    缓存映射(Cache mappings)

  类或者集合映射的 <cache> 元素 可以有下列形式

  <cache usage= transactional|read write|nonstrict read write|read only ( )/>

( )

  usage 说明了缓存的策略: transactional read write nonstrict read write 或 read only

  另外(首选?) 你可以在hibernate cfg xml中指定<class cache> 和 <collection cache> 元素

  这里的usage 属性指明了缓存并发策略(cache concurrency strategy)

    策略 只读缓存(Strategy: read only)

  如果你的应用程序只需读取一个持久化类的实例 而无需对其修改 那么就可以对其进行只读 缓存 这是最简单 也是实用性最好的方法 甚至在集群中 它也能完美地运作

  <class name= eg Immutable mutable= false > <cache usage= read only /> </class>

     策略:读/写缓存(Strategy: read/write)

  如果应用程序需要更新数据 那么使用读/写缓存 比较合适 如果应用程序要求 序列化事务 的隔离级别(serializable transaction isolation level) 那么就决不能使用这种缓存策略 如果在JTA环境中使用缓存 你必须指定Hibernate transaction manager_lookup_class 属性的值 通过它 Hibernate才能知道该应用程序中JTA的TransactionManager 的具体策略 在其它环境中 你必须保证在Session close() 或Session disconnect() 调用前 整个事务已经结束 如果你想在集群环境中使用此策略 你必须保证底层的缓存实现支持锁定(locking) Hibernate内置的缓存策略并不支持锁定功能

  <class name= eg Cat > <cache usage= read write /> <set name= kittens > <cache usage= read write /> </set></class>

     策略:非严格读/写缓存(Strategy: nonstrict read/write)

  如果应用程序只偶尔需要更新数据(也就是说 两个事务同时更新同一记录的情况很不常见) 也不需要十分严格的事务隔离 那么比较适合使用非严格读/写缓存 策略 如果在JTA环境中使用该策略 你必须为其指定Hibernate transaction manager_lookup_class 属性的值 在其它环境中 你必须保证在Session close() 或Session disconnect() 调用前 整个事务已经结束

     策略:事务缓存(transactional)

  Hibernate的事务缓存 策略提供了全事务的缓存支持 例如对JBoss TreeCache的支持 这样的缓存只能用于JTA环境中 你必须指定 为其Hibernate transaction manager_lookup_class 属性

  没有一种缓存提供商能够支持上列的所有缓存并发策略 下表中列出了各种提供器 及其各自适用的并发策略

  表    各种缓存提供商对缓存并发策略的支持情况(Cache Concurrency Strategy Support)

Cache read only nonstrict read write read write transactional Hashtable (not intended for production use) yes yes yes   EHCache yes yes yes   OSCache yes yes yes   SwarmCache yes yes     JBoss TreeCache yes     yes

     管理缓存(Managing the caches)

  无论何时 当你给save() update() 或 saveOrUpdate() 方法传递一个对象时 或使用load() get() list() iterate() 或scroll() 方法获得一个对象时 该对象都将被加入到Session 的内部缓存中

  当随后flush()方法被调用时 对象的状态会和数据库取得同步 如果你不希望此同步操作发生 或者你正处理大量对象 需要对有效管理内存时 你可以调用evict() 方法 从一级缓存中去掉这些对象及其集合

  ScrollableResult cats = sess createQuery( from Cat as cat ) scroll(); //a huge result sehile ( cats next() ) Cat cat = (Cat) cats get( ); doSomethingWithACat(cat); sess evict(cat);

  Session还提供了一个contains() 方法 用来判断某个实例是否处于当前session的缓存中

  如若要把所有的对象从session缓存中彻底清除 则需要调用Session clear()

  对于二级缓存来说 在SessionFactory 中定义了许多方法 清除缓存中实例 整个类 集合实例或者整个集合

  sessionFactory evict(Cat class catId); //evict a particular CatsessionFactory evict(Cat class); //evict all CatssessionFactory evictCollection( Cat kittens catId); //evict a particular collection of kittenssessionFactory evictCollection( Cat kittens ); //evict all kitten collections

  CacheMode 参数用于控制具体的Session如何与二级缓存进行交互

  CacheMode NORMAL 从二级缓存中读 写数据

  CacheMode GET 从二级缓存中读取数据 仅在数据更新时对二级缓存写数据

  CacheMode PUT 仅向二级缓存写数据 但不从二级缓存中读数据

  CacheMode REFRESH 仅向二级缓存写数据 但不从二级缓存中读数据 通过 Hibernate cache use_minimal_puts 的设置 强制二级缓存从数据库中读取数据 刷新缓存内容

  如若需要查看二级缓存或查询缓存区域的内容 你可以使用统计(Statistics) API

  Map cacheEntries = sessionFactory getStatistics() getSecondLevelCacheStatistics(regionName) getEntries();

  此时 你必须手工打开统计选项 可选的 你可以让Hibernate更人工可读的方式维护缓存内容

  Hibernate generate_statistics truehibernate cache use_structured_entries true

    查询缓存(The Query Cache)

  查询的结果集也可以被缓存 只有当经常使用同样的参数进行查询时 这才会有些用处 要使用查询缓存 首先你必须打开它

  Hibernate cache use_query_cache true

  该设置将会创建两个缓存区域 一个用于保存查询结果集( hibernate cache StandardQueryCache ) 另一个则用于保存最近查询的一系列表的时间戳( hibernate cache UpdateTimestampsCache ) 请注意 在查询缓存中 它并不缓存结果集中所包含的实体的确切状态 它只缓存这些实体的标识符属性的值 以及各值类型的结果 所以查询缓存通常会和二级缓存一起使用

  绝大多数的查询并不能从查询缓存中受益 所以Hibernate默认是不进行查询缓存的 如若需要进行缓存 请调用 Query setCacheable(true) 方法 这个调用会让查询在执行过程中时先从缓存中查找结果 并将自己的结果集放到缓存中去

  如果你要对查询缓存的失效政策进行精确的控制 你必须调用Query setCacheRegion() 方法 为每个查询指定其命名的缓存区域

  List blogs = sess createQuery( from Blog blog where blog blogger = :blogger ) setEntity( blogger blogger) setMaxResults( ) setCacheable(true) setCacheRegion( frontpages ) list();

  如果查询需要强行刷新其查询缓存区域 那么你应该调用Query setCacheMode(CacheMode REFRESH) 方法 这对在其他进程中修改底层数据(例如 不通过Hibernate修改数据) 或对那些需要选择性更新特定查询结果集的情况特别有用 这是对SessionFactory evictQueries() 的更为有效的替代方案 同样可以清除查询缓存区域

     理解集合性能(Understanding Collection performance)

  前面我们已经对集合进行了足够的讨论 本段中 我们将着重讲述集合在运行时的事宜

     分类(Taxonomy)

  Hibernate定义了三种基本类型的集合

  值数据集合

  一对多关联

  多对多关联

  这个分类是区分了不同的表和外键关系类型 但是它没有告诉我们关系模型的所有内容 要完全理解他们的关系结构和性能特点 我们必须同时考虑 用于Hibernate更新或删除集合行数据的主键的结构 因此得到了如下的分类

  有序集合类

  集合(sets)

  包(bags)

  所有的有序集合类(maps lists arrays)都拥有一个由<key> 和 <index> 组成的主键 这种情况下集合类的更新是非常高效的——主键已经被有效的索引 因此当Hibernate试图更新或删除一行时 可以迅速找到该行数据

  集合(sets)的主键由<key> 和其他元素字段构成 对于有些元素类型来说 这很低效 特别是组合元素或者大文本 大二进制字段 数据库可能无法有效的对复杂的主键进行索引 另一方面 对于一对多 多对多关联 特别是合成的标识符来说 集合也可以达到同样的高效性能 ( 附注 如果你希望SchemaExport 为你的<set> 创建主键 你必须把所有的字段都声明为not null= true )

  <idbag> 映射定义了代理键 因此它总是可以很高效的被更新 事实上 <idbag> 拥有着最好的性能表现

  Bag是最差的 因为bag允许重复的元素值 也没有索引字段 因此不可能定义主键 Hibernate无法判断出重复的行 当这种集合被更改时 Hibernate将会先完整地移除 (通过一个(in a single DELETE ))整个集合 然后再重新创建整个集合 因此Bag是非常低效的

  请注意 对于一对多关联来说 主键 很可能并不是数据库表的物理主键 但就算在此情况下 上面的分类仍然是有用的 (它仍然反映了Hibernate在集合的各数据行中是如何进行 定位 的 )

     Lists maps 和sets用于更新效率最高

  根据我们上面的讨论 显然有序集合类型和大多数set都可以在增加 删除 修改元素中拥有最好的性能

  可论证的是对于多对多关联 值数据集合而言 有序集合类比集合(set)有一个好处 因为Set 的内在结构 如果 改变 了一个元素 Hibernate并不会更新(UPDATE) 这一行 对于Set 来说 只有在插入(INSERT) 和删除(DELETE) 操作时 改变 才有效 再次强调 这段讨论对 一对多关联 并不适用

  注意到数组无法延迟载入 我们可以得出结论 list map和idbags是最高效的(非反向)集合类型 set则紧随其后 在Hibernate中 set应该时最通用的集合类型 这时因为 set 的语义在关系模型中是最自然的

  但是 在设计良好的Hibernate领域模型中 我们通常可以看到更多的集合事实上是带有inverse= true 的一对多的关联 对于这些关联 更新操作将会在多对一的这一端进行处理 因此对于此类情况 无需考虑其集合的更新性能

     Bag和list是反向集合类中效率最高的

  在把bag扔进水沟之前 你必须了解 在一种情况下 bag的性能(包括list)要比set高得多 对于指明了inverse= true 的集合类(比如说 标准的双向的一对多关联) 我们可以在未初始化(fetch)包元素的情况下直接向bag或list添加新元素! 这是因为Collection add() )或者Collection addAll() 方法 对bag或者List总是返回true(这点与与Set不同) 因此对于下面的相同代码来说 速度会快得多

  Parent p = (Parent) sess load(Parent class id); Child c = new Child(); c setParent(p); p getChildren() add(c); //no need to fetch the collection! sess flush();

     一次性删除(One shot delete)

  偶尔的 逐个删除集合类中的元素是相当低效的 Hibernate并没那么笨 如果你想要把整个集合都删除(比如说调用list clear()) Hibernate只需要一个DELETE就搞定了

  假设我们在一个长度为 的集合类中新增加了一个元素 然后再删除两个 Hibernate会安排一条INSERT 语句和两条DELETE 语句(除非集合类是一个bag) 这当然是显而易见的 但是 假设我们删除了 个数据 只剩下 个 然后新增 个 则有两种处理方式

  逐一的删除这 个数据 再新增三个

  删除整个集合类(只用一句DELETE语句) 然后增加 个数据

  Hibernate还没那么聪明 知道第二种选择可能会比较快 (也许让Hibernate不这么聪明也是好事 否则可能会引发意外的 数据库触发器 之类的问题 )

  幸运的是 你可以强制使用第二种策略 你需要取消原来的整个集合类(解除其引用) 然后再返回一个新的实例化的集合类 只包含需要的元素 有些时候这是非常有用的

  显然 一次性删除并不适用于被映射为inverse= true 的集合

     监测性能(Monitoring performance)

  没有监测和性能参数而进行优化是毫无意义的 Hibernate为其内部操作提供了一系列的示意图 因此可以从 每个SessionFactory 抓取其统计数据

     监测SessionFactory

  你可以有两种方式访问SessionFactory 的数据记录 第一种就是自己直接调用 sessionFactory getStatistics() 方法读取 显示统计 数据

  此外 如果你打开StatisticsService MBean选项 那么Hibernate则可以使用JMX技术 发布其数据记录 你可以让应用中所有的SessionFactory 同时共享一个MBean 也可以每个 SessionFactory分配一个MBean 下面的代码即是其演示代码

  // MBean service registration for a specific SessionFactoryHashtable tb = new Hashtable();tb put( type statistics );tb put( sessionFactory myFinancialApp );ObjectName on = new ObjectName( hibernate tb); // MBean object nameStatisticsService stats = new StatisticsService(); // MBean implementationstats setSessionFactory(sessionFactory); // Bind the stats to a SessionFactoryserver registerMBean(stats on); // Register the Mbean on the server

  // MBean service registration for all SessionFactory sHashtable tb = new Hashtable();tb put( type statistics );tb put( sessionFactory all );ObjectName on = new ObjectName( hibernate tb); // MBean object nameStatisticsService stats = new StatisticsService(); // MBean implementationserver registerMBean(stats on); // Register the MBean on the server

  TODO 仍需要说明的是 在第一个例子中 我们直接得到和使用MBean 而在第二个例子中 在使用MBean之前 我们则需要给出SessionFactory的JNDI名 使用hibernateStatsBean setSessionFactoryJNDIName( my/JNDI/Name ) 得到SessionFactory 然后将MBean保存于其中

  你可以通过以下方法打开或关闭SessionFactory 的监测功能

  在配置期间 将Hibernate generate_statistics 设置为true 或false

  在运行期间 则可以可以通过sf getStatistics() setStatisticsEnabled(true) 或hibernateStatsBean setStatisticsEnabled(true)

  你也可以在程序中调用clear() 方法重置统计数据 调用logSummary() 在日志中记录(info级别)其总结

     数据记录(Metrics)

  Hibernate提供了一系列数据记录 其记录的内容包括从最基本的信息到与具体场景的特殊信息 所有的测量值都可以由 Statistics 接口进行访问 主要分为三类

  使用Session 的普通数据记录 例如打开的Session的个数 取得的JDBC的连接数等

  实体 集合 查询 缓存等内容的统一数据记录

  和具体实体 集合 查询 缓存相关的详细数据记录

  例如 你可以检查缓存的命中成功次数 缓存的命中失败次数 实体 集合和查询的使用概率 查询的平均时间等 请注意 Java中时间的近似精度是毫秒 Hibernate的数据精度和具体的JVM有关 在有些平台上其精度甚至只能精确到 秒

  你可以直接使用getter方法得到全局数据记录(例如 和具体的实体 集合 缓存区无关的数据) 你也可以在具体查询中通过标记实体名 或HQL SQL语句得到某实体的数据记录 请参考Statistics EntityStatistics CollectionStatistics SecondLevelCacheStatistics 和QueryStatistics 的API文档以抓取更多信息 下面的代码则是个简单的例子

  Statistics stats = HibernateUtil sessionFactory getStatistics();double queryCacheHitCount = stats getQueryCacheHitCount();double queryCacheMissCount = stats getQueryCacheMissCount();double queryCacheHitRatio = queryCacheHitCount / (queryCacheHitCount + queryCacheMissCount);( Query Hit ratio: + queryCacheHitRatio);EntityStatistics entityStats = stats getEntityStatistics( Cat class getName() );long changes = entityStats getInsertCount() + entityStats getUpdateCount() + entityStats getDeleteCount();(Cat class getName() + changed + changes + times );

  如果你想得到所有实体 集合 查询和缓存区的数据 你可以通过以下方法获得实体 集合 查询和缓存区列表 getQueries() getEntityNames() getCollectionRoleNames() 和 getSecondLevelCacheRegionNames()

cha138/Article/program/Java/ky/201311/28706

相关参考

知识大全 hibernate的性能优化

  Hibernate是对JDBC的轻量级封装因此在很多情况下Hibernate的性能比直接使用JDBC存取数据库要低然而通过正确的方法和策略在使用Hibernate的时候还是可以非常接近直接使用JD

知识大全 Hibernate性能调优

Hibernate性能调优  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!  inverse=?  

知识大全 Hibernate性能调优[2]

Hibernate性能调优[2]  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!  Hibernat

知识大全 Hibernate性能调优[1]

Hibernate性能调优[1]  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!  inverse=

知识大全 hibernate大数据性能处理

   在项目中使用Hibernate进行大数据量的性能测试有一些总结   )在处理大数据量时会有大量的数据缓冲保存在Session的一级缓存

知识大全 使用hibernate的优势

  Hibernate在解决性能问题方面做得非常好有了它的缓存机制使用第三方缓存和数据库连接池就较好的解决的性能问题但这些还不够hibernate给了开发者足够的自由让开发者自己去控制性能问题  学习

知识大全 使用hibernate的11大优势

  Hibernate在解决性能问题方面做得非常好有了它的缓存机制使用第三方缓存和数据库连接池就较好的解决的性能问题但这些还不够hibernate给了开发者足够的自由让开发者自己去控制性能问题  学习

知识大全 如何提高Hibernate 3 启动速度

如何提高Hibernate3启动速度  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧! &n

知识大全 hibernate事务,一级缓存,二级缓存

  通过以上的介绍可以看出hibernate主要从以下几个方面来优化查询性能  降低访问数据库的频率减少select语句的数目实现手段有使用迫切左外连接或迫切内连接对延迟检索或立即检索设置批量检索数目

知识大全 如何最大限度提高.NET的性能

如何最大限度提高.NET的性能  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!  优化NET的性能&