C#資料庫事務原理及實踐(下)

hljhrbsjf發表於2006-09-05

 另一個走向極端的錯誤

??滿懷信心的新手們可能為自己所掌握的部分知識陶醉不已,剛接觸資料庫庫事務處理的準開發者們也一樣,躊躇滿志地準備將事務機制應用到他的資料處理程式的每一個模組每一條語句中去。的確,事務機制看起來是如此的誘人——簡潔、美妙而又實用,我當然想用它來避免一切可能出現的錯誤——我甚至想用事務把我的資料操作從頭到尾包裹起來。

??看著吧,下面我要從建立一個資料庫開始:

using System;
using System.Data;
using System.DataqlClient;

namespace Aspcn
{
 public class DbTran
 {
  file://執行事務處理
  public void DoTran()
  {
   file://建立連線並開啟
   SqlConnection myConn=GetConn();
   myConn.Open();

   SqlCommand myComm=new SqlCommand();
   SqlTransaction myTran;

   myTran=myConn.BeginTransaction();

   file://下面繫結連線和事務物件
   myComm.Connection=myConn;
   myComm.Transaction=myTran;

   file://試圖建立資料庫TestDB
   myCommmandText="CREATE database TestDB";
   myComm.ExecuteNonQuery();

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

  file://獲取資料連線
  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("事務處理已經成功完成。");
   Console.ReadLine();
  }
 }
}

??//---------------

  未處理的異常: System.Data.SqlClient.SqlException: 在多語句事務內不允許使用 CREATE DATABASE 語句。

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

??注意,如下的SQL語句不允許出現在事務中:

DATABASE修改資料庫
LOG備份日誌
CREATE DATABASE建立資料庫
DISK INIT建立資料庫或事務日誌裝置
DROP DATABASE刪除資料庫
DUMP TRANSACTION轉儲事務日誌
LOAD DATABASE裝載資料庫備份複本
LOAD TRANSACTION裝載事務日誌備份複本
RECONFIGURE 更新使用 sp_configure 系統儲存過程更改的配置選項的當前配置(sp_configure 結果集中的 config_value 列)值。
RESTORE DATABASE還原使用BACKUP命令所作的資料庫備份
RESTORE LOG還原使用BACKUP命令所作的日誌備份
UPDATE STATISTICS在指定的表或索引檢視中,對一個或多個統計組(集合)有關鍵值分發的資訊進行更新

??除了這些語句以外,你可以在你的資料庫事務中使用任何合法的SQL語句。

 另一個走向極端的錯誤

??滿懷信心的新手們可能為自己所掌握的部分知識陶醉不已,剛接觸資料庫庫事務處理的準開發者們也一樣,躊躇滿志地準備將事務機制應用到他的資料處理程式的每一個模組每一條語句中去。的確,事務機制看起來是如此的誘人——簡潔、美妙而又實用,我當然想用它來避免一切可能出現的錯誤——我甚至想用事務把我的資料操作從頭到尾包裹起來。

??看著吧,下面我要從建立一個資料庫開始:

using System;
using System.Data;
using System.DataqlClient;

namespace Aspcn
{
 public class DbTran
 {
  file://執行事務處理
  public void DoTran()
  {
   file://建立連線並開啟
   SqlConnection myConn=GetConn();
   myConn.Open();

   SqlCommand myComm=new SqlCommand();
   SqlTransaction myTran;

   myTran=myConn.BeginTransaction();

   file://下面繫結連線和事務物件
   myComm.Connection=myConn;
   myComm.Transaction=myTran;

   file://試圖建立資料庫TestDB
   myCommmandText="CREATE database TestDB";
   myComm.ExecuteNonQuery();

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

  file://獲取資料連線
  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("事務處理已經成功完成。");
   Console.ReadLine();
  }
 }
}

??//---------------

  未處理的異常: System.Data.SqlClient.SqlException: 在多語句事務內不允許使用 CREATE DATABASE 語句。

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

??注意,如下的SQL語句不允許出現在事務中:

DATABASE修改資料庫
LOG備份日誌
CREATE DATABASE建立資料庫
DISK INIT建立資料庫或事務日誌裝置
DROP DATABASE刪除資料庫
DUMP TRANSACTION轉儲事務日誌
LOAD DATABASE裝載資料庫備份複本
LOAD TRANSACTION裝載事務日誌備份複本
RECONFIGURE 更新使用 sp_configure 系統儲存過程更改的配置選項的當前配置(sp_configure 結果集中的 config_value 列)值。
RESTORE DATABASE還原使用BACKUP命令所作的資料庫備份
RESTORE LOG還原使用BACKUP命令所作的日誌備份
UPDATE STATISTICS在指定的表或索引檢視中,對一個或多個統計組(集合)有關鍵值分發的資訊進行更新

