hibernate性能优化:如何提高hibernate性能

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

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

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

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

( (Integer) s.createFilter( collection, "select count(*)" ).list.get(0) ).Value

  这里createFilter思路方法也可以被用来有效抓取集合部分内容而无需例子化整个集合:

s.createFilter( lazyCollection, "").FirstResult(0).MaxResults(10).list;

  20.1.5. 使用批量抓取(Using batch fetching)

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

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

< name="Person" batch-size="10">...</>

  随的Hibernate将只需要执行 3次查询分别为10、10、 5

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

< name="Person"> < name="cats" batch-size="3"> ... </></>

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

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

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

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

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

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

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

< name="Document"> <id name="id"> <generator ="native"/> </id> <property name="name" not-null="true" length="50"/> <property name="summary" not-null="true" length="200" lazy="true"/> <property name="text" not-null="true" length="2000" lazy="true"/></>

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

  你可以在AntTask中进行如下定义对持久类代码加入“ 2进制指令

<target name="instrument" depends="compile"> <taskdef name="instrument" name="org.hibernate.tool.instrument.InstrumentTask"> <path path="${jar.path}"/> <path path="${es.dir}"/> <path refid="lib..path"/> </taskdef> <instrument verbose="true"> <file dir="${testes.dir}/org/hibernate/auction/model"> < name="*."/> </file> </instrument></target>

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

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

  20.2. 2级缓存Cache(The Second Level Cache)

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

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

  表 20.1. 缓存Cache策略提供商(Cache Providers) Cache Provider Type Cluster Safe Query Cache Supported
Hashtable (not ended for production use) org.hibernate.cache.HashtableCacheProvider memory   yes
EHCache org.hibernate.cache.EhCacheProvider memory, disk   yes
OSCache org.hibernate.cache.OSCacheProvider memory, disk   yes
SwarmCache org.hibernate.cache.SwarmCacheProvider clustered (ip multicast) yes (clustered invalidation)  
JBoss TreeCache org.hibernate.cache.TreeCacheProvider clustered (ip multicast), transactional yes (replication) yes (clock sync req.)



  20.2.1. 缓存Cache映射(Cache mappings)

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

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

(1)

  usage介绍说明了缓存Cache策略: transactional、 read-write、 nonstrict-read-write或 read-only





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

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

  20.2.2. 策略:只读缓存Cache(Strategy: read _disibledevent=>

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

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

< name="eg.Cat" .... > <cache usage="read-write"/> .... < name="kittens" ... > <cache usage="read-write"/> .... </></>

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

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

  20.2.5. 策略:事务缓存Cache(transactional)

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

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

  表 20.2. 各种缓存Cache提供商对缓存Cache并发策略支持情况(Cache Concurrency Strategy Support) Cache read-only nonstrict-read-write read-write transactional
Hashtable (not ended for production use) yes yes yes  
EHCache yes yes yes  
OSCache yes yes yes  
SwarmCache yes yes    
JBoss TreeCache yes     yes



  20.3. 管理缓存Cache(Managing the caches)

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

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

ScrollableResult cats = sess.createQuery("from Cat as cat").scroll; //a huge result while ( cats.next ) { Cat cat = (Cat) cats.get(0); doSomethingWithACat(cat); sess.evict(cat);}

  Session还提供了个contains思路方法用来判断某个例子是否处于当前session缓存Cache中

  如若要把所有对象从session缓存Cache中彻底清除则需要Session.clear

  对于 2级缓存Cache来说在SessionFactory中定义了许多思路方法 清除缓存Cache中例子、整个类、集合例子或者整个集合

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

  CacheMode参数用于控制具体Session如何和 2级缓存Cache进行交互

  CacheMode.NORMAL - 从 2级缓存Cache中读、写数据

  CacheMode.GET - 从 2级缓存Cache中读取数据仅在数据更新时对 2级缓存Cache写数据

  CacheMode.PUT - 仅向 2级缓存Cache写数据但不从 2级缓存Cache中读数据

  CacheMode.REFRESH - 仅向 2级缓存Cache写数据但不从 2级缓存Cache中读数据通过 hibernate.cache.use_minimal_puts设置强制 2级缓存Cache从数据库中读取数据刷新缓存Cache内容

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

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

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

hibernate.generate_statistics truehibernate.cache.use_structured_entries true

  20.4. 查询缓存Cache(The Query Cache)

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

hibernate.cache.use_query_cache true

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

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

  如果你要对查询缓存Cache失效政策进行精确控制你必须Query.CacheRegion思路方法 为每个查询指定其命名缓存Cache区域

List blogs = sess.createQuery("from Blog blog where blog.blogger = :blogger") .Entity("blogger", blogger) .MaxResults(15) .Cacheable(true) .CacheRegion("frontpages") .list;

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

  20.5. 理解集合性能(Understanding Collection performance)

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

  20.5.1. 分类(Taxonomy)

  Hibernate定义了 3种基本类型集合:

  值数据集合

  对多关联

  多对多关联

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

  有序集合类

  集合(s)

  包(bags)

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

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

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

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

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

  20.5.2. Lists, maps 和s用于更新效率最高

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

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

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

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

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

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

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

  20.5.4. 次性删除(One shot delete)

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

  假设我们在个长度为20集合类中新增加了个元素然后再删除两个 Hibernate会安排条INSERT语句和两条DELETE语句(除非集合类是个bag) 这当然是显而易见

  但是假设我们删除了18个数据只剩下2个然后新增3个则有两种处理方式:

  逐删除这18个数据再新增 3个;

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

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

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

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

  20.6. 监测性能(Monitoring performance)

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

  20.6.1. 监测SessionFactory

  你可以有两种方式访问SessionFactory数据记录种就是自己直接 sessionFactory.getStatistics思路方法读取、显示统计数据

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

// MBean service registration for a specic SessionFactoryHashtable tb = Hashtable;tb.put("type", "statistics");tb.put("sessionFactory", "myFinancialApp");ObjectName _disibledevent=>
// MBean service registration for all SessionFactory'sHashtable tb = Hashtable;tb.put("type", "statistics");tb.put("sessionFactory", "all");ObjectName _disibledevent=>

  TODO:仍需要介绍说明是:在第个例子中我们直接得到和使用MBean;而在第 2个例子中在使用MBean的前 我们则需要给出SessionFactoryJNDI名使用hibernateStatsBean.SessionFactoryJNDIName("my/JNDI/Name") 得到SessionFactory然后将MBean保存于其中

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

  在配置期间将hibernate.generate_statistics设置为true或false;

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

  你也可以在clear思路方法重置统计数据logSummary 在日志中记录(info级别)其整理总结

  20.6.2. 数据记录(Metrics)

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

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

  实体、集合、查询、缓存Cache等内容数据记录

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

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

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

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

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

Tags:  hibernate如何优化 如何使用hibernate hibernate性能 hibernate性能优化

延伸阅读

最新评论

发表评论