SQL Server 事務與鎖

序言

爲何須要事務?sql

  當多個線程都開啓事務操做數據庫中的數據時,數據庫系統要能進行隔離操做,以保證各個線程獲取數據的準確性, 因此, 對於不一樣的事務,採用不一樣的隔離級別會有不一樣的結果。數據庫

若是沒有事務隔離,會出現什麼樣的狀況?併發

http://blog.itpub.net/26736162/viewspace-2638951/分佈式

事務

  事務就是做爲一個邏輯工做單元的SQL語句,若是任何一個語句操做失敗那麼整個操做就被失敗,之後操做就會回滾到操做前狀態,或者是上個節點。爲了確保要麼執行,要麼不執行,就可使用事務。ide

 

 

Sql中使用事務

測試數據測試

--建立一個帳戶表,添加約束,餘額(money)不小於零
    create table Tb_bankAcount( Id int identity(1,1) primary key, Name nvarchar(20) not null, Money int not null ) alter table Tb_bankAcount add constraint CK_money CHECK(money>=0) --添加數據
    insert into Tb_bankAcount values('A',200) insert into Tb_bankAcount values('B',200)
View Code

測試轉帳事務spa

begin transaction --開啓事務
    declare @errorCount int=0;--記錄錯誤的變量
        update Tb_bankAcount set Money-=500 where Name='A'
        set @errorCount+=@@ERROR
        update Tb_bankAcount set Money+=500 where Name='B'
        set @errorCount+=@@ERROR

    if @errorCount>0      --有錯誤就回滾
        rollback transaction
    else                  --沒有錯誤提交
        commit transaction
View Code

Ado.Net中使用事務(SqlTransaction形式).net

