9.1事務的詳細介紹

--學校財務讓出納張三和李四去銀行取錢,對同一個銀行賬務A內有200元,
--張三進行提款操作100元,李四進行轉賬操作100元到B賬戶。要是沒有進行隔離可能會出現一下併發問題:
--(1)第一類丟失更新:先是張三提款時賬戶內有200元,同時李四轉賬時也是200元,然後張三李四同時操作
--張三操作成功取走100元,李四操作失敗回滾賬戶內最終爲200元,這樣張三的操作被覆蓋了麼銀行損失100元
--(2)髒讀:張三提款100元未提交,李四進行轉賬查到賬戶內還有100元,這時候,張三放棄操作回滾,李四正常操作提交
--賬戶內最終爲0元,李四讀取了張三的髒數據,客戶損失了100元
--(3)虛讀:和髒讀類似,是針對插入操作過程中的讀取問題,如王二存款100元未提交,這時候,銀行做報表統計查詢賬戶爲200元
--然後王二提交了,這時候銀行再統計發現賬戶爲300元了,無法判斷哪個到底以哪個爲準?
--(4)不可重複讀:張三和李四同時開始,都查到賬戶爲200元,張三先開始提款100元提交,李四在準備最後更新的時候又進行了一次查詢
--發現結果時100元,這時李四就會很困惑,不知道將賬務改爲100還是0--
--(5)第二類丟失更新:不可重複讀的一個特例,例如:張三不做第二次查詢而是直接操作完成,賬戶內最終爲100元,李四的操作被覆蓋掉了
--銀行損失100元,感覺和第一類更新類似。
--食物是所有數據課管理系統中一個非常重要的概念,不管是數據庫管理人員還是數據庫開發人員,都應該對事務有較深刻的理解

--既然上述出現了問題,我們怎樣解決呢?如何防止丟失更新?如何防止髒讀和虛讀?如何實現可重複讀呢??
--這時候就引入了事務管理來解決以上問題。
--
--那什麼是事務呢?讓我們來了解一下事物的原理
--將一組操作綁定在一個事務中,爲了使事務成功,必須成功完成該事務中的所有操作
--例如銀行轉賬問題:把轉出和轉入作爲一個整體,形成一個操作集合,這個集合中的操作要麼不執行,要麼都執行。

--瞭解了事物的原理那再來了解一下事物的概念吧
--事務(transaction)是由對數據庫的若干操作組成的一個邏輯單元,這些操作要麼不執行,要麼都執行,是一個不可分割的整體。事務用這種方式保證數據滿足併發性
--和完整性的要求。使用事務可以避免發生有的語句被執行,而另一外一些語句沒有被執行,從而造成數據不一致問題。

--既然都瞭解到了事務的概念那爲啥不再多學點呢是不是。繼續瞭解事物的特性
--原子性(A),一致性(C),隔離性(I)和持久性(D)簡稱ACID原則。
--光知道這些還不夠,我們要做到知其然知其所以然
--原子性(Atomicity):事務必須是原子工作單元,實務中的操作要麼全部執行,要麼全不執行,不能只完成部分操作。
--原子性在數據庫系統中,由恢復機制來實現。
--一致性(Consistency):事務開始之前,數據庫處於一致性的狀態哦;事務結束後,數據庫必須仍處於一致性狀態。
--數據庫一致性的定義是由用戶負責的,如前面所述的銀行轉賬,用戶可以定義轉賬前後兩個賬戶金額之和應保持不變。
--隔離性(Isolation):系統必須保證事務不收其他併發執行事務的影響,即當多個事務同時進行是,各事務之間相互隔離,
--不可相互干擾,事務查看數據時所處的狀態,要麼是另一個併發事務修改它之前的狀態,要麼是另一個併發事務修改它之後的狀態,
--事務不會查看中間狀態的數據。隔離性通過系統的併發控制機制實現。
--持久性(Durability):一個已完成的事務對數據所作的任何變動在系統中是永久有效的,即使該事務產生的修改是不正確的,錯誤也將一直保持。
--持久性通過恢復機制實現,發生故障時,可以通過日誌等手段恢復數據信息。
--事務四原則保證了一個事物或者成功提交,或者失敗回滾,二者必居其一,因此,它對數據的修改具有可恢復性。即當事務失敗時,它對數據的修改都會恢復到該事務執行前的狀態。

