微信号:java_bj

介绍:从算法基础到常用框架的知识体系,从初级程序员到高级架构师的成长之路,从创业小团队到Google、BAT的工作机会,始于JAVA而又不止于JAVA.JAVAer在北京,我们一起成长.

数据库系列三:事务(下)

2016-07-25 06:50 Java北京整理

点击上方“Java北京”关注我们



九、死锁

1、死锁发生的条件

  1. 互斥条件:一个资源每次只能被一个进程使用;

  2. 请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放;

  3. 不剥夺条件:进程已获得的资源,在末使用完之前不能强行剥夺;

  4. 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系

2、死锁检测

Innodb检测死锁有两种情况:

  1. 满足循环等待条件

  2. 锁结构超过mysql配置中设置的最大数量或锁的遍历深度超过设置的最大深度时,innodb也会判断为死锁(这是提高性能方面的考虑,避免事务一次占用太多的资源)

因循环等待条件而产生的死锁只有可能是四种形式:

  1. 两张表两行记录交叉申请互斥锁

  2. 同一张表则存在主键索引锁冲突

  3. 主键索引锁与非聚簇索引锁冲突

  4. 锁升级导致的锁等待队列阻塞

3、死锁避免

  1. 如果使用insert…select语句备份表格且数据量较大,在单独的时间点操作,避免与其他sql语句争夺资源,或使用select into outfile加上load data infile代替 insert…select,这样不仅快,而且不会要求锁定

  2. 一个锁定记录集的事务,其操作结果集应尽量简短,以免一次占用太多资源,与其他事务处理的记录冲突。

  3. 更新或者删除表格数据,sql语句的where条件都是主键或都是索引,避免两种情况交叉,造成死锁。对于where子句较复杂的情况,将其单独通过sql得到后,再在更新语句中使用。

  4. sql语句的嵌套表格不要太多,能拆分就拆分,避免占有资源同时等待资源,导致与其他事务冲突

  5. 对定点运行脚本的情况,避免在同一时间点运行多个对同一表进行读写的脚本,特别注意加锁且操作数据量比较大的语句。

  6. 应用程序中增加对死锁的判断,如果事务意外结束,重新运行该事务,减少对功能的影响

4、死锁解决

  1. show processlist:找到死锁线程号.然后Kill pid

  2. Show innodb status:检查引擎状态 ,可以看到哪些语句产生死锁

  3. 查看information_schema架构下的innodb_locks、innodb_trx、innodb_lock_waits等表

 

十、事务模型

Java 平台支持三种事务模型:

1、Local Transaction 模型

在 LocalTransaction 模型中,事务由底层数据库资源管理程序(而非应用程序所在的容器或框架)管理,这便是它得名的原因。在这个模型中,开发者管理连接,而不是事务在使用对象关系映射框架,如Hibernate、TopLink 或JavaPersistence API (JPA)执行数据库更新时,不能使用 Local Transaction 模型。在使用数据访问对象(DAO)或基于 JDBC 的框架和数据库存储过程时可以使用。

可以采用以下两种方式来使用Local Transaction 模型:让数据库来管理连接,或者以编程的方式管理连接。要让数据库管理连接,需要将JDBC Connection对象上的 autoCommit 属性设置为 true(默认值),这将通知底层数据库管理系统(DBMS)在完成插入、更新或删除操作之后提交事务,或者在操作失败时返回任务;以编程的方式管理连接,要将 Connection 对象上的 autoCommit 属性设置为 false(这将通知底层 DBMS 在代码而非数据库中管理连接),并手动提交或回滚连接。

2、Programmatic Transaction 模型

模型名称来源于:开发者负责管理事务。在 Programmatic Transaction 模型中,与 LocalTransaction 模型不同,开发者将管理事务,并且将与底层数据库连接 相分离。

借助此模型,开发人员将负责从事务管理程序获取一个事务,启动该事务,提交事务,以及(如果出现异常)回滚到事务——而这,会产生大量易于出错的代码,从而对应用程序中的业务逻辑造成影响。但是,一些事务策略需要使用 Programmatic Transaction 模型。

虽然概念是相同,但Programmatic Transaction 模型的实现在 Spring Framework 和 EJB 3.0 规范中是不同的。

使用 EJB 3.0 实现编程事务

