首页 > > > 07_transaction.pdf

07_transaction.pdf

07_transaction.pdf

上传者: 小公子 2011-06-21 评分1 评论0 下载7 收藏10 阅读量776 暂无简介 简介 举报

简介:本文档为《07_transactionpdf》,可适用于软件工程领域,主题内容包含数据库事务与并发n教学内容n数据库事务的概念n声明事务边界n并发问题n设置事务隔离级别n使用悲观锁解决并发问题n使用乐观锁解决并发问题PDF文件使用符等。

数据库事务与并发 n 教学内容 n 数据库事务的概念 n 声明事务边界 n 并发问题 n 设置事务隔离级别 n 使用悲观锁解决并发问题 n 使用乐观锁解决并发问题 PDF 文件使用 "pdfFactory" 试用版本创建 www.fineprint.com.cn 数据库事务的概念 n 事务是指一组相互依赖的操作行为,如银行交易、股票交易或网 上购物。事务的成功取决于这些相互依赖的操作行为是否都能执 行成功,只要有一个操作行为失败,就意味着整个事务失败。例 如,Tom到银行办理转账事务,把100元钱转到Jack的账号上,这 个事务包含以下操作行为: n (1)从Tom的账户上减去100元。 n (2)往Jack的账户上增加100元。 n 显然,以上两个操作必须作为一个不可分割的工作单元。假如仅 仅第一步操作执行成功,使得Tom的账户上扣除了100元,但是 第二步操作执行失败,Jack的账户上没有增加100元,那么整个 事务失败。 n 数据库事务是对现实生活中事务的模拟,它由一组在业务逻辑上 相互依赖的SQL语句组成。 PDF 文件使用 "pdfFactory" 试用版本创建 www.fineprint.com.cn 数据库事务的生命周期 PDF 文件使用 "pdfFactory" 试用版本创建 www.fineprint.com.cn MySQL数据库系统的客户程序 PDF 文件使用 "pdfFactory" 试用版本创建 www.fineprint.com.cn 声明事务的边界 n 事务的开始边界。 n 事务的正常结束边界(COMMIT):提交 事务,永久保存被事务更新后的数据库 状态。 n 事务的异常结束边界(ROLLBACK):撤 销事务,使数据库退回到执行事务前的 初始状态。 PDF 文件使用 "pdfFactory" 试用版本创建 www.fineprint.com.cn 在mysql.exe程序中声明事务 n 每启动一个mysql.exe程序,就会得到一个单独的数据 库连接。每个数据库连接都有个全局变量 @@autocommit,表示当前的事务模式,它有两个可 选值: n 0:表示手工提交模式。 n 1:默认值,表示自动提交模式。 n 如果要察看当前的事务模式,可使用如下SQL命令: n mysql> select @@autocommit n 如果要把当前的事务模式改为手工提交模式,可使用 如下SQL命令: n mysql> set autocommit=0; PDF 文件使用 "pdfFactory" 试用版本创建 www.fineprint.com.cn 在MySQL中创建支持事务的表 n 在MySQL中,数据库表分为三种类型:INNODB、BDB 和MyISAM类型。其中InnoDB和BDB类型的表支持数据 库事务,而MyISAM类型的表不支持事务。在MySQL中 用create table语句新建的表默认为MyISAM类型。如 果希望创建INNODB类型的表,可以采用以下形式的 DDL语句: create table ACCOUNTS ( ID bigint not null, NAME varchar(15), BALANCE decimal(10,2), primary key (ID) ) type=INNODB; PDF 文件使用 "pdfFactory" 试用版本创建 www.fineprint.com.cn 在自动提交模式下运行事务 n 在自动提交模式下,每个SQL语句都是一个独立的事务。 如果在一个mysql.exe程序中执行SQL语句: n mysql>insert into ACCOUNTS values(1,'Tom',1000); n MySQL会自动提交这个事务,这意味着向ACCOUNTS 表中新插入的记录会永久保存在数据库中。此时在另 一个mysql.exe程序中执行SQL语句: n mysql>select * from ACCOUNTS; n 这条select语句会查询到ID为1的ACCOUNTS记录。这 表明在第一个mysql.exe程序中插入的ACCOUNTS记录 被永久保存,这体现了事务的ACID特性中的永久性。 PDF 文件使用 "pdfFactory" 试用版本创建 www.fineprint.com.cn 在手工提交模式下运行事务 n 在手工提交模式下,必须显式指定事务 开始边界和结束边界: n 事务的开始边界:begin n 提交事务:commit n 撤销事务:rollback PDF 文件使用 "pdfFactory" 试用版本创建 www.fineprint.com.cn 在手工提交模式下运行事务 n (1)启动两个mysql.exe程序,在两个程序中都执行以下命令,以便设定手工提交事务模式: n mysql>set autocommit=0; n (2)在第一个mysql.exe程序中执行SQL语句: n mysql>begin; n mysql>insert into ACCOUNTS values(2, 'Jack',1000); n (3)在第二个mysql.exe程序中执行SQL语句: n mysql>begin; n mysql>select * from ACCOUNTS; n mysql>commit; n 以上select语句的查询结果中并不包含ID为2的ACCOUNTS记录,这是因为第一个mysql.exe程 序还没有提交事务。 n (4)在第一个mysql.exe程序中执行以下SQL语句,以便提交事务: n mysql>commit; n (5)在第二个mysql.exe程序中执行SQL语句: n mysql>begin; n mysql>select * from ACCOUNTS; n mysql>commit; n 此时,select语句的查询结果中会包含ID为2的ACCOUNTS记录,这是因为第一个mysql.exe程 序已经提交事务。 PDF 文件使用 "pdfFactory" 试用版本创建 www.fineprint.com.cn 通过JDBC API声明事务边界 n Connection类提供了以下用于控制事务 的方法: n setAutoCommit(boolean autoCommit):设 置是否自动提交事务 n commit():提交事务 n rollback():撤销事务 PDF 文件使用 "pdfFactory" 试用版本创建 www.fineprint.com.cn 通过JDBC API声明事务边界 try { con = java.sql.DriverManager.getConnection(dbUrl,dbUser,dbPwd); //设置手工提交事务模式 con.setAutoCommit(false); stmt = con.createStatement(); //数据库更新操作1 stmt.executeUpdate("update ACCOUNTS set BALANCE=900 where ID=1 "); //数据库更新操作2 stmt.executeUpdate("update ACCOUNTS set BALANCE=1000 where ID=2 "); con.commit(); //提交事务 }catch(Exception e) { try{ con.rollback(); //操作不成功则撤销事务 }catch(Exception ex){ //处理异常 …… } //处理异常 …… }finally{…} PDF 文件使用 "pdfFactory" 试用版本创建 www.fineprint.com.cn 通过Hibernate API声明事务边界 n 声明事务的开始边界: Transaction tx=session.beginTransaction(); n 提交事务: tx.commit(); n 撤销事务: tx.rollback(); PDF 文件使用 "pdfFactory" 试用版本创建 www.fineprint.com.cn 多个事务并发运行时的并发问题 n 第一类丢失更新:撤销一个事务时,把其他事务已提交的更新数 据覆盖。 n 脏读:一个事务读到另一事务未提交的更新数据。 n 虚读:一个事务读到另一事务已提交的新插入的数据。 n 不可重复读:一个事务读到另一事务已提交的更新数据。 n 第二类丢失更新:这是不可重复读中的特例,一个事务覆盖另一 事务已提交的更新数据。 PDF 文件使用 "pdfFactory" 试用版本创建 www.fineprint.com.cn 取款事务和支票转账事务 n 取款事务包含以下步骤: n (1)某银行客户在银行前台请求取款100元,出纳员先查询 账户信息,得知存款余额为1000元。 n (2)出纳员判断出存款额超过了取款额,就支付给客户100 元,并将账户上的存款余额改为900元。 n 支票转账事务包含以下步骤: n (1)某出纳员处理一转帐支票,该支票向一帐户汇入100元。 出纳员先查询账户信息,得知存款余额为900元。 n (2)出纳员将存款余额改为1000元。 PDF 文件使用 "pdfFactory" 试用版本创建 www.fineprint.com.cn 并发运行的两个事务导致脏读 取款事务在T5时刻把存款余额改为900元,支票转账事务在T6时刻查询 账户的存款余额为900元,取款事务在T7时刻被撤销,支票转账事务在 T8时刻把存款余额改为1000元。 由于支票转账事务查询到了取款事务未提交的更新数据,并且在这个查 询结果的基础上进行更新操作,如果取款事务最后被撤销,会导致银行 客户损失100元。 PDF 文件使用 "pdfFactory" 试用版本创建 www.fineprint.com.cn 并发运行的两个事务导致第二类丢失更新 取款事务在T5时刻根据在T3时刻的查询结果,把存款余额改为1000-100 元,在T6时刻提交事务。支票转账事务在T7时刻根据在T4时刻的查询结 果,把存款余额改为1000+100元。由于支票转账事务覆盖了取款事务对 存款余额所做的更新,导致银行最后损失100元。 PDF 文件使用 "pdfFactory" 试用版本创建 www.fineprint.com.cn 数据库的事务隔离级别 PDF 文件使用 "pdfFactory" 试用版本创建 www.fineprint.com.cn 隔离级别与并发性能的关系 PDF 文件使用 "pdfFactory" 试用版本创建 www.fineprint.com.cn 设定隔离级别的原则 n 隔离级别越高,越能保证数据的完整性和一致 性,但是对并发性能的影响也越大。 n 对于多数应用程序,可以优先考虑把数据库系 统的隔离级别设为Read Committed,它能够避 免脏读,而且具有较好的并发性能。尽管它会 导致不可重复读、虚读和第二类丢失更新这些 并发问题,在可能出现这类问题的个别场合, 可以由应用程序采用悲观锁或乐观锁来控制。 PDF 文件使用 "pdfFactory" 试用版本创建 www.fineprint.com.cn 在mysql.exe程序中设置隔离级别 n 每启动一个mysql.exe程序,就会获得一个单独的数据 库连接。每个数据库连接都有个全局变量 @@tx_isolation,表示当前的事务隔离级别。MySQL 默认的隔离级别为Repeatable Read。如果要察看当前 的隔离级别,可使用如下SQL命令: n mysql> select @@tx_isolation; n 如果要把当前mysql.exe程序的隔离级别改为Read Committed,可使用如下SQL命令: n mysql> set transaction isolation level read committed; PDF 文件使用 "pdfFactory" 试用版本创建 www.fineprint.com.cn 在Hibernate中设置隔离级别 n 在Hibernate的配置文件中可以显式的设置隔离级别。 每一种隔离级别都对应一个整数: n 1:Read Uncommitted n 2:Read Committed n 4:Repeatable Read n 8:Serializable n 例如,以下代码把hibernate.properties文件中的隔离 级别设为Read Committed: hibernate.connection.isolation=2 对于从数据库连接池中获得的每个连接,Hibernate都 会把它改为使用Read Committed隔离级别。 PDF 文件使用 "pdfFactory" 试用版本创建 www.fineprint.com.cn 使用悲观锁 Account account=(Account)session.get (Account.class,new Long(1), LockMode.UPGRADE); account.setBalance(account.getBalance()-100); Hibernate执行的select语句为: select * from ACCOUNTS where ID=1 for update; update ACCOUNTS set BALANCE=900… PDF 文件使用 "pdfFactory" 试用版本创建 www.fineprint.com.cn 利用悲观锁协调并发运行的取款事务和支票转账事务 PDF 文件使用 "pdfFactory" 试用版本创建 www.fineprint.com.cn 使用乐观锁 n 乐观锁是由应用程序提供的一种机制,这种机 制既能保证多个事务并发访问数据,又能防止 第二类丢失更新问题。 n 在应用程序中,可以利用Hibernate提供的版本 控制功能来实现乐观锁。对象-关系映射文件中 的<version>元素和<timestamp>元素都具有 版本控制功能: n <version>元素利用一个递增的整数来跟踪数据库 表中记录的版本 n <timestamp>元素用时间戳来跟踪数据库表中记录 的版本。 PDF 文件使用 "pdfFactory" 试用版本创建 www.fineprint.com.cn 利用<version>元素对ACCOUNTS表中记录进行版本控制 n (1)在Account类中定义一个代表版本信息的属性: private int version; public int getVersion() { return this.version; } public void setVersion(int version) { this.version = version; } PDF 文件使用 "pdfFactory" 试用版本创建 www.fineprint.com.cn 利用<version>元素对ACCOUNTS表中记录进行版本控制 n (2)在ACCOUNTS表中定义一个代表版本信息的字段: create table ACCOUNTS ( ID bigint not null, NAME varchar(15), BALANCE decimal(10,2), VERSION integer, primary key (ID) ) type=INNODB; PDF 文件使用 "pdfFactory" 试用版本创建 www.fineprint.com.cn 利用<version>元素对ACCOUNTS表中记录进行版本控制 n (3)在Account.hbm.xml文件中用<version>元素来建立Account 类的version属性与ACCOUNTS表中VERSION字段的映射: <id name="id" type="long" column="ID"> <generator class="increment"/> </id> <version name="version" column="VERSION" /> …… PDF 文件使用 "pdfFactory" 试用版本创建 www.fineprint.com.cn 利用<version>元素对ACCOUNTS表中记录进行版本控制 n 当Hibernate更新一个Account对象时,会根据它的id与version属 性到ACCOUNTS表中去定位匹配的记录,假定Account对象的 version属性为0,那么在取款事务中Hibernate执行的update语句 为: update ACCOUNTS set NAME=’Tom’,BALANCE=900,VERSION=1 where ID=1 and VERSION=0; n 如果存在匹配的记录,就更新这条记录,并且把VERSION字段的 值加1。当支票转账事务接着执行以下update语句时: update ACCOUNTS set NAME=’Tom’,BALANCE=1100,VERSION=1 where ID=1 and VERSION=0; n 由于ID为1的ACCOUNTS记录的版本已经被取款事务修改,因此 找不到匹配的记录,此时Hibernate会抛出 StaleObjectStateException。 PDF 文件使用 "pdfFactory" 试用版本创建 www.fineprint.com.cn 利用<version>元素对ACCOUNTS表中记录进行版本控制 n 在应用程序中应该捕获该异常,这种异 常有两种处理方式: n 方式一:自动撤销事务,通知用户账户信息 已被其他事务修改,需要重新开始事务。 n 方式二:通知用户账户信息已被其他事务修 改,显示最新存款余额信息,由用户决定如 何继续事务,用户也可以决定立刻撤销事务。 PDF 文件使用 "pdfFactory" 试用版本创建 www.fineprint.com.cn 利用<version>元素对ACCOUNTS表中记录进行版本控制 try { tx = session.beginTransaction(); log.write("transferCheck():开始事务"); Thread.sleep(500); Account account=(Account)session.get(Account.class,new Long(1)); log.write("transferCheck():查询到存款余额为:balance="+account.getBalance()); Thread.sleep(500); account.setBalance(account.getBalance()+100); log.write("transferCheck():汇入100元,把存款余额改为:"+account.getBalance()); tx.commit(); //当Hibernate执行update语句时,可能会抛出StaleObjectException log.write("transferCheck():提交事务"); Thread.sleep(500); }catch(StaleObjectStateException e){ if (tx != null) { tx.rollback(); } e.printStackTrace(); System.out.println("账户信息已被其他事务修改,本事务被撤销,请重新开始支票转账事务"); log.write("transferCheck():账户信息已被其他事务修改,本事务被撤销"); } PDF 文件使用 "pdfFactory" 试用版本创建 www.fineprint.com.cn 利用乐观锁协调并发的取款事务和支票转账事务 PDF 文件使用 "pdfFactory" 试用版本创建 www.fineprint.com.cn 实现乐观锁的其他方法 n 如果应用程序是基于已有的数据库,而数据库 表中不包含代表版本或时间戳的字段, Hibernate提供了其他实现乐观锁的办法。把 <class>元素的optimistic-lock属性设为“all”: <class name="Account" table="ACCOUNTS" optimistic-lock="all" dynamic-update="true"> n Hibernate会在update语句的where子句中包含 Account对象被加载时的所有属性: update ACCOUNTS set BALANCE=900 where ID=1 and NAME='Tom' and BALANCE='1000'; PDF 文件使用 "pdfFactory" 试用版本创建 www.fineprint.com.cn

该用户的其他资料

  • 名称/格式
  • 评分
  • 下载次数
  • 资料大小
  • 上传时间

用户评论

0/200
    暂无评论
上传我的资料

相关资料

资料评价:

/ 33
所需积分:1 立即下载
返回
顶部
举报
资料
关闭

温馨提示

感谢您对爱问共享资料的支持,精彩活动将尽快为您呈现,敬请期待!