--學習如逆水行舟不進則退,所以還得繼續學習
--我們一起來學習下事物的工作原理吧
--事務以begin transaction 開始,以commit transaction或rollback transaction結束
--其中commit transaction表示事務正常結束,提交給數據庫,而rollback tranasction表示事務非正常結束,撤銷事務已經做的操作,回滾到開始時的狀態。
--再給大家熟一點,其實我們平常執行的SQL語句就是一個事務,爲啥我們都不知道呢?
--因爲事務分爲顯式事務和隱性事務。
--而我們執行的SQL語句就是隱性事務,也叫系統提供的事務。不如說create table studnet(....)就是一個隱性事務
--這下感覺隱性事務是不是好多呀,既然多呢,那咱們就不多說了,我們還是說說顯式事務吧
--那什麼是顯式事務呢
--書上說的是顯式事務又稱爲用戶定義的事務。事務有一個開頭和一個結尾,它們指定了操作的邊界。邊界內的所有資源都參與同一個事務。當事務執行遇到錯誤時,將取消事務對數據庫所做的操作。
--因此呢我們需要把參與事務的語句封裝在一個begin tran/commit tran塊中。一個顯式事務的語句以begin transaction開始,至commit transaction或rollback transaction結束。事務的定義是一個完整的過程,
--指定事務的開始和表明事務的結束兩者缺一不可。
--事務學得差不多了但是還缺少語法,不然我們無法定義事務呀
--現在就讓我們來看看事務的用法吧
--(1)begin transaction語句定義事務的起點。語法格式:begin tran[saction]事務名稱 @事務變量名稱
--具體說明下哦:@事務變量名稱是由用戶定義的變量,必須用char、varchar、nchar或nvarchar數據類型來聲明該變量
--begin transaction語句的執行使全局變量@@trancount的值加1
--(2)commit transaction提交事務
--提交事務,意味着將事務開始以來所執行的所有數據修改爲數據庫的永久部分,因爲此也標誌着一個事務的結束。一旦執行了該命令,將不能回滾事務。只有所有修改都準備提交給數據庫時,才執行這一操作。
--語法爲:commit [tran[saction]]事務名稱 @事務變量名稱
--再來說明下:commit transaction語句的執行會使全局變量@@trancount的值減1
--(3)rollback transaction回滾事務
--就是說當事務執行過程中遇到錯誤時,使用rollback transaction語句使事務回滾到七點或者指定的保持點。同時系統講清除自事務起點或者某個保存點所做的數據修改,並且釋放由事務控制的資源。這條語句也標誌着事務的結束。
--語法爲:rollback[tran[saction][事務名稱] @事務變量名稱|存儲點名稱|@含有存儲點名稱的變量名]
--最後說明下:當條件回滾隻影響事務的一部分時,食物不要全部撤銷已執行的操作。可以讓事務回滾到指定位置。此時呢,需在事務中設定保存點(savepoint)。保存點所在位置之前的事務語句,不用回滾,
--即保存點之前的操作被視爲有效的。保存點的創建通過“save transaction 保存點名稱”語句來實現,然後再執行“rollback transaction 保存點名稱”語句回滾到該保存點;若事務回滾到起點,則全局變量@@trancount的值減1;
--若事務回滾到指定的保存點,則全局變量@@trancount的值不變。

--好了關於事務的相關定義語法啥的就介紹完了,其實真算起來也沒啥東西是不是。所謂萬事俱備,只欠東風了。既然我們把事務的定義啥的都瞭解了,下面就是進入實戰的階段啦。先夥伴你們的槍炮都上膛了嗎
--現在隨我一起向事務的案例發起進攻,衝啊,兄弟們,你們是無敵的!!!
--第一座城池事務的顯示開始和顯示回滾
--創建數據,創建表,聲明局部變量,開始事務,插入數據,顯示回滾,查看結果
use master --使用master作爲當前數據庫
go
--master數據庫中若存在用戶創建的表testTable,則刪除之
if object_id('testTable') is not null
drop table testTable
go

create table testTable(id int,name nchar(10))
go
declare @TransactionName varchar(20);--聲明局部變量
set @TransactionName='Transaction1';--局部變量賦初值
print @@trancount --向客戶端返回當鏈接上的已發生的begin transaction語句數
begin tran @TransactionName --顯示開始事務
    print @@trancount
    insert into testTable values(1,'張三')--插入記錄
    insert into testTable values(2,'李四')
rollback tran @TransactionName --顯示回滾事務,取消插入操作,將標中數據恢復到初始狀態
print @@trancount
--我們看代碼沒有錯誤吧,既然沒有錯誤,程序就會從開始運行到結束,最會有個回滾事務
--事務回滾到局部變量賦初值,所以呢張三李四的數據沒有插入到表中。
begin tran @TransactionName
    print @@trancount
    insert into testTable values(3,'王二')--插入記錄
    insert into testTable values(4,'麻子')
    if @@ERROR>0  --如果系統出現意外
        rollback tran @TransactionName  --則進行回滾操作 
    else
        commit tran @TransactionName  --顯示提交事務
print @@trancount

--首先來看事務開始@@trancount初值爲0,沒有啥意外,插入數據,這時候@@trancount的值爲0
--插入數據後,來到判斷,沒有錯誤,執行if條件的另一個語句,也就是提交事務,這時事務提交完成
--也就是說這個整體都己經完成了。所以兩條記錄成功插入了表中。
select * from testTable  --查詢表中所有記錄
drop table testTable  --刪除表
--第一座城池已經攻下,我們來總結下這座城帶給了我們什麼
--首先是數據表創建,先判斷該數據庫中是否有該表,有則刪除,沒有則創建
--其次就是變量的聲明和賦值
--然後就是顯示事務的創建開始,數據的插入事務回滾和不回滾,以及在事務中判斷事務是否會出現錯誤
--最終數據時提交還是回滾,以及查看數據,刪除表。
--不知不覺我們從這座城學到了這麼多東西。
--拿下一城士氣爆棚,繼續進攻
--第二城
--向教師表中插入一名教師的信息,運行正常則插入成功,反之回滾
use bankdb --使用bankdb作爲當前數據庫
go
--bankdb數據庫中若有teacher表則刪除
if OBJECT_ID('teacher') is not null
drop table teacher
go
--創建teacher表
create table teacher(
id int,name varchar(10),birthday datetime,department varchar(20),salary int null
)
go

begin transaction
    insert into teacher values('001','你妹',1937-07-01,'計算機學院',1000)
    insert into teacher values('002','你大爺',1921-08-0,'計算機學院',1000)
    select * from teacher

    update teacher set salary=salary+1000  --給教師加薪水
    Save transaction savepoint1
    insert into teacher values('003','鐵憨憨',1958-07-17,'計算機學院',null)
    if @@ERROR>0
        rollback transaction savepoint1
    if @@ERROR>0
        rollback transaction
    else 
        commit transaction
select * from teacher

--這座城大略一看沒啥東西呀是不是,但仔細一看save transaction [名字] 是啥東西啊,
--這是在事務內設置的保存點的名字,這樣在第一次回滾時,就可以回滾到這個保存點,就是savepoint1
--而不是回滾到整個事務,insert into teacher會被取消,但是事務本身仍然將繼續,也就是插入的教師信息將從事務中消除去
--但是事務本身仍在進行。數據表撤銷該教室信息的插入,但是給教師的薪水加100元的操作會被正常保存在數據庫中;到了後一個回滾,
--由於沒有給出回滾到的保存點名字,rollback transaction將回滾到begin transaction前的狀態,即修改和插入操作都被撤銷,就像沒有發生一樣。
--這座城太簡陋了,沒啥東西啊,同志們收拾下,準備下一個城池
--第三城
--刪除‘工業工程’系,將‘工業工程’系的學生規劃到‘企業管理’系
use 教學管理
go
begin transaction my_transaction_delete
use 教學管理 --使用數據庫-》教學管理
go
    delete from 系部 where 系別='工業工程'
    save transaction after_delet  --設置事務恢復斷點
