<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    I'll be back!

      Focus on BPM, celebrate PegaRULES Process Commander (PRPC)
    posts - 76, comments - 161, trackbacks - 0, articles - 2
      BlogJava :: 首頁(yè) :: 新隨筆 :: 聯(lián)系 :: 聚合  :: 管理

    數(shù)據(jù)庫(kù)事務(wù)特征

    Posted on 2007-10-24 00:09 zolly 閱讀(1273) 評(píng)論(0)  編輯  收藏
    什么是數(shù)據(jù)庫(kù)事務(wù)

      數(shù)據(jù)庫(kù)事務(wù)是指作為單個(gè)邏輯工作單元執(zhí)行的一系列操作。

    設(shè)想網(wǎng)上購(gòu)物的一次交易,其付款過(guò)程至少包括以下幾步數(shù)據(jù)庫(kù)操作:

      · 更新客戶所購(gòu)商品的庫(kù)存信息

      · 保存客戶付款信息--可能包括與銀行系統(tǒng)的交互

      · 生成訂單并且保存到數(shù)據(jù)庫(kù)中

      · 更新用戶相關(guān)信息,例如購(gòu)物數(shù)量等等

    正常的情況下,這些操作將順利進(jìn)行,最終交易成功,與交易相關(guān)的所有數(shù)據(jù)庫(kù)信息也成功地更新。但是,如果在這一系列過(guò)程中任何一個(gè)環(huán)節(jié)出了差錯(cuò),例如在更新商品庫(kù)存信息時(shí)發(fā)生異常、該顧客銀行帳戶存款不足等,都將導(dǎo)致交易失敗。一旦交易失敗,數(shù)據(jù)庫(kù)中所有信息都必須保持交易前的狀態(tài)不變,比如最后一步更新用戶信息時(shí)失敗而導(dǎo)致交易失敗,那么必須保證這筆失敗的交易不影響數(shù)據(jù)庫(kù)的狀態(tài)--庫(kù)存信息沒(méi)有被更新、用戶也沒(méi)有付款,訂單也沒(méi)有生成。否則,數(shù)據(jù)庫(kù)的信息將會(huì)一片混亂而不可預(yù)測(cè)。

    數(shù)據(jù)庫(kù)事務(wù)正是用來(lái)保證這種情況下交易的平穩(wěn)性和可預(yù)測(cè)性的技術(shù)。

      數(shù)據(jù)庫(kù)事務(wù)的ACID屬性

    事務(wù)處理可以確保除非事務(wù)性單元內(nèi)的所有操作都成功完成,否則不會(huì)永久更新面向數(shù)據(jù)的資源。通過(guò)將一組相關(guān)操作組合為一個(gè)要么全部成功要么全部失敗的單元,可以簡(jiǎn)化錯(cuò)誤恢復(fù)并使應(yīng)用程序更加可靠。一個(gè)邏輯工作單元要成為事務(wù),必須滿足所謂的ACID(原子性、一致性、隔離性和持久性)屬性:

      · 原子性

    事務(wù)必須是原子工作單元;對(duì)于其數(shù)據(jù)修改,要么全都執(zhí)行,要么全都不執(zhí)行。通常,與某個(gè)事務(wù)關(guān)聯(lián)的操作具有共同的目標(biāo),并且是相互依賴的。如果系統(tǒng)只執(zhí)行這些操作的一個(gè)子集,則可能會(huì)破壞事務(wù)的總體目標(biāo)。原子性消除了系統(tǒng)處理操作子集的可能性。

      · 一致性

    事務(wù)在完成時(shí),必須使所有的數(shù)據(jù)都保持一致狀態(tài)。在相關(guān)數(shù)據(jù)庫(kù)中,所有規(guī)則都必須應(yīng)用于事務(wù)的修改,以保持所有數(shù)據(jù)的完整性。事務(wù)結(jié)束時(shí),所有的內(nèi)部數(shù)據(jù)結(jié)構(gòu)(如 B 樹索引或雙向鏈表)都必須是正確的。某些維護(hù)一致性的責(zé)任由應(yīng)用程序開發(fā)人員承擔(dān),他們必須確保應(yīng)用程序已強(qiáng)制所有已知的完整性約束。例如,當(dāng)開發(fā)用于轉(zhuǎn)帳的應(yīng)用程序時(shí),應(yīng)避免在轉(zhuǎn)帳過(guò)程中任意移動(dòng)小數(shù)點(diǎn)。

      · 隔離性

    由并發(fā)事務(wù)所作的修改必須與任何其它并發(fā)事務(wù)所作的修改隔離。事務(wù)查看數(shù)據(jù)時(shí)數(shù)據(jù)所處的狀態(tài),要么是另一并發(fā)事務(wù)修改它之前的狀態(tài),要么是另一事務(wù)修改它之后的狀態(tài),事務(wù)不會(huì)查看中間狀態(tài)的數(shù)據(jù)。這稱為可串行性,因?yàn)樗軌蛑匦卵b載起始數(shù)據(jù),并且重播一系列事務(wù),以使數(shù)據(jù)結(jié)束時(shí)的狀態(tài)與原始事務(wù)執(zhí)行的狀態(tài)相同。當(dāng)事務(wù)可序列化時(shí)將獲得最高的隔離級(jí)別。在此級(jí)別上,從一組可并行執(zhí)行的事務(wù)獲得的結(jié)果與通過(guò)連續(xù)運(yùn)行每個(gè)事務(wù)所獲得的結(jié)果相同。由于高度隔離會(huì)限制可并行執(zhí)行的事務(wù)數(shù),所以一些應(yīng)用程序降低隔離級(jí)別以換取更大的吞吐量。

      · 持久性

    事務(wù)完成之后,它對(duì)于系統(tǒng)的影響是永久性的。該修改即使出現(xiàn)致命的系統(tǒng)故障也將一直保持。
    DBMS的責(zé)任和我們的任務(wù)

    企業(yè)級(jí)的數(shù)據(jù)庫(kù)管理系統(tǒng)(DBMS)都有責(zé)任提供一種保證事務(wù)的物理完整性的機(jī)制。就常用的SQL Server2000系統(tǒng)而言,它具備鎖定設(shè)備隔離事務(wù)、記錄設(shè)備保證事務(wù)持久性等機(jī)制。因此,我們不必關(guān)心數(shù)據(jù)庫(kù)事務(wù)的物理完整性,而應(yīng)該關(guān)注在什么情況下使用數(shù)據(jù)庫(kù)事務(wù)、事務(wù)對(duì)性能的影響,如何使用事務(wù)等等。

    本文將涉及到在.net框架下使用C#語(yǔ)言操縱數(shù)據(jù)庫(kù)事務(wù)的各個(gè)方面。

     

      體驗(yàn)SQL語(yǔ)言的事務(wù)機(jī)制

    作為大型的企業(yè)級(jí)數(shù)據(jù)庫(kù),SQL Server2000對(duì)事務(wù)提供了很好的支持。我們可以使用SQL語(yǔ)句來(lái)定義、提交以及回滾一個(gè)事務(wù)。

    如下所示的SQL代碼定義了一個(gè)事務(wù),并且命名為"MyTransaction"(限于篇幅,本文并不討論如何編寫SQL語(yǔ)言程序,請(qǐng)讀者自行參考相關(guān)書籍):

    DECLARE @TranName VARCHAR(20)

    SELECT @TranName = 'MyTransaction'
    BEGIN TRANSACTION @TranNameGOUSE pubs
    GO

    UPDATE roysched
    SET royalty = royalty * 1.10
    WHERE title_id LIKE 'Pc%'
    GO

    COMMIT TRANSACTION MyTransaction
    GO

    這里用到了SQL Server2000自帶的示例數(shù)據(jù)庫(kù)pubs,提交事務(wù)后,將為所有暢銷計(jì)算機(jī)書籍支付的版稅增加 10%。

    打開SQL Server2000的查詢分析器,選擇pubs數(shù)據(jù)庫(kù),然后運(yùn)行這段程序,結(jié)果顯而易見(jiàn)。

    可是如何在C#程序中運(yùn)行呢?我們記得在普通的SQL查詢中,一般需要把查詢語(yǔ)句賦值給SalCommand.CommandText屬性,這里也就像普通的SQL查詢語(yǔ)句一樣,將這些語(yǔ)句賦給SqlCommand.CommandText屬性即可。要注意的一點(diǎn)是,其中的"GO"語(yǔ)句標(biāo)志著SQL批處理的結(jié)束,編寫SQL腳本是需要的,但是在這里是不必要的。我們可以編寫如下的程序來(lái)驗(yàn)證這個(gè)想法:

    //TranSql.csusing System;
    using System.Data;
    using System.Data.SqlClient;
    namespace Aspcn
    {
     public class DbTranSql
     {
      file://將事務(wù)放到SQL Server中執(zhí)行
      public void DoTran()
      {
       file://建立連接并打開
       SqlConnection myConn=GetConn();myConn.Open();
       SqlCommand myComm=new SqlCommand();
       try
       {
        myComm.Connection=myConn;
        myComm.CommandText="DECLARE @TranName VARCHAR(20) ";
        myComm.CommandText+="SELECT @TranName = 'MyTransaction' ";
        myComm.CommandText+="BEGIN TRANSACTION @TranName ";
        myComm.CommandText+="USE pubs ";
        myComm.CommandText+="UPDATE roysched SET royalty = royalty * 1.10 WHERE title_id LIKE 'Pc%' ";
        myComm.CommandText+="COMMIT TRANSACTION MyTransaction ";
        myComm.ExecuteNonQuery();
       }
       catch(Exception err)
       {
        throw new ApplicationException("事務(wù)操作出錯(cuò),系統(tǒng)信息:"+err.Message);
       }
       finally
       {
        myConn.Close();
       }
      }
      file://獲取數(shù)據(jù)連接
      private SqlConnection GetConn()
      {
       string strSql="Data Source=localhost;Integrated Security=SSPI;user id=sa;password=";
       SqlConnection myConn=new SqlConnection(strSql);
       return myConn;
      }
     }

     public class Test
     {
      public static void Main()
      {
       DbTranSql tranTest=new DbTranSql();
       tranTest.DoTran();
       Console.WriteLine("事務(wù)處理已經(jīng)成功完成。");
       Console.ReadLine();
      }
     }
    }


      注意到其中的SqlCommand對(duì)象myComm,它的CommandText屬性僅僅是前面SQL代碼字符串連接起來(lái)即可,當(dāng)然,其中的"GO"語(yǔ)句已經(jīng)全部去掉了。這個(gè)語(yǔ)句就像普通的查詢一樣,程序?qū)QL文本事實(shí)上提交給DBMS去處理了,然后接收返回的結(jié)果(如果有結(jié)果返回的話)。

    很自然,我們最后看到了輸出"事務(wù)處理已經(jīng)成功完成",再用企業(yè)管理器查看pubs數(shù)據(jù)庫(kù)的roysched表,所有title_id字段以"PC"開頭的書籍的royalty字段的值都增加了0.1倍。

    這里,我們并沒(méi)有使用ADO.net的事務(wù)處理機(jī)制,而是簡(jiǎn)單地將執(zhí)行事務(wù)的SQL語(yǔ)句當(dāng)作普通的查詢來(lái)執(zhí)行,因此,事實(shí)上該事務(wù)完全沒(méi)有用到.net的相關(guān)特性。
    了解.net中的事務(wù)機(jī)制

    如你所知,在.net框架中主要有兩個(gè)命名空間(namespace)用于應(yīng)用程序同數(shù)據(jù)庫(kù)系統(tǒng)的交互:System.Data.SqlClient和System.Data.OleDb。前者專門用于連接Microsoft公司自己的SQL Server數(shù)據(jù)庫(kù),而后者可以適應(yīng)多種不同的數(shù)據(jù)庫(kù)。這兩個(gè)命名空間中都包含有專門用于管理數(shù)據(jù)庫(kù)事務(wù)的類,分別是System.Data.SqlClient.SqlTranscation類和System.Data.OleDb.OleDbTranscation類。

    就像它們的名字一樣,這兩個(gè)類大部分功能是一樣的,二者之間的主要差別在于它們的連接機(jī)制,前者提供一組直接調(diào)用 SQL Server 的對(duì)象,而后者使用本機(jī) OLE DB 啟用數(shù)據(jù)訪問(wèn)。 事實(shí)上,ADO.net 事務(wù)完全在數(shù)據(jù)庫(kù)的內(nèi)部處理,且不受 Microsoft 分布式事務(wù)處理協(xié)調(diào)器 (DTC) 或任何其他事務(wù)性機(jī)制的支持。本文將主要介紹System.Data.SqlClient.SqlTranscation類,下面的段落中,除了特別注明,都將使用System.Data.SqlClient.SqlTranscation類。

     

      事務(wù)的開啟和提交

    現(xiàn)在我們對(duì)事務(wù)的概念和原理都了然于心了,并且作為已經(jīng)有一些基礎(chǔ)的C#開發(fā)者,我們已經(jīng)熟知編寫數(shù)據(jù)庫(kù)交互程序的一些要點(diǎn),即使用SqlConnection類的對(duì)象的Open()方法建立與數(shù)據(jù)庫(kù)服務(wù)器的連接,然后將該連接賦給SqlCommand對(duì)象的Connection屬性,將欲執(zhí)行的SQL語(yǔ)句賦給它的CommandText屬性,于是就可以通過(guò)SqlCommand對(duì)象進(jìn)行數(shù)據(jù)庫(kù)操作了。對(duì)于我們將要編寫的事務(wù)處理程序,當(dāng)然還需要定義一個(gè)SqlTransaction類型的對(duì)象。并且看到SqlCommand對(duì)象的Transcation屬性,我們很容易想到新建的SqlTransaction對(duì)象應(yīng)該與它關(guān)聯(lián)起來(lái)。

    基于以上認(rèn)識(shí),下面我們就開始動(dòng)手寫我們的第一個(gè)事務(wù)處理程序。我們可以很熟練地寫出下面這一段程序:

    //DoTran.csusing System;
    using System.Data;
    using System.Data.SqlClient;
    namespace Aspcn
    {
     public class DbTran
     {
      file://執(zhí)行事務(wù)處理
      public void DoTran()
      {
       file://建立連接并打開
       SqlConnection myConn=GetConn();
       myConn.Open();
       SqlCommand myComm=new SqlCommand();
       SqlTransaction myTran=new SqlTransaction();
       try
       {
        myComm.Connection=myConn;
        myComm.Transaction=myTran;
       
        file://定位到pubs數(shù)據(jù)庫(kù) 
        myComm.CommandText="USE pubs";
        myComm.ExecuteNonQuery();

        file://更新數(shù)據(jù)
        file://將所有的計(jì)算機(jī)類圖書
        myComm.CommandText="UPDATE roysched SET royalty = royalty * 1.10 WHERE title_id LIKE 'Pc%'";
        myComm.ExecuteNonQuery();//提交事務(wù)
        myTran.Commit();
       }
       catch(Exception err)
       {
        throw new ApplicationException("事務(wù)操作出錯(cuò),系統(tǒng)信息:"+err.Message);
       }
       finally
       {
        myConn.Close();
       }
      }
      file://獲取數(shù)據(jù)連接
      private SqlConnection GetConn()
      {
       string strSql="Data Source=localhost;Integrated Security=SSPI;user id=sa;password=";
       SqlConnection myConn=new SqlConnection(strSql);
       return myConn;
      }
     }

     public class Test{public static void Main()
     {
      DbTran tranTest=new DbTran();
      tranTest.DoTran();
      Console.WriteLine("事務(wù)處理已經(jīng)成功完成。");
      Console.ReadLine();
     }
    }
    }

    顯然,這個(gè)程序非常簡(jiǎn)單,我們非常自信地編譯它,但是,出乎意料的結(jié)果使我們的成就感頓時(shí)煙消云散:

    error CS1501: 重載"SqlTransaction"方法未獲取"0"參數(shù)

    是什么原因呢?注意到我們初始化的代碼:

    SqlTransaction myTran=new SqlTransaction();

    顯然,問(wèn)題出在這里,事實(shí)上,SqlTransaction類并沒(méi)有公共的構(gòu)造函數(shù),我們不能這樣新建一個(gè)SqlTrancaction類型的變量。在事務(wù)處理之前確實(shí)需要有一個(gè)SqlTransaction類型的變量,將該變量關(guān)聯(lián)到SqlCommand類的Transcation屬性也是必要的,但是初始化方法卻比較特別一點(diǎn)。在初始化SqlTransaction類時(shí),你需要使用SqlConnection類的BeginTranscation()方法:

    SqlTransaction myTran; myTran=myConn.BeginTransaction();
      
    該方法返回一個(gè)SqlTransaction類型的變量。在調(diào)用BeginTransaction()方法以后,所有基于該數(shù)據(jù)連接對(duì)象的SQL語(yǔ)句執(zhí)行動(dòng)作都將被認(rèn)為是事務(wù)MyTran的一部分。同時(shí),你也可以在該方法的參數(shù)中指定事務(wù)隔離級(jí)別和事務(wù)名稱,如:

    SqlTransaction myTran;
    myTran=myConn.BeginTransaction(IsolationLevel.ReadCommitted,"SampleTransaction");
      
    關(guān)于隔離級(jí)別的概念我們將在隨后的內(nèi)容中探討,在這里我們只需牢記一個(gè)事務(wù)是如何被啟動(dòng),并且關(guān)聯(lián)到特定的數(shù)據(jù)鏈接的。

    先不要急著去搞懂我們的事務(wù)都干了些什么,看到這一行:

    myTran.Commit();

    是的,這就是事務(wù)的提交方式。該語(yǔ)句執(zhí)行后,事務(wù)的所有數(shù)據(jù)庫(kù)操作將生效,并且為數(shù)據(jù)庫(kù)事務(wù)的持久性機(jī)制所保持--即使系統(tǒng)在這以后發(fā)生致命錯(cuò)誤,該事務(wù)對(duì)數(shù)據(jù)庫(kù)的影響也不會(huì)消失。

    對(duì)上面的程序做了修改之后我們可以得到如下代碼(為了節(jié)約篇幅,重復(fù)之處已省略,請(qǐng)參照前文):

    //DoTran.cs……}

    file://執(zhí)行事務(wù)處理
    public void DoTran()
    {
     file://建立連接并打開
     SqlConnection myConn=GetConn();
     myConn.Open();
     SqlCommand myComm=new SqlCommand();

     file://SqlTransaction myTran=new SqlTransaction();
     file://注意,SqlTransaction類無(wú)公開的構(gòu)造函數(shù)

     SqlTransaction myTran;

     file://創(chuàng)建一個(gè)事務(wù)
     myTran=myConn.BeginTransaction();
     try
     {
      file://從此開始,基于該連接的數(shù)據(jù)操作都被認(rèn)為是事務(wù)的一部分
      file://下面綁定連接和事務(wù)對(duì)象
      myComm.Connection=myConn;
      myComm.Transaction=myTran; file://定位到pubs數(shù)據(jù)庫(kù)
      myComm.CommandText="USE pubs";
      myComm.ExecuteNonQuery();//更新數(shù)據(jù)
      file://將所有的計(jì)算機(jī)類圖書
      myComm.CommandText="UPDATE roysched SET royalty = royalty * 1.10 WHERE title_id LIKE 'Pc%'";
      myComm.ExecuteNonQuery();
     
      file://提交事務(wù)
      myTran.Commit();
     }
     catch(Exception err)
     {
      throw new ApplicationException("事務(wù)操作出錯(cuò),系統(tǒng)信息:"+err.Message);
      }
     finally
     {
      myConn.Close();
      }
    }
    ……


    到此為止,我們僅僅掌握了如何開始和提交事務(wù)。下一步我們必須考慮的是在事務(wù)中可以干什么和不可以干什么。

     另一個(gè)走向極端的錯(cuò)誤

    滿懷信心的新手們可能為自己所掌握的部分知識(shí)陶醉不已,剛接觸數(shù)據(jù)庫(kù)庫(kù)事務(wù)處理的準(zhǔn)開發(fā)者們也一樣,躊躇滿志地準(zhǔn)備將事務(wù)機(jī)制應(yīng)用到他的數(shù)據(jù)處理程序的每一個(gè)模塊每一條語(yǔ)句中去。的確,事務(wù)機(jī)制看起來(lái)是如此的誘人——簡(jiǎn)潔、美妙而又實(shí)用,我當(dāng)然想用它來(lái)避免一切可能出現(xiàn)的錯(cuò)誤——我甚至想用事務(wù)把我的數(shù)據(jù)操作從頭到尾包裹起來(lái)。

    看著吧,下面我要從創(chuàng)建一個(gè)數(shù)據(jù)庫(kù)開始:

    using System;
    using System.Data;
    using System.Data.SqlClient;

    namespace Aspcn
    {
     public class DbTran
     {
      file://執(zhí)行事務(wù)處理
      public void DoTran()
      {
       file://建立連接并打開
       SqlConnection myConn=GetConn();
       myConn.Open();

       SqlCommand myComm=new SqlCommand();
       SqlTransaction myTran;

       myTran=myConn.BeginTransaction();

       file://下面綁定連接和事務(wù)對(duì)象
       myComm.Connection=myConn;
       myComm.Transaction=myTran;

       file://試圖創(chuàng)建數(shù)據(jù)庫(kù)TestDB
       myComm.CommandText="CREATE database TestDB";
       myComm.ExecuteNonQuery();

       file://提交事務(wù)
       myTran.Commit();
      }

      file://獲取數(shù)據(jù)連接
      private SqlConnection GetConn()
      {
       string strSql="Data Source=localhost;Integrated Security=SSPI;user id=sa;password=";
       SqlConnection myConn=new SqlConnection(strSql);
       return myConn;
      }
     }

     public class Test
     {
      public static void Main()
      {
       DbTran tranTest=new DbTran();
       tranTest.DoTran();
       Console.WriteLine("事務(wù)處理已經(jīng)成功完成。");
       Console.ReadLine();
      }
     }
    }

    //---------------

      未處理的異常: System.Data.SqlClient.SqlException: 在多語(yǔ)句事務(wù)內(nèi)不允許使用 CREATE DATABASE 語(yǔ)句。

    at System.Data.SqlClient.SqlCommand.ExecuteNonQuery()
    at Aspcn.DbTran.DoTran()
    at Aspcn.Test.Main()

    注意,如下的SQL語(yǔ)句不允許出現(xiàn)在事務(wù)中:

    ALTER DATABASE 修改數(shù)據(jù)庫(kù)
    BACKUP LOG 備份日志
    CREATE DATABASE 創(chuàng)建數(shù)據(jù)庫(kù)
    DISK INIT 創(chuàng)建數(shù)據(jù)庫(kù)或事務(wù)日志設(shè)備
    DROP DATABASE 刪除數(shù)據(jù)庫(kù)
    DUMP TRANSACTION 轉(zhuǎn)儲(chǔ)事務(wù)日志
    LOAD DATABASE 裝載數(shù)據(jù)庫(kù)備份復(fù)本
    LOAD TRANSACTION 裝載事務(wù)日志備份復(fù)本
    RECONFIGURE 更新使用 sp_configure 系統(tǒng)存儲(chǔ)過(guò)程更改的配置選項(xiàng)的當(dāng)前配置(sp_configure 結(jié)果集中的 config_value 列)值。
    RESTORE DATABASE 還原使用BACKUP命令所作的數(shù)據(jù)庫(kù)備份
    RESTORE LOG 還原使用BACKUP命令所作的日志備份
    UPDATE STATISTICS 在指定的表或索引視圖中,對(duì)一個(gè)或多個(gè)統(tǒng)計(jì)組(集合)有關(guān)鍵值分發(fā)的信息進(jìn)行更新

    除了這些語(yǔ)句以外,你可以在你的數(shù)據(jù)庫(kù)事務(wù)中使用任何合法的SQL語(yǔ)句。

     

      事務(wù)回滾

    事務(wù)的四個(gè)特性之一是原子性,其含義是指對(duì)于特定操作序列組成的事務(wù),要么全部完成,要么就一件也不做。如果在事務(wù)處理的過(guò)程中,發(fā)生未知的不可預(yù)料的錯(cuò)誤,如何保證事務(wù)的原子性呢?當(dāng)事務(wù)中止時(shí),必須執(zhí)行回滾操作,以便消除已經(jīng)執(zhí)行的操作對(duì)數(shù)據(jù)庫(kù)的影響。

    一般的情況下,在異常處理中使用回滾動(dòng)作是比較好的想法。前面,我們已經(jīng)得到了一個(gè)更新數(shù)據(jù)庫(kù)的程序,并且驗(yàn)證了它的正確性,稍微修改一下,可以得到:

     

    //RollBack.cs
    using System;
    using System.Data;
    using System.Data.SqlClient;

    namespace Aspcn
    {
     public class DbTran
     {
      file://執(zhí)行事務(wù)處理
      public void DoTran()
      {
       file://建立連接并打開
       SqlConnection myConn=GetConn();
       myConn.Open();

       SqlCommand myComm=new SqlCommand();
       SqlTransaction myTran;

       file://創(chuàng)建一個(gè)事務(wù)
       myTran=myConn.BeginTransaction();
       file://從此開始,基于該連接的數(shù)據(jù)操作都被認(rèn)為是事務(wù)的一部分
       file://下面綁定連接和事務(wù)對(duì)象
       myComm.Connection=myConn;
       myComm.Transaction=myTran;

       try
       {
        file://定位到pubs數(shù)據(jù)庫(kù)
        myComm.CommandText="USE pubs";
        myComm.ExecuteNonQuery();
       
        myComm.CommandText="UPDATE roysched SET royalty = royalty * 1.10 WHERE title_id LIKE 'Pc%'";
        myComm.ExecuteNonQuery();

        file://下面使用創(chuàng)建數(shù)據(jù)庫(kù)的語(yǔ)句制造一個(gè)錯(cuò)誤
        myComm.CommandText="Create database testdb";
        myComm.ExecuteNonQuery();

        myComm.CommandText="UPDATE roysched SET royalty = royalty * 1.20 WHERE title_id LIKE 'Ps%'";
        myComm.ExecuteNonQuery();

        file://提交事務(wù)
        myTran.Commit();
       }
       catch(Exception err)
       {
        myTran.Rollback();
        Console.Write("事務(wù)操作出錯(cuò),已回滾。系統(tǒng)信息:"+err.Message);
       }
      }

      file://獲取數(shù)據(jù)連接
      private SqlConnection GetConn()
      {
       string strSql="Data Source=localhost;Integrated Security=SSPI;user id=sa;password=";
       SqlConnection myConn=new SqlConnection(strSql);
       return myConn;
      }
     }
     public class Test
     {
      public static void Main()
      {
       DbTran tranTest=new DbTran();
       tranTest.DoTran();
       Console.WriteLine("事務(wù)處理已經(jīng)成功完成。");
       Console.ReadLine();
      }
     }
    }

    首先,我們?cè)谥虚g人為地制造了一個(gè)錯(cuò)誤——使用前面講過(guò)的Create database語(yǔ)句。然后,在異常處理的catch塊中有如下語(yǔ)句:

    myTran.Rollback();

    當(dāng)異常發(fā)生時(shí),程序執(zhí)行流跳轉(zhuǎn)到catch塊中,首先執(zhí)行的就是這條語(yǔ)句,它將當(dāng)前事務(wù)回滾。在這段程序可以看出,在Create database之前,已經(jīng)有了一個(gè)更新數(shù)據(jù)庫(kù)的操作——將pubs數(shù)據(jù)庫(kù)的roysched表中的所有title_id字段以“PC”開頭的書籍的royalty字段的值都增加0.1倍。但是,由于異常發(fā)生而導(dǎo)致的回滾使得對(duì)于數(shù)據(jù)庫(kù)來(lái)說(shuō)什么都沒(méi)有發(fā)生。由此可見(jiàn),Rollback()方法維護(hù)了數(shù)據(jù)庫(kù)的一致性及事務(wù)的原子性。

     

      使用存儲(chǔ)點(diǎn)

    事務(wù)只是一種最壞情況下的保障措施,事實(shí)上,平時(shí)系統(tǒng)的運(yùn)行可靠性都是相當(dāng)高的,錯(cuò)誤很少發(fā)生,因此,在每次事務(wù)執(zhí)行之前都檢查其有效性顯得代價(jià)太高——絕大多數(shù)的情況下這種耗時(shí)的檢查是不必要的。我們不得不想另外一種辦法來(lái)提高效率。

    事務(wù)存儲(chǔ)點(diǎn)提供了一種機(jī)制,用于回滾部分事務(wù)。因此,我們可以不必在更新之前檢查更新的有效性,而是預(yù)設(shè)一個(gè)存儲(chǔ)點(diǎn),在更新之后,如果沒(méi)有出現(xiàn)錯(cuò)誤,就繼續(xù)執(zhí)行,否則回滾到更新之前的存儲(chǔ)點(diǎn)。存儲(chǔ)點(diǎn)的作用就在于此。要注意的是,更新和回滾代價(jià)很大,只有在遇到錯(cuò)誤的可能性很小,而且預(yù)先檢查更新的有效性的代價(jià)相對(duì)很高的情況下,使用存儲(chǔ)點(diǎn)才會(huì)非常有效。

    使用.net框架編程時(shí),你可以非常簡(jiǎn)單地定義事務(wù)存儲(chǔ)點(diǎn)和回滾到特定的存儲(chǔ)點(diǎn)。下面的語(yǔ)句定義了一個(gè)存儲(chǔ)點(diǎn)“NoUpdate”:

    myTran.Save("NoUpdate");

    當(dāng)你在程序中創(chuàng)建同名的存儲(chǔ)點(diǎn)時(shí),新創(chuàng)建的存儲(chǔ)點(diǎn)將替代原有的存儲(chǔ)點(diǎn)。

    在回滾事務(wù)時(shí),只需使用Rollback()方法的一個(gè)重載函數(shù)即可:

      myTran.Rollback("NoUpdate");

    下面這段程序說(shuō)明了回滾到存儲(chǔ)點(diǎn)的方法和時(shí)機(jī):

     

    using System;
    using System.Data;
    using System.Data.SqlClient;

    namespace Aspcn
    {
     public class DbTran
     { 
      file://執(zhí)行事務(wù)處理
      public void DoTran()
      {
       file://建立連接并打開
       SqlConnection myConn=GetConn();
       myConn.Open();

       SqlCommand myComm=new SqlCommand();
       SqlTransaction myTran;

       file://創(chuàng)建一個(gè)事務(wù)
       myTran=myConn.BeginTransaction();
       file://從此開始,基于該連接的數(shù)據(jù)操作都被認(rèn)為是事務(wù)的一部分
       file://下面綁定連接和事務(wù)對(duì)象
       myComm.Connection=myConn;
       myComm.Transaction=myTran;

       try
       {
        myComm.CommandText="use pubs";
        myComm.ExecuteNonQuery();

        myTran.Save("NoUpdate");

        myComm.CommandText="UPDATE roysched SET royalty = royalty * 1.10 WHERE title_id LIKE 'Pc%'";
        myComm.ExecuteNonQuery();

        file://提交事務(wù)
        myTran.Commit();
       }
       catch(Exception err)
       {
        file://更新錯(cuò)誤,回滾到指定存儲(chǔ)點(diǎn)
        myTran.Rollback("NoUpdate");
        throw new ApplicationException("事務(wù)操作出錯(cuò),系統(tǒng)信息:"+err.Message);
       }
      }

      file://獲取數(shù)據(jù)連接
      private SqlConnection GetConn()
      {
       string strSql="Data Source=localhost;Integrated Security=SSPI;user id=sa;password=";
       SqlConnection myConn=new SqlConnection(strSql);
       return myConn;
      }
     }
     public class Test
     {
      public static void Main()
      {
       DbTran tranTest=new DbTran();
       tranTest.DoTran();
       Console.WriteLine("事務(wù)處理已經(jīng)成功完成。");
       Console.ReadLine();
      }
     }
    }

    很明顯,在這個(gè)程序中,更新無(wú)效的幾率是非常小的,而且在更新前驗(yàn)證其有效性的代價(jià)相當(dāng)高,因此我們無(wú)須在更新之前驗(yàn)證其有效性,而是結(jié)合事務(wù)的存儲(chǔ)點(diǎn)機(jī)制,提供了數(shù)據(jù)完整性的保證。

     

      隔離級(jí)別的概念

    企業(yè)級(jí)的數(shù)據(jù)庫(kù)每一秒鐘都可能應(yīng)付成千上萬(wàn)的并發(fā)訪問(wèn),因而帶來(lái)了并發(fā)控制的問(wèn)題。由數(shù)據(jù)庫(kù)理論可知,由于并發(fā)訪問(wèn),在不可預(yù)料的時(shí)刻可能引發(fā)如下幾個(gè)可以預(yù)料的問(wèn)題:

      臟讀:包含未提交數(shù)據(jù)的讀取。例如,事務(wù)1 更改了某行。事務(wù)2 在事務(wù)1 提交更改之前讀取已更改的行。如果事務(wù)1 回滾更改,則事務(wù)2 便讀取了邏輯上從未存在過(guò)的行。

      不可重復(fù)讀取:當(dāng)某個(gè)事務(wù)不止一次讀取同一行,并且一個(gè)單獨(dú)的事務(wù)在兩次(或多次)讀取之間修改該行時(shí),因?yàn)樵谕粋€(gè)事務(wù)內(nèi)的多次讀取之間修改了該行,所以每次讀取都生成不同值,從而引發(fā)不一致問(wèn)題。

      幻象:通過(guò)一個(gè)任務(wù),在以前由另一個(gè)尚未提交其事務(wù)的任務(wù)讀取的行的范圍中插入新行或刪除現(xiàn)有行。帶有未提交事務(wù)的任務(wù)由于該范圍中行數(shù)的更改而無(wú)法重復(fù)其原始讀取。

    如你所想,這些情況發(fā)生的根本原因都是因?yàn)樵诓l(fā)訪問(wèn)的時(shí)候,沒(méi)有一個(gè)機(jī)制避免交叉存取所造成的。而隔離級(jí)別的設(shè)置,正是為了避免這些情況的發(fā)生。事務(wù)準(zhǔn)備接受不一致數(shù)據(jù)的級(jí)別稱為隔離級(jí)別。隔離級(jí)別是一個(gè)事務(wù)必須與其它事務(wù)進(jìn)行隔離的程度。較低的隔離級(jí)別可以增加并發(fā),但代價(jià)是降低數(shù)據(jù)的正確性。相反,較高的隔離級(jí)別可以確保數(shù)據(jù)的正確性,但可能對(duì)并發(fā)產(chǎn)生負(fù)面影響。

    根據(jù)隔離級(jí)別的不同,DBMS為并行訪問(wèn)提供不同的互斥保證。在SQL Server數(shù)據(jù)庫(kù)中,提供四種隔離級(jí)別:未提交讀、提交讀、可重復(fù)讀、可串行讀。這四種隔離級(jí)別可以不同程度地保證并發(fā)的數(shù)據(jù)完整性:

     

    隔離級(jí)別 臟 讀 不可重復(fù)讀取 幻 像
    未提交讀
    提交讀
    可重復(fù)讀
    可串行讀

    可以看出,“可串行讀”提供了最高級(jí)別的隔離,這時(shí)并發(fā)事務(wù)的執(zhí)行結(jié)果將與串行執(zhí)行的完全一致。如前所述,最高級(jí)別的隔離也就意味著最低程度的并發(fā),因此,在此隔離級(jí)別下,數(shù)據(jù)庫(kù)的服務(wù)效率事實(shí)上是比較低的。盡管可串行性對(duì)于事務(wù)確保數(shù)據(jù)庫(kù)中的數(shù)據(jù)在所有時(shí)間內(nèi)的正確性相當(dāng)重要,然而許多事務(wù)并不總是要求完全的隔離。例如,多個(gè)作者工作于同一本書的不同章節(jié)。新章節(jié)可以在任意時(shí)候提交到項(xiàng)目中。但是,對(duì)于已經(jīng)編輯過(guò)的章節(jié),沒(méi)有編輯人員的批準(zhǔn),作者不能對(duì)此章節(jié)進(jìn)行任何更改。這樣,盡管有未編輯的新章節(jié),但編輯人員仍可以確保在任意時(shí)間該書籍項(xiàng)目的正確性。編輯人員可以查看以前編輯的章節(jié)以及最近提交的章節(jié)。這樣,其它的幾種隔離級(jí)別也有其存在的意義。

    在.net框架中,事務(wù)的隔離級(jí)別是由枚舉System.Data.IsolationLevel所定義的:

    [Flags]
    [Serializable]
    public enum IsolationLevel

    其成員及相應(yīng)的含義如下:

    成 員 含 義
    Chaos 無(wú)法改寫隔離級(jí)別更高的事務(wù)中的掛起的更改。
    ReadCommitted 在正在讀取數(shù)據(jù)時(shí)保持共享鎖,以避免臟讀,但是在事務(wù)結(jié)束之前可以更改數(shù)據(jù),從而導(dǎo)致不可重復(fù)的讀取或幻像數(shù)據(jù)。
    ReadUncommitted 可以進(jìn)行臟讀,意思是說(shuō),不發(fā)布共享鎖,也不接受獨(dú)占鎖。
    RepeatableRead 在查詢中使用的所有數(shù)據(jù)上放置鎖,以防止其他用戶更新這些數(shù)據(jù)。防止不可重復(fù)的讀取,但是仍可以有幻像行。
    Serializable 在DataSet上放置范圍鎖,以防止在事務(wù)完成之前由其他用戶更新行或向數(shù)據(jù)集中插入行。
    Unspecified 正在使用與指定隔離級(jí)別不同的隔離級(jí)別,但是無(wú)法確定該級(jí)別。

    顯而意見(jiàn),數(shù)據(jù)庫(kù)的四個(gè)隔離級(jí)別在這里都有映射。

    默認(rèn)的情況下,SQL Server使用ReadCommitted(提交讀)隔離級(jí)別。

    關(guān)于隔離級(jí)別的最后一點(diǎn)就是如果你在事務(wù)執(zhí)行的過(guò)程中改變了隔離級(jí)別,那么后面的命名都在最新的隔離級(jí)別下執(zhí)行——隔離級(jí)別的改變是立即生效的。有了這一點(diǎn),你可以在你的事務(wù)中更靈活地使用隔離級(jí)別從而達(dá)到更高的效率和并發(fā)安全性。

      忠告

    無(wú)疑,引入事務(wù)處理是應(yīng)對(duì)可能出現(xiàn)的數(shù)據(jù)錯(cuò)誤的好方法,但是也應(yīng)該看到事務(wù)處理需要付出的巨大代價(jià)——用于存儲(chǔ)點(diǎn)、回滾和并發(fā)控制所需要的CPU時(shí)間和存儲(chǔ)空間。

    摘自:http://hi.baidu.com/bolove/blog/item/b2397f1e477b3ef41ad5763f.html


    只有注冊(cè)用戶登錄后才能發(fā)表評(píng)論。


    網(wǎng)站導(dǎo)航:
     
    主站蜘蛛池模板: 亚洲第一综合天堂另类专| 成年免费大片黄在线观看岛国| 亚洲 欧洲 日韩 综合在线| 亚洲熟妇无码八AV在线播放| 女人张腿给男人桶视频免费版| 999久久久免费精品播放| 国产精品一区二区三区免费| MM1313亚洲精品无码久久| 亚洲大香伊人蕉在人依线| 亚洲AV永久纯肉无码精品动漫| 亚洲人成无码久久电影网站| 日韩免费无砖专区2020狼| 日本精品人妻无码免费大全| 亚洲高清免费在线观看| 无码国产精品一区二区免费vr| 一级毛片a女人刺激视频免费| 精品亚洲成a人在线观看| 亚洲色无码专区一区| va天堂va亚洲va影视中文字幕| 亚洲一区二区在线免费观看| 国产亚洲av片在线观看16女人| 国产亚洲色婷婷久久99精品91| 亚洲精品456播放| 亚洲阿v天堂在线2017免费| 一区国严二区亚洲三区| 免费国产真实迷j在线观看| 曰皮全部过程视频免费国产30分钟| 成人毛片免费在线观看| 西西大胆无码视频免费| 亚洲免费中文字幕| av无码免费一区二区三区| 日本h在线精品免费观看| 在线看免费观看AV深夜影院| 国产成人午夜精品免费视频| 毛片a级毛片免费观看免下载| 国产成人免费爽爽爽视频| 韩国日本好看电影免费看| 日本不卡视频免费| 亚洲成A∨人片天堂网无码| 亚洲综合国产精品第一页| 亚洲精品乱码久久久久久蜜桃不卡 |