在 EJB 3.0 中,将从事务管理程序(换句话说,容器)获取一个事务,方法是对 javax.transaction.UserTransaction 执行 Java Naming and Directory Interface (JNDI) 查找。获取 UserTransaction 之后,可以调用 begin() 方法来启动事务,调用 commit() 方法来提交事务,并且调用 rollback() 方法以便在出现错误时回滚事务。在此模型中,容器不会自动提交或回滚事务;开发人员需要自己在执行数据库更新的方法编写此行为。如下代码通过 JPA 展示了使用 EJB 3.0 的Programmatic Transaction 模型:

@Stateless
@TransactionManagement(TransactionManagementType.BEAN)
public class TradingServiceImpl implements TradingService {
   @PersistenceContext(unitName="trading") EntityManager em;

   public void processTrade(TradeData trade) throws Exception {
      InitialContext ctx = new InitialContext();
      UserTransaction txn = (UserTransaction)ctx.lookup("UserTransaction");
      try {
         txn.begin();
	 em.persist(trade);
	 AcctData acct = em.find(AcctData.class, trade.getAcctId());
	 double tradeValue = trade.getPrice() * trade.getShares();
	 double currentBalance = acct.getBalance();
	 if (trade.getAction().equals("BUY")) {
	    acct.setBalance(currentBalance - tradeValue);
         } else {
            acct.setBalance(currentBalance + tradeValue);
         }
         txn.commit();
      } catch (Exception up) {
         txn.rollback();
         throw up;
      }
   }
}


在带无状态会话bean的Java EE容器环境中使用 Programmatic Transaction 模型时,必须通知容器正在使用编程事务。为此,需要使用 @TransactionManagement 注释并将事务类型设置为 BEAN。如果未使用此注释,则容器将假定使用声明式事务管理(CONTAINER),它是 EJB 3.0 的默认事务类型。在无状态会话 bean 上下文外部的客户机层中使用编程事务时,不需要设置事务类型。

使用 Spring 实现编程事务

SpringFramework 提供了两种实现 Programmatic Transaction 模型的方法。一种是通过 Spring TransactionTemplate 来实现,另一种是直接使用 Spring 平台事务管理程序。Spring 至少有九种平台事务管理程序。最常用的包括DataSourceTransactionManager、HibernateTransactionManager、JpaTransactionManager 和 JtaTransactionManager。以下以JpaTransactionManager为例。

要在Spring中配置 JpaTransactionManager,只需要使用 org.springframework.orm.jpa.JpaTransactionManager 类在应用程序上下文 XML 文件中定义 bean,并添加一个到 JPA Entity Manager Factory bean的引用。然后,假定包含应用程序逻辑的类是由 Spring 管理的,将事务管理程序注入到 bean 中,如下:

<bean id="transactionManager"
         class="org.springframework.orm.jpa.JpaTransactionManager">
   <property name="entityManagerFactory" ref="entityManagerFactory"/>
</bean>

<bean id="tradingService" class="com.trading.service.TradingServiceImpl">
   <property name="txnManager" ref="transactionManager"/>
</bean> 


如果 Spring 未管理应用程序类,那么可以在您的方法中引用事务管理程序,方法是在 Spring 上下文中使用 getBean() 方法。

在源代码中,现在可以使用平台管理程序获取一个事务。执行了所有更新之后,可以调用 commit() 方法来提交事务,或者调用rollback() 方法来回滚事务。如下:

public class TradingServiceImpl  {
   @PersistenceContext(unitName="trading") EntityManager em;

   JpaTransactionManager txnManager = null;
   public void setTxnManager(JpaTransactionManager mgr) {
      txnManager = mgr;
   }

   public void processTrade(TradeData trade) throws Exception {
      TransactionStatus status =
         txnManager.getTransaction(new DefaultTransactionDefinition());
      try {
         em.persist(trade);
	 AcctData acct = em.find(AcctData.class, trade.getAcctId());
	 double tradeValue = trade.getPrice() * trade.getShares();
	 double currentBalance = acct.getBalance();
	 if (trade.getAction().equals("BUY")) {
	    acct.setBalance(currentBalance - tradeValue);
	 } else {
	    acct.setBalance(currentBalance + tradeValue);
	 }
         txnManager.commit(status);
      } catch (Exception up) {
         txnManager.rollback(status);
         throw up;
      }
   }
}


注意上面代码中 SpringFramework 与 EJB 3.0 之间的差异。在Spring 中,获取事务(随后启动它)的方法是在平台事务管理程序上调用getTransaction()。匿名 DefaultTransactionDefinition 类包含关于事务及其行为的详细信息,包括事务名称、隔离级别、传播模式(事务属性)和事务超时(如果存在)。在本例中,我们可以仅使用默认值,其名称是空字符串,底层 DBMS 的默认的隔离级别通常为READ_COMMITTED,事务属性是 PROPAGATION_REQUIRED,以及 DBMS 的默认超时。还需注意,commit() 和 rollback() 方法是使用平台事务管理程序调用的,而不是事务。

