一、Transaction(事務)是什麼:sql
事務是做爲單一工做單元而執行的一系列操做。包括增刪查改。數據庫
二、事務的種類:架構
事務分爲顯示事務和隱式事務:併發
隱式事務:就是日常咱們使用每一條sql 語句就是一個事務,只不過他們執行完成以後事務就跟着結束了。性能
顯示事務:就是須要咱們來手寫了,這個時候就能夠進行控制事務的開始和結束了。spa
1 --顯式事務(對事物能夠進行控制) 2 3 --開始事務 4 begin transaction; 5 update [Sales.Shippers] 6 set companyname='順豐' where shipperid=5; 7 8 select * from [Sales.Shippers]; 9 10 --結束事務: 11 --第一種:事務的回滾 12 rollback; 13 14 --第二種:事務的提交 15 commit;
三、事務很重要的四個屬性: 線程
一、原子性:事務必須是原子工做單位。——在事務中修改數據,要麼全都執行,要麼全都不執行。在事務執行完成以前(調提交指令寫入到sql的事務日誌以前),出現問題或重啓,sql server 會回滾全部的修改事務。 可是也有例外的錯誤不會回滾事務————例如:主鍵衝突和鎖超時等。 錯誤日誌會 捕獲這些錯誤的指令,並記錄日誌裏面,而後執行一些操做(例如:回滾事務)3d
二、一致性:發生在同一進程的事物裏面的 修改和 查詢是不會產生衝突的。保持訪問的數據的一致性。版本控制
三、隔離性:控制數據訪問的機制; 說明: 一個事務正在對一個表的數據正在修改, 尚未執行完成;;這時另外一個事務,想要查詢裏面的數據,是不能查到的,必須等到 修改的事務執行完成。:sql server 採用的 「鎖」的機制,將正在修改的事務 處理的表的數據 鎖定。這樣是爲了保證數據同步,數據的一致性。日誌
四、持久性: 當一個事務的指令 已經提交到 事務日誌裏面,即便磁盤上的數據尚未修改,這個時候數據庫的服務中止,在服務重啓的時候還會將事務日誌裏的指令執行(進行回覆處理)。保證數據的持久性。
上面將基本的事務介紹了一下,下面開始介紹併發。因此必需要介紹就是事務的「鎖」。
四、事務中的鎖
事務中都含有什麼鎖呢?
最經常使用的鎖:排它鎖(獨佔鎖)和共享鎖,還有其餘的鎖,這裏就不作介紹了,好比:更新鎖、架構鎖、意向鎖等。
五、排它鎖和共享鎖
排它鎖:
當一個事務執行更新修改操做的時候會申請排它鎖,主要是在寫操做裏面使用。須要注意的兩點:一、一個事務含有排它鎖,就不能含有其餘任何鎖。二、一條數據只能被一個排它鎖鎖住,就不能再被其餘排他鎖鎖定。
共享鎖:
主要是在讀操做中使用,而且多個事務能夠同時對一條數據使用共享鎖。
排它鎖和共享鎖最重要的區別:排它鎖是不能被控制他的處理方式和時間,可是共享鎖是能夠控制其隔離級別來控制其處理的時間。
1 begin transaction; 2 update [Sales.Shippers] set companyname='順豐' where shipperid=5; 3 --事務尚未查詢完成,爲這條數據 加上一個 排它鎖。這時這條數據就不能被其餘進程 訪問到
事務尚未執行完成,再開一個線程,執行查詢操做
1 select * from [Sales.Shippers] where shipperid=5
由於讀操做默認使用的共享鎖,可是這個時候這條數據已經被其餘線程的排它鎖鎖住,因此會形成阻塞,直到排它鎖釋放。
六、隔離級別
首先要先明白三點:
一、用於控制併發用戶如何讀寫數據的操作。
二、讀操做默認使用共享鎖;寫操做須要使用排它鎖。
三、讀操做可以控制他的處理的方式,寫操做不能控制它的處理方式
隔離級別分爲六種:
read uncommited(讀取未提交數據),read commited(讀取已提交數據)讀取的默認方式,repeatable read(可重複讀),serializable(可序列化),snapshot(快照),read commited snapshot(已經提交讀隔離)(後兩個是sql server 2005 裏面 引入的)。隔離的強度依次遞增。
一、read uncommitted:
1 select * from [Sales.Shippers] where shipperid=3;
查詢結果:
在本線程內執行:
1 begin transaction; 2 update [Sales.Shippers] set companyname='圓通' where shipperid=3;
在另一個線程內 使用 read uncommitted 隔離級別 查詢數據:
1 --設置讀操做的隔離級別 2 set transaction isolation level read uncommitted; 3 select * from [Sales.Shippers] where shipperid=3;
查詢結果:
若是這個時候將那個事務回滾,那麼這個時候 查詢到的數據就是「髒數據」。
總結:
read uncommitted:最低的隔離級別:查詢的時候不會請求共享鎖,因此不會和排它鎖產生衝突(不會等待排它鎖執行完),查詢效率很是高,速度飛快。可是缺點:會查到「髒數據」(排它鎖的事務已經將數據修改,還沒提交,這個時候查詢到的數據 是已經更改過的。若是事務回滾,就是「髒數據」)
優勢:查詢效率很是高,速度很是快。
缺點:會產生「髒數據」
適用性:
適用於 像聊天軟件的 聊天記錄,會是軟件的運行速度很是快。 可是不適用於 商務軟件。尤爲是銀行
二、read committed
讀取的默認隔離級別就是read committed 和上面正好相反。若是上面狀況,採用read committed 隔離級別查詢的話查到的就是尚未更改以前的數據。
因此在這裏就再也不演示。
三、repeatable read:
查詢的時候會加上共享鎖,可是查詢完成以後,共享鎖就會被撤銷。好比一些購票系統,若是查到票了,當買的時候就沒有,這是不行的。因此要在查詢到數據以後作一些延遲共享鎖,進而阻塞排它鎖來修改。(若是查詢的事務沒有提交,不會釋放共享鎖,這個時候獨佔鎖就不能訪問這條數據)
注意:一、repeatable 只會鎖定查詢的數據 ,而 其餘行數據還能夠進行 修改(更新、刪除)(下面那條語句共享鎖只會鎖定 shipperid爲4 的行)
二、其餘進行插入數據,而且插入的數據知足第一次開始事務時的 查詢的篩選條件的時候;第二次查詢的時候就會將新插入的數據 查詢出來。這就叫作「幻讀」(解決幻讀,須要更高級別的隔離,就是下面的serializable)
在查詢線程裏面執行sql語句:
1 set transaction isolation level repeatable read; 2 begin transaction; 3 select * from [Sales.Shippers] where shipperid=4;
而後在 另一個線程內執行修改語句:
update [Sales.Shippers] set companyname='shit' where shipperid=4;
這個時候會將更改的線程阻塞掉:
幻讀演示:
在線程1 內 輸入:
1 set transaction isolation level serializable; 2 begin transaction; 3 select * from [Sales.Shippers] where companyname='順豐'
結果:
在線程2 內 輸入 插入語句:
1 insert into [Sales.Shippers] (companyname,phone) values('順豐',112321211)
結果:
在 用 共享鎖 執行查詢語句:
1 select * from [Sales.Shippers] where companyname='順豐'
結果:
四、serializable(可序列化)
更高級的 隔離。用戶解決「幻讀」(上面提到的)。就是使用上面的(repeatable read) 加上共享鎖 並不撤銷,若是鎖定的 一行數據,那麼 其餘的進程 還能夠對 其餘的數據進行操做,也能夠 進行新增和刪除的操做。 因此若是想要在查詢的時候,不能對整張表進行任何操做,那麼就要 將表的結構也 鎖定 (就須要使用 更強的 鎖定)
在查詢線程執行sql語句:
1 set transaction isolation level serializable; 2 begin transaction; 3 select * from [Sales.Shippers] where companyname='順豐'
那麼在另一個線程執行下面兩個語句,不論那一條語句都會阻塞住:
1 insert into [Sales.Shippers] (companyname,phone) values('順豐',112321211) --由於插入的數據知足 查詢的篩選的 條件條件了
結果:
總結:
可序列話 隔離讀操做:用戶 解決 幻影數據(將標的數據和表的結構都鎖定),是併發下降...隔離級別越高,併發越低,可是效率越低,因此不是要肯定使用 最好不要使用
下面兩種隔離級別是在 sql server 2005纔出現的,隔離級別更高:
五、snapshot(快照)
爲數據產生一個臨時數據庫,當sql server 數據更新以前將當前數據庫複製到 tempdb數據庫裏面,查詢就是從tempdb數據庫中查詢,可是不能再 使用 snapshot 線程的事務內執行 修改操做,由於不能修改 舊版本數據庫(tempdb),會報錯。
snapshot隔離級別,讀操做 不適用 共享鎖,使用的是「行版本控制」,因此讀數據的性能效率很高,可是修改操做性能就下降的不少。
由於是將 數據庫 中的數據 複製到 tempdb 數據庫中,因此不會產生 幻讀。
--設置數據庫支持快照隔離級別: alter database ssdemo set allow_snapshot_isolation on;--這個時候會產生一個臨時數據庫(寫操做的排它鎖鎖定的是 現實存在的數據庫,,讀操做的讀取的是 臨時數據庫)
在一個線程中執行 更新操做,用排它鎖鎖定當前數據
begin transaction; --使用 排它鎖(獨佔鎖)X,鎖定 下面的那條數據 update [Sales.Shippers] set companyname='飛鳳' where shipperid=3;
這個時候在在另一個線程中查詢這條數據(默認的隔離級別),就會將當前線程阻塞。
若是使用 snapshot 隔離級別查詢就不會阻塞。
1 set transaction isolation level snapshot; 2 --下面的就能夠 從臨時數據庫中查詢到數據 3 begin transaction; 4 --沒有使用 共享鎖 ,使用的行版本控制,因此無論數據是否加了 獨佔鎖,均可以到 tempdb中 讀取數據(若是數據修改了纔到tempdb中讀取數據) 5 select * from [Sales.Shippers] where shipperid=3;--查詢到的 是尚未完成更新以前的數據
可是同時也會帶來兩個問題:
一、當 另一個事務 已經提交,可是這邊的查詢到數據仍是沒有修改。由於 每次查詢到的快照是針對於 本次回話對應的那個 transaction 的,由於在這個事務裏面是沒有修改的,因此查詢到的數據是沒有修改的。
二、(更新問題)由於 那邊的數據已是 飛鳳公司了,可是這裏仍是 聯邦,因此,在這個事務裏面是不能對錶進行修改,由於訪問的是臨時數據庫,想要對 數據庫修改是不可能的(sql server 就會報錯,阻止修改)
針對於上面兩個問題,因此下面 更高的隔離級別出現了 read committed snapshot:
六、read committed snapshot
首先開啓數據庫的 read committed snapshot 隔離級別:
1 --設置 數據庫 爲 讀取已經提交的快照 開啓 2 alter database ssdemo set read_committed_snapshot on;
在一個線程中執行:
begin transaction; update [Sales.Shippers] set companyname='聯邦' where shipperid=3;
在另一個線程中:
1 --不用顯示聲明使用 read committed snapshot 隔離級別,由於設置完 read_committed_snapshot 隔離級別啓動,默認就是 read commited snapshot 隔離級別 2 begin transaction; 3 select * from [Sales.Shippers] where shipperid=3;--查詢到是 已經提交以後的數據 4 5 update [Sales.Shippers] set companyname='xiaoxiao' where shipperid=3;
這個時候查詢到的數據是尚未更改以前的,若是將 前面的那個回話提交,那麼在查詢 查詢到的數據是 提交修改以後的數據。因此解決了上面的問題1.
若是在修改的話。也是在第一個 更新線程中的事務更新以後的數據進行執行修改的操做,不會報錯。