簡介數據庫
事務是單個的工做單元,這就意味着單元內有多個操做,事務是多個操做的整合體。若是某個事務執行成功,則涵蓋在這個事務裏的全部數據操做均會一併執行提交,成爲數據庫中的永久組成部分。併發
若是事務因某項操做執行錯誤,那麼事務內全部的操做都將無效,事務實行回滾機制,數據操做都會還原到初始爲更改的狀態。性能
爲何須要事務測試
在銀行業務中,有一條記帳原則,即有借有貸,借貸相等。爲了保證這種原則,每發生一筆銀行業務,就必須確保帳目上借方和貸方至少個記一筆帳,而且這兩筆帳要麼同時成功,要麼同時失敗。 spa
若是帳目上的記錄只出現了借方,或者只出現了貸方。那麼這就違反了記帳的原則,出現記帳錯誤的狀況。此業務場景就是事務典型的體現,經過事務處理的處理機制,才能夠解決此類業務場景。日誌
示例分析code
業務場景:對象
銀行轉帳每每涉及兩個或兩個以上的帳戶,包括轉出對象和轉入對象兩種。在轉出的存款金額減小必定金額的同時,轉入帳戶會增長相應金額的存款。接下來以轉帳的場景爲例,用代碼演示轉帳的過程。blog
數據準備:進程
--建立表
if exists(select * from sysobjects where name='bank' ) drop table bank go
create table bank ( customerName nvarchar(4), currentMoney money ) go
--添加約束:帳戶餘額(currentMoney)不能少於1元,不然視爲銷戶
alter table bank add constraint ck_currentMoney check (currentMoney>=1) go
--插入測試數據,即轉帳的兩個對象:張三帳戶餘額爲1000元,李四帳戶餘額爲1元
insert into bank values ('張三',1000),('李四',1); go
--查看結果
select * from bank go
使用SQL語句模擬轉帳功能:
邏輯:從張三的帳戶直接轉帳1000元到李四的帳戶,張三帳戶減小1000元。李四帳戶增長1000元。
代碼以下:
--根據業務邏輯編寫SQL語句執行數據操做
--轉出
update bank set currentMoney=currentMoney-1000
where customerName='張三'
--轉入
update bank set currentMoney=currentMoney+1000
where customerName='李四'
執行結果:
結果分析:
張三轉給李四的1000元並無從帳戶里扣除,李四的帳戶卻多了1000元,轉帳後兩個帳戶的餘額總和變爲1000+1001=2001,存入銀行的錢憑空多出1000元。若是此操做發生在生活中,想想是多麼可怕的事情。此操做的緣由是由於以前在建立表的時候定義的約束,致使第一個SQL命令沒法執行,下一個SQL沒有由於中斷中止執行。
若是在程序中,可能會用代碼邏輯去控制,即等第一個命令執行成功在執行下一個命令,這樣雖然能夠監控第一個命令的結果作出對應的處理。可是若是第一個命令執行成功,第二個命令執行出錯該怎麼辦,這樣仍是沒法確保一個業務的總體性。
咱們可使用事務機制來解決此問題,轉帳過程就是一個事務,它須要兩條或者更多條SQL語句來完成一系列的操做,無論有多少條命令,它們始終圍繞着一個主題就是轉帳。若是其中某個步驟出錯,則整個轉帳業務也不能成立,應出錯要把事務中操做的數據恢復爲原來的數據。在開發中若是不使用事務處理機制,咱們是很難保證事務中單個操做不出錯的。一旦事務中某個操做出錯就致使了總體的邏輯性。
事務的概念
事務是一種機制,一個操做序列,它包含了多個操做命令,而且把全部的命令做爲一個總體並都圍繞着一種業務主題一塊兒向系統提交或撤銷操做請求,即這一組命令要麼都執行,要麼都不執行,通俗的理解就是共同進退。所以事務是一個不可分割的工做邏輯單元,在數據庫系統上執行併發操做時,事務是做爲最小的控制單元來使用的,它特別適用於多用戶同時操做的數據庫系統。例如,航空公司的訂票系統、銀行、保險公司等。
事務是做爲單個邏輯工做單元執行的一系列操做。一個邏輯工做單元必須有四個屬性,即原子性、一致性、隔離性、持久性,這些特性簡稱爲ACID。
1.原子性(一個事務只圍繞一個業務主題,事務中每一個操做只針對一個表進行)
事務是一個完整的操做。事務的各個元素是不可分的(原子的)。事務中的全部元素必須做爲一個總體提交或回滾。若是事務中任何元素執行失敗,則整個事務就視爲失敗。
以銀行轉帳事務爲例,若是該事務提交了,則轉出和轉入兩個帳戶的數據都將會更新數據。若是因爲某種緣由,事務中某一項操做失敗,則事務不會提交,兩個帳戶的數據都不會更新,而且會撤銷對任何帳戶餘額的修改,這主要說明的是事務中的操做不能部分執行。
2.一致性(數據存儲合乎業務邏輯)
當事務完成時,數據必須處於一致狀態。也就是是說,在事務開始以前,數據庫中存儲的數據處於一致狀態。在正在進行的事務中,數據可能處於不一致的狀態,如數據可能有部分修改。然而,當事務成功完成時,數據必須再次回到已知的一致狀態。經過事務對數據所作的修改不能損壞數據的邏輯性,或者說事務不能使數據處於混亂不合法性。如,一個訂單系統下了一筆訂單後,數據庫中存在下單後的訂單數據,訂單中的商品庫存卻沒有因採購訂單的商品的購買而減小。
以銀行轉帳事務爲例。在事務開始以前,全部帳戶的餘額處於一致狀態。在事務進行的過程當中,一個帳戶餘額減小,而另外一個帳戶餘額還沒有修改。所以,全部帳戶餘額處於不一致狀態(數據不合乎業務邏輯)。
3.隔離性(事務自己就能夠很好處理併發操做)
對數據進行修改的全部併發事務是彼此隔離的,這代表事務必須是獨立的,它不該以任何方式依賴於或影響其餘事務。修改數據的事務能夠在另外一個使用相同數據的事務開始以前訪問這些數據,或者在另外一個使用相同數據的事務的結束以後訪問這些數據。另外,當事務修改數據時,若是任何其餘進程正在同時使用相同的數據,則會等到事務提交以後,對數據的修改才能生效。能夠通俗的理解,在對數據庫數據修改的通道上,只容許單個事務經過執行,待當前通道事務執行成功纔會執行下一個事務。如轉帳事務,張三和李四之間的轉帳與王五和趙二之間的轉帳,永遠是相互獨立的。
4.持久性(事務能夠預防系統的突發情況)
事務的持久性指無論系統是否發生了故障,事務處理的結果都是永久的。
一個事務完成以後,它對於數據庫的改變是永久性的,即便系統出現故障也是如此。就是說,一旦事務被提交,事務的效果會被永久地保留在數據庫中。(這有點像紋身)
若是系統SQL Server數據庫或者某些組件發生了故障,則數據庫會在系統從新啓動的時候自動恢復。SQL Server使用事務日誌保存受到故障影響的事務並從新運行未提交的事務。
SQL Server事務中的關鍵代碼
1.開始事務
BEGIN TRANSACTION
這個語句顯示地標記一個事務的起始點。
2.提交事務
COMMIT TRANSACTION
這個語句標誌一個事務成功結束。自事務開始至提交語句之間(BEGIN TRANSACTION ~ COMMIT TRANSACTION)執行的全部數據操做將將永久地保存在數據庫數據文件中,並釋放鏈接時佔用的資源。
3.回滾(撤銷)事務
ROLLBACK TRANSACTION
清除自事務起始點至該語句之間,所作的全部數據操做,將數據狀態回滾到事務開始以前,並釋放由事務控制的資源。
SQL Server執行事務的邏輯:
BENGIN TRANSACTION語句後面的SQL語句對數據庫的操做都將記錄在事務日誌中,只到遇到ROLLBACK TRANSACTION語句或COMMIT TRANSACTION語句。若是事務中某一項操做失敗且執行了ROLLBACK TRANSACTION語句,
那麼在BEGIN TRANSACTION語句以後全部更新的數據都能回滾到事務以前的狀態。若是事務中的全部操做所有正確完成,而且使用了COMMIT TRANSACTION語句,那麼全部數據操做都將提交到數據庫。
事務分類
在SQL Server中事務有如下3種類型:
1.顯示事務:
使用SQL Server事務中的關鍵代碼明確指定事務的階段(BEGIN TRANSACTION、COMMIT TRANSACTION、ROLLBACK TRANSACTION)。
2.隱式事務:
經過設置SET IMPLICIT_TRANSACTION ON語句,將隱式事務模式打開。當以隱式事務操做時,SQL Server將在提交或回滾事務後自動啓動新事務。不須要描述每一個事務的開始,只要提交或回滾每一個事務便可。
3.自動提交事務:
這是SQL Server的默認模式,它將每條單獨的T-SQL語句視爲一個事務。若是成功執行,則自動提交。若是錯誤,則自動回滾。
在實際開發中最經常使用的就是顯示事務,當前文章介紹的也是顯示事務,它明確地指定事務的每一個階段。
SQL Server事務運用
接着<爲何須要事務>段落中的銀行轉帳示例繼續介紹怎麼在代碼中實現事務。
結合@@ERROR的運用:
在事務處理的過程當中,如何經過代碼判斷T-SQL語句執行是否有誤,從而順利實現提交事務或回滾事務操做呢?能夠在事務的每一個操做以後,使用@@ERROR全局變量,以檢查判斷當前T-SQL語句執行是否有誤,若是有錯誤則返回非零值。
實現代碼:
set nocount on
go
print '查看轉帳事務前的帳戶餘額'
select * from bank go
--開始事務(指定事務今後開始,後續的SQL語句是一個總體)
begin transaction
--定義變量,用於累計事務執行過程當中的錯誤
declare @errorSum int
set @errorSum=0 --初始化爲0,即默認表明無錯誤
--事務邏輯-轉帳:張三的帳戶減小1000元,李四的帳戶增長1000元
update bank set currentMoney=currentMoney-1000 where customerName='張三'
set @errorSum=@@ERROR+@errorSum --累計是否有誤
update bank set currentMoney=currentMoney+1000 where customerName='李四'
set @errorSum=@@ERROR+@errorSum --累計是否有誤
print '查看轉帳事務過程當中的餘額'
select * from bank --根據錯誤變量的值,判斷語句在執行過程是否出錯,以肯定事務進行提交或回滾
if @errorSum<>0 --非零
begin
print '交易失敗,回滾事務'
rollback transaction
end
else
begin
print '交易成功,提交事務,數據錄入至數據庫'
commit transaction
end
go
print '查看轉帳事務後的餘額'
select * from bank go
語句執行結果:
示例中事務在執行過程當中因某條操做中斷,張三是轉帳轉出對象,轉出1000元后張三帳戶爲0,違反了約束而出錯。事務此時保證了轉帳邏輯的合法性。下面將張三轉帳金額改成800,運行結果以下:
事務了執行提交,轉帳事務完整成功執行。
編寫事務時要遵照的原則
事務儘量簡短:
事務啓動至結束後再數據庫管理系統中保留大量資源,以保證事務的原子性、一致性,隔離性和持久性。若是在多用戶系統中,較大的事務將會佔用系統的大量資源,是系統不堪重負,會影響軟件的性能,甚至致使系統崩潰。
事務中訪問的數據量儘可能最少:
當併發執行事務處理時,事務操做的數據量越少,事務之間對操做數據的爭奪就越少。
查詢數據時儘可能不要使用事務:
對數據進行瀏覽查詢操做並不會更新數據庫的數據,所以儘可能不使用事務查詢數據,避免佔用過量的系統資源。
在事務處理過程當中儘可能不要出現等待用戶輸入的操做:
在處理事務的過程當中,若是須要等待用戶輸入數據,那麼事務會長時間地佔用資源,有可能形成系統阻塞。