性能优化(1+N,list与iterator,缓存,事务)

1、注意session.clear()的运用,尤其是不断分页循环的时候

  A 在一个大集合中进行遍历,取出其中含有敏感字的对象

  B 另一种形式的内存泄露.

2、1+N问题

问题描述:如@ManyToOne时,两个类分别是User与Group,取User时,本想发一条SQL语句,结果顺带发了N条语句,将每个User对应的Group也查询了。

解决方法有三种:

  (1)设为@ManyToOne(fetch=FetchType.LAZY)

  (2)在Group类中的@Entity下面加一条@BatchSize(size=5),则每一条SQL语句可以取出5个Group对象

  (3)查询语句写为“from User u left join fetch u.group g”

3、list和iterator的区别

  (1)list()返回List,直接取对象;list第二次发出,仍会到数据库中查询数据

  (2)iterator先取对象的主键,即ID,等到要用的时候再根据ID取出对象;iterator第二次首先查找session级缓存.

4、缓存

(1)一级缓存(session级别的缓存)

  例如,同一个session中,load两次,只向数据库发一条SQL语句。但如果是两个不同session中,分别load一次,则发两条SQL语句。

(2)二级缓存(SessionFactory级别的缓存,可以跨越session级别存在)

  两个不同session中,分别load一次,要想只发一条SQL语句,则需要利用二级缓存。

  load默认使用二级缓存,iterator默认使用二级缓存

  list默认向二级缓存添加数据,但是查询的时候不使用.

  如果Query需要使用二级缓存,则打开查询缓存。

  适用情况:经常被访问,改动不大,数量有限的情况,如用户权限,组织机构等。

  使用方法:Hibernate不提供二级缓存的实现,由其他厂商提供(如EH,OS,Swarm,JBoss)。使用时要配置xml,导入jar包。

       在相关类中需要@Entity@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)

(3)查询缓存:在二级缓存设置有效时才可以使用,需要在xml中设置。相同的查询语句才能使用查询缓存。 

  需要调用Query setCachable(true)方法指明使用二级缓存.

5、缓存算法

LRU,LFU, FIFO

Least Recently Used 按时间

Least Frequently Userd 按命中次数

First In First Out

memoryStoreEvictionPlicy=”LRU”(ehcache)

6、事务并发

(1)ACID 原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)、持久性(Durability)。

(2)事物并发带来的问题:

  • 脏读:读了其他未提交事务B的数据,该事务B后来被回滚了
  • 不可重复读:同一事务中,读了两遍,前后数据不一致。因为其他事务B更改了数据
  • 幻读:同一事务中,读了两遍,前后数据不一致。因为其他事务B插入/删除了数据
  • 第一类丢失更新(lost update): 在完全未隔离事务的情况下,两个事物更新同一条数据资源,某一事物异常终止,回滚造成第一个完成的更新也同时丢失。
  • 第二类丢失更新(second lost updates):是不可重复读的特殊情况,如果两个事务都读取同一行,然后两个都进行写操作,并提交,第一个事务所做的改变就会丢失。

(3)数据库事务隔离级别:(javax.sql.Connection),事务隔离级别由数据库实现,Hibernate配置文件中可以对其进行配置。

  • READ_UNCOMMITTED:允许读取改变了的还未提交的数据,可能导致脏读、不可重复读和幻读。 
  • READ COMMITTED(Oracle默认):允许并发事务提交之后读取,可以避免脏读,可能导致重复读和幻读。 
  • REPEATABLE_READ(MySQL默认):对相同字段的多次读取结果一致,可导致幻读。 
  • SERIALIZABLE:完全服从ACID的原则,确保不发生脏读、不可重复读和幻读。 

(4)解决事物并发带来的问题采用悲观锁或乐观锁

a.悲观锁:依赖于数据库。

  悲观锁,正如其名,它指的是对数据被外界(包括本系统当前的其他事务,以及来自外部系统的事务处理)修改持保守态度,因此,在整个数据处理过程中,将数据处于锁定状态。悲观锁的实现,往往依靠数据库提供的锁机制(也只有数据库层提供的锁机制才能真正保证数据访问的排他性,否则,即使在本系统中实现了加锁机制,也无法保证外部系统不会修改数据)。

  一个典型的依赖数据库的悲观锁调用:select * from account where name=”Erica” for update这条 sql 语句锁定了 account 表中所有符合检索条件( name=”Erica” )的记录。本次事务提交之前(事务提交时会释放事务过程中的锁),外界无法修改这些记录。悲观锁,也是基于数据库的锁机制实现。

  在Hibernate使用悲观锁十分容易,但实际应用中悲观锁是很少被使用的,因为它大大限制了并发性。如下例子:

  T1,T2时刻取款事务和转账事务分别开启,T3事务查询ACCOUNTS表的数据并用悲观锁锁定,T4转账事务也要查询同一条数据,数据库发现该记录已经被前一个事务使用悲观锁锁定了,然后让转账事务等待直到取款事务提交。T6时刻取款事务提交,T7时刻转账事务获取数据。

b.乐观锁:与数据库无关,效率高。在类中加入private int version;属性@Version 该属性由Hibernate控制,每更新一次加1,不一致时报错。

    相对悲观锁而言,乐观锁机制采取了更加宽松的加锁机制。悲观锁大多数情况下依靠数据库的锁机制实现,以保证操作最大程度的独占性。但随之而来的就是数据库性能的大量开销,特别是对长事务而言,这样的开销往往无法承受。乐观锁机制在一定程度上解决了这个问题。乐观锁,大多是基于数据版本(Version)记录机制实现。何谓数据版本?即为数据增加一个版本标识,在基于数据库表的版本解决方案中,一般是通过为数据库表增加一个"version"字段来实现。
  乐观锁的工作原理:读取出数据时,将此版本号一同读出,之后更新时,对此版本号加一。此时,将提交数据的版本数据与数据库表对应记录的当前版本信息进行比对,如果提交的数据版本号大于数据库表当前版本号,则予以更新,否则认为是过期数据。

  Hibernate为乐观锁提供了3中实现:1. 基于version  2. 基于timestamp  3. 为遗留项目添加添加乐观锁

原文地址:https://www.cnblogs.com/seven7seven/p/3868530.html