--工業工程系的學生系別編號改爲企業管理系的系別編號
    update 學生 set 系別='企業管理' where 系別='工業工程'
    if @@ERROR<>0 or @@ROWCOUNT=0 then
--檢測更新是否成功,@@error返回上一個SQL語句狀態,非零及說明出錯,錯了則回滾
    begin 
    roolback tran after_delete
--回滾到保存點after_delete,如果使用rollback ny_transaction_delete則會回滾到事務開始之前
    commit tran
    print '更新學生表時出錯'
    return
    end

commit transaction my_transaction_delete
go

--大眼一看這是啥也,啥也不是,其實也就是說名不指定回滾事務名稱或保存點
--rollback transaction命令會將事務回滾到事務執行前,如果事務是嵌套的,則會回滾到最靠近的begin transaction命令前
--最終大boss 銀行裝帳業務的事務處理
--銀行轉賬是事務管理最典型的代表,通知門拿起你們的武器,向Boss發起進攻
use master
go
if DB_ID('bankdb') is not null
drop database bankdb
go
--創建數據庫bankdb
create database bankdb
go
--選擇當前數據庫
use bankdb
go
--創建表account
if OBJECT_ID('account') is not null
    drop table account
go

create table account(
id int identity(1,1) primary key,    --設置主鍵
cardno char(20) unique not null,--創建非空唯一值
balance numeric(18,2)
)

--插入記錄到account
insert into account values('01',1000.0)
insert into account values('02',1000.0)
go

--創建存儲過程以延時轉賬事務
if exists(
    select name from sys.objects where name=N'sp_transfer_money')
    drop procedure sp_transfer_money
go

create procedure sp_transfer_money  --創建存儲過程
    @out_cardno char(20),--轉出賬戶
    @in_cardno char(20),--轉入賬戶
    @money numeric(18,2)--轉賬金額
as
begin
    declare @remain numeric(18,2)
    select @remain=balance from account where cardno=@out_cardno
    if @money>0
        if @remain>=@money
            begin
                begin transaction t1--開始執行事務
                --執行的第一個操作,轉賬出錢,減去轉出的金額
                update account set balance=balance-@money where cardno=@out_cardno
                --執行第二個操作,接受轉賬的金額,餘額增加
                update account set balance=balance+@money where cardno=@in_cardno

                if @@ERROR>0  --如果系統出現意外
                    begin
                        rollback tran t1  --進行回滾操作,恢復轉賬之前狀態
                        return 0
                    end
                else
                    begin 
                        commit transaction t1  --顯示提交事務
                        print '轉賬成功!'
                    end
                end
            else
                begin
                    print '餘額不足'
                end
            else
                print '轉賬金額應大於餘額'
            end
go
--執行儲存過程
exec sp_transfer_money '01','02',50

--查看轉賬記錄
select * from account

--相信大家學到這,對銀行轉賬事務處理不難理解吧。Boss已除,天下太平,我也要卸甲歸田啦。
--最後再來總結下使用事務的考慮
--事務應儘可能短哦
--因爲較長的事務增加了事務佔用數據的時間,會使其他必須等待訪問數據的事務等待較長時間。
--爲使事務儘可能短,可以採取以下方法
--(1)事務在使用過程控制語句改變程序運行順序時,一定要非常小心。
--(2)在開始事務之前,一定要了解用戶交互式操作才能得到的消息,以便在事務執行過程中,
--可以避免進行一些耗費時間的交互式操作,從而縮短事務進程的時間。
--(3)應該儘可能地使用一些數據操縱語言,例如insert,update和delete語句,因爲這些語句主要是操縱數據庫中的數據
--而對於一些數據定語言,應該儘可能地少用或者不用,因爲數據定義語言的操作既佔用比較長的時間,又佔用比較多的資源
--並且數據定定義語言的操作通常不涉及數據,所以應在事務中儘可能的少用或者不用
--(4)在使用數據操縱語言時,一定要在這些語句中使用條件判斷語句,使得數據操縱語言涉及到儘可能少的記錄,從而縮短事務的處理時間
--還有就是不建議使用嵌套事務


 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章