??除了這些語句以外,你可以在你的資料庫事務中使用任何合法的SQL語句。

 隔離級別的概念

??企業級的資料庫每一秒鐘都可能應付成千上萬的併發訪問,因而帶來了併發控制的問題。由資料庫理論可知,由於併發訪問,在不可預料的時刻可能引發如下幾個可以預料的問題:

  髒讀:包含未提交資料的讀取。例如,事務1 更改了某行。事務2 在事務1 提交更改之前讀取已更改的行。如果事務1 回滾更改,則事務2 便讀取了邏輯上從未存在過的行。

  不可重複讀取:當某個事務不止一次讀取同一行,並且一個單獨的事務在兩次(或多次)讀取之間修改該行時,因為在同一個事務內的多次讀取之間修改了該行,所以每次讀取都生成不同值,從而引發不一致問題。

  幻象:透過一個任務,在以前由另一個尚未提交其事務的任務讀取的行的範圍中插入新行或刪除現有行。帶有未提交事務的任務由於該範圍中行數的更改而無法重複其原始讀取。

??如你所想,這些情況發生的根本原因都是因為在併發訪問的時候,沒有一個機制避免交叉存取所造成的。而隔離級別的設定,正是為了避免這些情況的發生。事務準備接受不一致資料的級別稱為隔離級別。隔離級別是一個事務必須與其它事務進行隔離的程度。較低的隔離級別可以增加併發,但代價是降低資料的正確性。相反,較高的隔離級別可以確保資料的正確性,但可能對併發產生負面影響。

??根據隔離級別的不同,DBMS為並行訪問提供不同的互斥保證。在SQL Server資料庫中,提供四種隔離級別:未提交讀、提交讀、可重複讀、可序列讀。這四種隔離級別可以不同程度地保證併發的資料完整性:

隔離級別髒 讀不可重複讀取幻 像
未提交讀
提交讀
可重複讀
可序列讀

??可以看出,“可序列讀”提供了最高階別的隔離,這時併發事務的執行結果將與序列執行的完全一致。如前所述,最高階別的隔離也就意味著最低程度的併發,因此,在此隔離級別下,資料庫的服務效率事實上是比較低的。儘管可序列性對於事務確保資料庫中的資料在所有時間內的正確性相當重要,然而許多事務並不總是要求完全的隔離。例如,多個作者工作於同一本書的不同章節。新章節可以在任意時候提交到專案中。但是,對於已經編輯過的章節,沒有編輯人員的批准,作者不能對此章節進行任何更改。這樣,儘管有未編輯的新章節,但編輯人員仍可以確保在任意時間該書籍專案的正確性。編輯人員可以檢視以前編輯的章節以及最近提交的章節。這樣,其它的幾種隔離級別也有其存在的意義。

??在.net框架中,事務的隔離級別是由列舉System.Data.IsolationLevel所定義的:??

[Flags]
[Serializable]
public enum IsolationLevel

??其成員及相應的含義如下:

成 員含 義
Chaos無法改寫隔離級別更高的事務中的掛起的更改。
ReadCommitted在正在讀取資料時保持共享鎖,以避免髒讀,但是在事務結束之前可以更改資料,從而導致不可重複的讀取或幻像資料。
ReadUncommitted可以進行髒讀,意思是說,不釋出共享鎖,也不接受獨佔鎖。
RepeatableRead在查詢中使用的所有資料上放置鎖,以防止其他使用者更新這些資料。防止不可重複的讀取,但是仍可以有幻像行。
Serializable在DataSet上放置範圍鎖,以防止在事務完成之前由其他使用者更新行或向資料集中插入行。
Unspecified 正在使用與指定隔離級別不同的隔離級別,但是無法確定該級別。

??顯而意見,資料庫的四個隔離級別在這裡都有對映。

??預設的情況下,SQL Server使用ReadCommitted(提交讀)隔離級別。

??關於隔離級別的最後一點就是如果你在事務執行的過程中改變了隔離級別,那麼後面的命名都在最新的隔離級別下執行——隔離級別的改變是立即生效的。有了這一點,你可以在你的事務中更靈活地使用隔離級別從而達到更高的效率和併發安全性。

  最後的忠告

??無疑,引入事務處理是應對可能出現的資料錯誤的好方法,但是也應該看到事務處理需要付出的巨大代價——用於儲存點、回滾和併發控制所需要的CPU時間和儲存空間。

??本文的內容只是針對Microsoft SQL Server資料庫的,對應於.net框架中的System.Data.SqlClient名稱空間,對於使用OleDb的情形,具體的實現稍有不同,但這不是本文的內容,有興趣的讀者可以到.net中華網()的論壇裡找到答案。

[@more@]

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/631872/viewspace-863313/,如需轉載,請註明出處,否則將追究法律責任。

相關文章