`
crazyox
  • 浏览: 183052 次
  • 性别: Icon_minigender_2
  • 来自: 北京
社区版块
存档分类
最新评论

数据库事务

阅读更多

数据库事务是指作为单个逻辑工作单元执行的一系列操作。

设想网上购物的一次交易,其付款过程至少包括以下几步数据库操作:

  · 更新客户所购商品的库存信息

  · 保存客户付款信息--可能包括与银行系统的交互

  · 生成订单并且保存到数据库中

  · 更新用户相关信息,例如购物数量等等

正常的情况下,这些操作将顺利进行,最终交易成功,与交易相关的所有数据库信息也成功地更新。但是,如果在这一系列过程中任何一个环节出了差错,例如在更新商品库存信息时发生异常、该顾客银行帐户存款不足等,都将导致交易失败。一旦交易失败,数据库中所有信息都必须保持交易前的状态不变,比如最后一步更新用户信息时失败而导致交易失败,那么必须保证这笔失败的交易不影响数据库的状态--库存信息没有被更新、用户也没有付款,订单也没有生成。否则,数据库的信息将会一片混乱而不可预测。

数据库事务正是用来保证这种情况下交易的平稳性和可预测性的技术。

数据库事务的ACID属性

事务处理可以确保除非事务性单元内的所有操作都成功完成,否则不会永久更新面向数据的资源。通过将一组相关操作组合为一个要么全部成功要么全部失败的单元,可以简化错误恢复并使应用程序更加可靠。一个逻辑工作单元要成为事务,必须满足所谓的ACID(原子性、一致性、隔离性和持久性)属性:

  · 原子性

事务必须是原子工作单元;对于其数据修改,要么全都执行,要么全都不执行。通常,与某个事务关联的操作具有共同的目标,并且是相互依赖的。如果系统只执行这些操作的一个子集,则可能会破坏事务的总体目标。原子性消除了系统处理操作子集的可能性。

  · 一致性

事务在完成时,必须使所有的数据都保持一致状态。在相关数据库中,所有规则都必须应用于事务的修改,以保持所有数据的完整性。事务结束时,所有的内部数据结构(如 B 树索引或双向链表)都必须是正确的。某些维护一致性的责任由应用程序开发人员承担,他们必须确保应用程序已强制所有已知的完整性约束。例如,当开发用于转帐的应用程序时,应避免在转帐过程中任意移动小数点。

  · 隔离性

由并发事务所作的修改必须与任何其它并发事务所作的修改隔离。事务查看数据时数据所处的状态,要么是另一并发事务修改它之前的状态,要么是另一事务修改它之后的状态,事务不会查看中间状态的数据。这称为可串行性,因为它能够重新装载起始数据,并且重播一系列事务,以使数据结束时的状态与原始事务执行的状态相同。当事务可序列化时将获得最高的隔离级别。在此级别上,从一组可并行执行的事务获得的结果与通过连续运行每个事务所获得的结果相同。由于高度隔离会限制可并行执行的事务数,所以一些应用程序降低隔离级别以换取更大的吞吐量。

  · 持久性

事务完成之后,它对于系统的影响是永久性的。该修改即使出现致命的系统故障也将一直保持。


DBMS的责任和我们的任务

企业级的数据库管理系统(DBMS)都有责任提供一种保证事务的物理完整性的机制。就常用的SQL Server2000系统而言,它具备锁定设备隔离事务、记录设备保证事务持久性等机制。因此,我们不必关心数据库事务的物理完整性,而应该关注在什么情况下使用数据库事务、事务对性能的影响,如何使用事务等等。

  体验SQL语言的事务机制

作为大型的企业级数据库,SQL Server2000对事务提供了很好的支持。我们可以使用SQL语句来定义、提交以及回滚一个事务。

如下所示的SQL代码定义了一个事务,并且命名为"MyTransaction"(限于篇幅,本文并不讨论如何编写SQL语言程序,请读者自行参考相关书籍):

sql 代码
  1. DECLARE @TranName VARCHAR(20)   
  2.   
  3. SELECT @TranName = 'MyTransaction'    
  4. BEGIN TRANSACTION @TranNameGOUSE pubs   
  5. GO   
  6.   
  7. UPDATE roysched   
  8. SET royalty = royalty * 1.10   
  9. WHERE title_id LIKE 'Pc%'   
  10. GO   
  11.   
  12. COMMIT TRANSACTION MyTransaction   
  13. GO  


这里用到了SQL Server2000自带的示例数据库pubs,提交事务后,将为所有畅销计算机书籍支付的版税增加 10%。

打开SQL Server2000的查询分析器,选择pubs数据库,然后运行这段程序,结果显而易见。

可是如何在C#程序中运行呢?我们记得在普通的SQL查询中,一般需要把查询语句赋值给SalCommand.CommandText属性,这里也就像普通的SQL查询语句一样,将这些语句赋给SqlCommand.CommandText属性即可。要注意的一点是,其中的"GO"语句标志着SQL批处理的结束,编写SQL脚本是需要的,但是在这里是不必要的。我们可以编写如下的程序来验证这个想法:

c# 代码
  1. //TranSql.csusing System;   
  2. using System.Data;   
  3. using System.Data.SqlClient;   
  4. namespace Aspcn   
  5. {   
  6.  public class DbTranSql   
  7.  {   
  8.   file://将事务放到SQL Server中执行   
  9.   public void DoTran()   
  10.   {   
  11.    file://建立连接并打开   
  12.    SqlConnection myConn=GetConn();myConn.Open();   
  13.    SqlCommand myComm=new SqlCommand();   
  14.    try  
  15.    {   
  16.     myComm.Connection=myConn;   
  17.     myComm.CommandText="DECLARE @TranName VARCHAR(20) ";   
  18.     myComm.CommandText+="SELECT @TranName = 'MyTransaction' ";   
  19.     myComm.CommandText+="BEGIN TRANSACTION @TranName ";   
  20.     myComm.CommandText+="USE pubs ";   
  21.     myComm.CommandText+="UPDATE roysched SET royalty = royalty * 1.10 WHERE title_id LIKE 'Pc%' ";   
  22.     myComm.CommandText+="COMMIT TRANSACTION MyTransaction ";   
  23.     myComm.ExecuteNonQuery();   
  24.    }   
  25.    catch(Exception err)   
  26.    {   
  27.     throw new ApplicationException("事务操作出错,系统信息:"+err.Message);   
  28.    }   
  29.    finally  
  30.    {   
  31.     myConn.Close();   
  32.    }   
  33.   }   
  34.   file://获取数据连接   
  35.   private SqlConnection GetConn()   
  36.   {   
  37.    string strSql="Data Source=localhost;Integrated Security=SSPI;user id=sa;password=";   
  38.    SqlConnection myConn=new SqlConnection(strSql);   
  39.    return myConn;   
  40.   }   
  41.  }   
  42.   
  43.  public class Test   
  44.  {   
  45.   public static void Main()   
  46.   {   
  47.    DbTranSql tranTest=new DbTranSql();   
  48.    tranTest.DoTran();   
  49.    Console.WriteLine("事务处理已经成功完成。");   
  50.    Console.ReadLine();   
  51.   }   
  52.  }   
  53. }  

注意到其中的SqlCommand对象myComm,它的CommandText属性仅仅是前面SQL代码字符串连接起来即可,当然,其中的"GO"语句已经全部去掉了。这个语句就像普通的查询一样,程序将SQL文本事实上提交给DBMS去处理了,然后接收返回的结果(如果有结果返回的话)。

很自然,我们最后看到了输出"事务处理已经成功完成",再用企业管理器查看pubs数据库的roysched表,所有title_id字段以"PC"开头的书籍的royalty字段的值都增加了0.1倍。

这里,我们并没有使用ADO.net的事务处理机制,而是简单地将执行事务的SQL语句当作普通的查询来执行,因此,事实上该事务完全没有用到.net的相关特性。
了解.net中的事务机制

如你所知,在.net框架中主要有两个命名空间(namespace)用于应用程序同数据库系统的交互:System.Data.SqlClient和System.Data.OleDb。前者专门用于连接Microsoft公司自己的SQL Server数据库,而后者可以适应多种不同的数据库。这两个命名空间中都包含有专门用于管理数据库事务的类,分别是System.Data.SqlClient.SqlTranscation类和System.Data.OleDb.OleDbTranscation类。

就像它们的名字一样,这两个类大部分功能是一样的,二者之间的主要差别在于它们的连接机制,前者提供一组直接调用 SQL Server 的对象,而后者使用本机 OLE DB 启用数据访问。 事实上,ADO.net 事务完全在数据库的内部处理,且不受 Microsoft 分布式事务处理协调器 (DTC) 或任何其他事务性机制的支持。本文将主要介绍System.Data.SqlClient.SqlTranscation类,下面的段落中,除了特别注明,都将使用System.Data.SqlClient.SqlTranscation类。

http://www.blog.edu.cn/user2/60402/archives/2007/1763148.shtml

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics