sql之事務和併發

一、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.

若是在修改的話。也是在第一個 更新線程中的事務更新以後的數據進行執行修改的操做,不會報錯。

相關文章
相關標籤/搜索