3、Declarative Transaction 模型

DeclarativeTransaction 模型,又称作 Container Managed Transactions (CMT),是 Java 平台中最常用的事务模型。在该模型中,容器环境负责启动、提交和回滚事务。开发人员仅负责指定事务的行为。

SpringFramework 和 EJB 3.0 都利用注释来指定事务行为。Spring 使用 @Transactional 注释,而 EJB 3.0 使用 @TransactionAttribute 注释。在使用 Declarative Transaction 模型时,容器将不会针对检测到的异常自动回滚事务。开发人员必须指定出现异常时在何处以及何时回滚事务。在 Spring Framework 中,通过使用 @Transactional 注释上的 rollbackFor 属性来指定。在 EJB 中,通过调用 SessionContext 上的 setRollbackOnly() 方法来指定。

十一、事务策略

上面描述的事务模型形成了事务策略的基础。在大多数业务应用程序场景中使用的主要事务策略包括:

1、Client Orchestration 事务策略

当来自客户机层的基于多服务器或基于模型的调用完成单一工作单元时,将使用 Client Orchestration 事务策略。此处的客户机层可以表示来自 Web 框架、门户应用程序、桌面系统或工作流产品或业务流程管理(BPM)组件的调用。从本质上说,客户机层拥有处理流以及完成特定请求所需的 “步骤”。举例来说,要添加一个交易订单,假定需要将交易插入到数据库中,然后更新客户的帐户余额以反映交易的值。如果应用程序的 API 层非常细粒度化,那么需要调用客户机层中的两个方法。在此场景中,事务工作单元必须位于客户机层中以确保自动化的工作单元。

2、API Layer 事务策略

当粗粒度方法充当后端功能的主要入口点时,将使用 API Layer 事务策略。(如果愿意,可以将它们称作 services)。在此场景中,客户机(基于 Web、基于 Web 服务、基于消息或者甚至桌面)向后端发起单个调用以执行特定的请求。使用上文提到的交易订单场景,在本例中将拥有一个入口点方法(比如说 processTrade())由客户机层调用。然后,这个方法将包含插入交易订单和更新帐户所需的业务流程。我将这个策略指定为这个名称的原因是,在大多数情况下,后端处理功能会通过使用接口或 API 向客户应用程序公开。这是最常用的事务策略之一。

3、High Concurrency 事务策略

High Concurrency 事务策略是 API Layer 事务策略的一个变体,它用于不支持通过 API 层长时间运行事务的应用程序(通常出于性能或可伸缩性需求的原因)。顾名思义,此策略主要在从用户的角度支持高度并发性的应用程序中使用。事务在 Java 平台的开销非常大。根据所使用的数据库,它们可以造成在数据库中出现锁定,独占资源,在吞吐量方面拖慢应用程序,并且在某些情况下甚至造成数据库死锁。这种事务策略内部的主要思想是缩短事务作用域,以便能够最大限度地减少数据库中的死锁,同时仍然为任何特定的客户机请求维持自动工作单元。在某些情况下,可能需要重构应用程序逻辑,以支持这种事务策略。

4、High-Speed Processing 事务策略

High-Speed Processing 事务策略或许是事务策略的最极端。如果您需要从应用程序中获取最快的处理时间(以及吞吐量),并且仍然在处理中维持一定程序的事务原子性,那么可以使用它。虽然这种策略从数据完整性和一致的角度来说引入了一些风险,但如果正确实现,它将成为 Java 平台中速度可能是最快的事务策略。它可能是这四个事务策略中最难以实现的一个。


相关阅读:

数据库系列一:事务(上)

数据库系列二:事务(中)



是时候关注一个只分享干货的公众号了

长按二维码 关注我们

JAVA北京(java_bj)



 
Java北京 更多文章 万亿级调用系统:微信序列号生成器架构设计及演变 为什么大型网站前端使用 PHP 后台逻辑用 Java? CAS集群解决方案 一种大批量数据(文件解析)的处理方案 京东618:多中心交易平台系统高压下的高可用性
猜您喜欢 Linux Netlink 基本使用 是时候表演真正的技术了,快快来 QCon 上海赶场! 【开放注册公告】吾爱破解论坛2016年11月11日光棍节开放注册公告 【大宝阅读】配色高手的养成攻略(下) 一周阅读推荐 #5