using (SqlConnection conn = new SqlConnection(connStr)) { //要執行的sql腳本 string sqlText = @"update Tb_bankAcount set Money-=100 where Name='A'
        update Tb_bankAcount set Money+=100 where Name='B'"; conn.Open(); SqlTransaction tran = conn.BeginTransaction(); using (SqlCommand com = new SqlCommand(sqlText, conn)) { try { //開啓事務 com.Transaction = tran; com.ExecuteNonQuery(); //提交事務 tran.Commit(); Console.WriteLine("事務執行成功"); } catch (Exception ex) { //回滾事務 tran.Rollback(); Console.WriteLine(ex.Message); } } }
View Code

  上邊的代碼執行時,因爲知足約束條件(Money>0),執行事務提交。線程

  使用SqlTransaction執行事務時,每一個事務都是基於SqlConnection的,若是咱們的事務要跨越多個程序集或者使用多個數據庫時,使用SqlTransaction來實現事務就比較麻煩了,針對這個問題.net 2.0出現了TransactionScopecode

Ado.Net中使用分佈式事務(TransactionScope形式)

static void Main(string[] args) { //鏈接字符串 string connstr1 = @"your connctionString1"; string connstr2 = @"your connctionString2"; using (TransactionScope ts = new TransactionScope()) { #region 執行任務1 using (SqlConnection conn1 = new SqlConnection(connstr1)) { using (SqlCommand com = conn1.CreateCommand()) { conn1.Open(); com.CommandText = "delete from t_stu where id=10"; com.ExecuteNonQuery(); } } #endregion #region 執行任務2 using (SqlConnection conn2 = new SqlConnection(connstr2)) { using (SqlCommand com = conn2.CreateCommand()) { conn2.Open(); com.CommandText = "insert into t_stu(stuname,age) values ('zs',22')"; com.ExecuteNonQuery(); } } #endregion //經過ts.Complete()方法進行提交 ts.Complete(); } }
View Code

  上邊的代碼十分簡單,咱們能夠看到使用TransactionScope能夠輕鬆的構建分佈式的事務模型,conn1和conn2兩個鏈接能夠鏈接不一樣的數據庫。TransactionScope實現了IDispose()接口,咱們可使用using語法糖來自動釋放資源。執行TransactionScope時會依此執行TranactionScope的全部代碼,當執行到ts.Complete()時表示事務中的任務都執行完成了,進行提交。若是不顯示地執行ts.Complete()方法,TransactionScope中代碼執行完畢後執行回滾操做。

其餘測試代碼

提交事務

BEGIN TRAN Tran_Money; INSERT INTO [dbo].[Money]([Name],[Money])VALUES('沐風',100) COMMIT TRAN;
View Code

回滾事務

BEGIN TRAN Tran_Money; INSERT INTO [dbo].[Money]([Name],[Money])VALUES('沐風',100) ROLLBACK TRAN;
View Code

轉帳事務

BEGIN TRAN Tran_Money; --開始事務
  DECLARE @tran_error INT; SET @tran_error = 0; BEGIN TRY UPDATE  dbo.Money
    SET     Money = Money - 30
    WHERE   Name = '張三'; SET @tran_error = @tran_error + @@ERROR; --測試出錯代碼,看看張三的錢減小,李四的錢是否會增長
        --SET @tran_error = 1;
    UPDATE  dbo.Money
    SET     Money = Money + 30
    WHERE   Name = '李四'; SET @tran_error = @tran_error + @@ERROR; END TRY BEGIN CATCH PRINT '出現異常,錯誤編號:' + CONVERT(VARCHAR, ERROR_NUMBER()) + ',錯誤消息:'
        + ERROR_MESSAGE(); SET @tran_error = @tran_error + 1; END CATCH; IF ( @tran_error > 0 ) BEGIN
        --執行出錯,回滾事務
        ROLLBACK TRAN; PRINT '轉帳失敗,取消交易!'; END; ELSE
    BEGIN
        --沒有異常,提交事務
        COMMIT TRAN; PRINT '轉帳成功!'; END
轉帳事務

鎖是實現事務的關鍵,鎖能夠保證事務的完整性和併發性。數據庫中的鎖也是爲了解決在併發訪問時出現各類衝突的一種機制。

鎖的目的是什麼?

主要解決多個用戶同時對數據庫的併發操做時會帶來如下數據不一致的問題:

  • 丟失更新,同時修改一條數據
  • 讀髒,A修改了數據後,B讀取後A又取消了修改,B讀髒
  • 不可重複讀,A用戶讀取數據,隨後B用戶讀取該數據並修改,此時A用戶再讀取數據時發現先後兩次的值不一致
  • 還有一種是幻讀,這個狀況好像很少。

併發控制的主要方法是封鎖,鎖就是在一段時間內禁止用戶作某些操做以免產生數據不一致

 鎖的粒度有哪些?

  • 數據庫鎖:鎖定整個數據庫,這一般發生在整個數據庫模式改變的時候。
  • 表鎖:鎖定整個表,這包含了與該表相關聯的全部數據相關的對象,包括實際的數據行(每一行)以及與該表相關聯的全部索引中的鍵。
  • 區段鎖:鎖定整個區段,由於一個區段由8頁組成,因此區段鎖定是指鎖定控制了區段、控制了該區段內8個數據或索引頁以及這8頁中的全部數據行。
  • 頁鎖:鎖定該頁中的全部數據或索引鍵。
  • 行或行標識符:雖然從技術上將,鎖是放在行標識符上的,可是本質上,它鎖定了整個數據行。

行級鎖

select * from tablename with (rowlock) where id=3

 

select * from tb WITH(XLOCK) where id = 5

 

public partial class SqlWith { public const string NoLock = "WITH(NOLOCK) "; public const string HoldLock = "WITH(HOLDLOCK)"; public const string PagLock = "WITH(PAGLOCK)"; public const string ReadCommitted = "WITH(READCOMMITTED)"; public const string TabLockX = "WITH(TABLOCKX)"; public const string UpdLock = "WITH(UPDLOCK)"; public const string RowLock = "WITH(ROWLOCK)"; public const string Null = "Non"; }

 

 

 

資料

事務的4種隔離級別(Isolation Level)分別是什麼?

相關文章
相關標籤/搜索