爲何須要事務?sql
當多個線程都開啓事務操做數據庫中的數據時,數據庫系統要能進行隔離操做,以保證各個線程獲取數據的準確性, 因此, 對於不一樣的事務,採用不一樣的隔離級別會有不一樣的結果。數據庫
若是沒有事務隔離,會出現什麼樣的狀況?併發
http://blog.itpub.net/26736162/viewspace-2638951/分佈式
事務就是做爲一個邏輯工做單元的SQL語句,若是任何一個語句操做失敗那麼整個操做就被失敗,之後操做就會回滾到操做前狀態,或者是上個節點。爲了確保要麼執行,要麼不執行,就可使用事務。ide
測試數據測試
--建立一個帳戶表,添加約束,餘額(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)
測試轉帳事務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
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); } } }
上邊的代碼執行時,因爲知足約束條件(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(); } }
上邊的代碼十分簡單,咱們能夠看到使用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;
回滾事務
BEGIN TRAN Tran_Money; INSERT INTO [dbo].[Money]([Name],[Money])VALUES('沐風',100) ROLLBACK TRAN;
轉帳事務
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
鎖是實現事務的關鍵,鎖能夠保證事務的完整性和併發性。數據庫中的鎖也是爲了解決在併發訪問時出現各類衝突的一種機制。
主要解決多個用戶同時對數據庫的併發操做時會帶來如下數據不一致的問題:
併發控制的主要方法是封鎖,鎖就是在一段時間內禁止用戶作某些操做以免產生數據不一致
行級鎖
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"; }