sqlserver 併發機制

 

1、事務四大屬性html

分別是原子性、一致性、隔離性、持久性。數據庫

一、原子性(Atomicity)網絡

原子性是指事務包含的全部操做要麼所有成功,要麼所有失敗回滾,所以事務的操做若是成功就必需要徹底應用到數據庫,若是操做失敗則不能對數據庫有任何影響。session

二、一致性(Consistency)併發

一致性是指事務必須使數據庫從一個一致性狀態變換到另外一個一致性狀態,也就是說一個事務執行以前和執行以後都必須處於一致性狀態。舉例來講,假設用戶A和用戶B二者的錢加起來一共是1000,那麼無論A和B之間如何轉帳、轉幾回帳,事務結束後兩個用戶的錢相加起來應該還得是1000,這就是事務的一致性。性能

三、隔離性(Isolation)優化

隔離性是當多個用戶併發訪問數據庫時,好比同時操做同一張表時,數據庫爲每個用戶開啓的事務,不能被其餘事務的操做所幹擾,多個併發事務之間要相互隔離。關於事務的隔離性數據庫提供了多種隔離級別,稍後會介紹到。spa

四、持久性(Durability).net

持久性是指一個事務一旦被提交了,那麼對數據庫中的數據的改變就是永久性的,即使是在數據庫系統遇到故障的狀況下也不會丟失提交事務的操做。例如咱們在使用JDBC操做數據庫時,在提交事務方法後,提示用戶事務操做完成,當咱們程序執行完成直到看到提示後,就能夠認定事務已經正確提交,即便這時候數據庫出現了問題,也必需要將咱們的事務徹底執行完成。不然的話就會形成咱們雖然看到提示事務處理完畢,可是數據庫由於故障而沒有執行事務的重大錯誤。這是不容許的。命令行

2、事務的隔離級別

一、爲何要設置隔離級別

https://blog.csdn.net/xiaokang123456kao/article/details/75268240

在數據庫操做中,在併發的狀況下可能出現以下問題:

髒讀:一個事務讀取到了另一個事務沒有提交的數據。

A修改了數據,隨後B又讀出該數據,但A由於某些緣由取消了對數據的修改,數據恢復原值,此時B獲得的數據就與數據庫內的數據產生了不一致 

幻讀:同一事務中,用一樣的操做讀取兩次,獲得的記錄數不相同。

A讀取數據,隨後B又插入了數據,此時A再讀數據是發現先後兩次獲取的數據行集不一致 

不可重複讀:在同一事務中,兩次讀取同一數據,獲得內容不一樣。

A用戶讀取數據,隨後B用戶讀出該數據並修改,此時A用戶再讀取數據時發現先後兩次的值不一致

丟失更新:事務T1讀取了數據,並執行了一些操做,而後更新數據。事務T2也作相同的事,則T1和T2更新數據時可能會覆蓋對方的更新,從而引發錯誤。

A,B兩個用戶讀同一數據並進行修改,其中一個用戶的修改結果破壞了另外一個修改的結果,好比訂票系統 

併發控制的主要方法是經過鎖,在一段時間內禁止用戶作某些操做以免產生數據不一致

1. 更新丟失(Lost update)

若是多個線程操做,基於同一個查詢結構對錶中的記錄進行修改,那麼後修改的記錄將會覆蓋前面修改的記錄,前面的修改就丟失掉了,這就叫作更新丟失。這是由於系統沒有執行任何的鎖操做,所以併發事務並無被隔離開來。

第1類丟失更新:事務A撤銷時,把已經提交的事務B的更新數據覆蓋了。  

第2類丟失更新:事務A覆蓋事務B已經提交的數據,形成事務B所作的操做丟失。  

解決方法:對行加鎖,只容許併發一個更新事務。

2. 髒讀(Dirty Reads)

髒讀(Dirty Read):A事務讀取B事務還沒有提交的數據並在此基礎上操做,而B事務執行回滾,那麼A讀取到的數據就是髒數據。  

解決辦法:若是在第一個事務提交前,任何其餘事務不可讀取其修改過的值,則能夠避免該問題。

3. 不可重複讀(Non-repeatable Reads)

一個事務對同一行數據重複讀取兩次,可是卻獲得了不一樣的結果。事務T1讀取某一數據後,事務T2對其作了修改,當事務T1再次讀該數據時獲得與前一次不一樣的值。  

解決辦法:若是隻有在修改事務徹底提交以後才能夠讀取數據,則能夠避免該問題。

4. 幻象讀

指兩次執行同一條 select 語句會出現不一樣的結果,第二次讀會增長一數據行,並無說這兩次執行是在同一個事務中。通常狀況下,幻象讀應該正是咱們所須要的。但有時候卻不是,若是打開的遊標,在對遊標進行操做時,並不但願新增的記錄加到遊標命中的數據集中來。隔離級別爲 遊標穩定性 的,能夠阻止幻象讀。例如:目前工資爲1000的員工有10人。那麼事務1中讀取全部工資爲1000的員工,獲得了10條記錄;這時事務2向員工表插入了一條員工記錄,工資也爲1000;那麼事務1再次讀取全部工資爲1000的員工共讀取到了11條記錄。  

解決辦法:若是在操做事務完成數據處理以前,任何其餘事務都不能夠添加新數據,則可避免該問題。

二、事務的隔離級別

爲了解決數據庫併發問題,數據庫提供了幾種隔離級別。如下隔離級別由低到高。

Read uncommitted(未受權讀取、讀未提交): 若是一個事務已經開始寫數據,則另一個事務則不容許同時進行寫操做,但容許其餘事務讀此行數據。該隔離級別能夠經過「排他寫鎖」實現。這樣就避免了更新丟失,卻可能出現髒讀。也就是說事務B讀取到了事務A未提交的數據。 Read committed(受權讀取、讀提交): 讀取數據的事務容許其餘事務繼續訪問該行數據,可是未提交的寫事務將會禁止其餘事務訪問該行。該隔離級別避免了髒讀,可是卻可能出現不可重複讀。事務A事先讀取了數據,事務B緊接了更新了數據,並提交了事務,而事務A再次讀取該數據時,數據已經發生了改變。 Repeatable read(可重複讀取): 可重複讀是指在一個事務內,屢次讀同一數據。在這個事務尚未結束時,另一個事務也訪問該同一數據。那麼,在第一個事務中的兩次讀數據之間,即便第二個事務對數據進行修改,第一個事務兩次讀到的的數據是同樣的。這樣就發生了在一個事務內兩次讀到的數據是同樣的,所以稱爲是可重複讀。讀取數據的事務將會禁止寫事務(但容許讀事務),寫事務則禁止任何其餘事務。這樣避免了不可重複讀取和髒讀,可是有時可能出現幻象讀。(讀取數據的事務)這能夠經過「共享讀鎖」和「排他寫鎖」實現。 Serializable(序列化): 提供嚴格的事務隔離。它要求事務序列化執行,事務只能一個接着一個地執行,但不能併發執行。若是僅僅經過「行級鎖」是沒法實現事務序列化的,必須經過其餘機制保證新插入的數據不會被剛執行查詢操做的事務訪問到。序列化是最高的事務隔離級別,同時代價也花費最高,性能很低,通常不多使用,在該級別下,事務順序執行,不只能夠避免髒讀、不可重複讀,還避免了幻像讀。   隔離級別越高,越能保證數據的完整性和一致性,可是對併發性能的影響也越大。對於多數應用程序,能夠優先考慮把數據庫系統的隔離級別設爲Read Committed。它可以避免髒讀取,並且具備較好的併發性能。儘管它會致使不可重複讀、幻讀和第二類丟失更新這些併發問題,在可能出現這類問題的個別場合,能夠由應用程序採用悲觀鎖或樂觀鎖來控制。大多數數據庫的默認級別就是Read committed,好比Sql Server , Oracle。MySQL的默認隔離級別就是Repeatable read。 --------------------- 做者:想做會飛的魚 來源:CSDN 原文:https://blog.csdn.net/xiaokang123456kao/article/details/75268240 版權聲明:本文爲博主原創文章,轉載請附上博文連接!

 

* 爲何須要鎖?

在併發環境下,若是多個客戶端訪問同一條數據,此時就會產生數據不一致的問題,如何解決,經過加鎖的機制,常見的有兩種鎖,樂觀鎖和悲觀鎖,能夠在必定程度上解決併發訪問。SQLSERVER經過鎖來提供ACID屬性,處理併發訪問.
* 什麼是悲觀鎖?

悲觀鎖,正如其名,具備強烈的獨佔和排他特性。它指的是對數據被外界(包括本系統當前的其餘事務,以及來自外部系統的事務處理)修改持保守態度,所以,在整個數據處理過程當中,將數據處於鎖定狀態。悲觀鎖的實現,每每依靠數據庫提供的鎖機制(也只有數據庫層提供的鎖機制才能真正保證數據訪問的排他性,不然,即便在本系統中實現了加鎖機制,也沒法保證外部系統不會修改數據)。

悲觀鎖主要包含共享鎖和排他鎖。
共享鎖:又稱讀鎖/S鎖,共享鎖是在執行select操做時使用的鎖機制。共享鎖就是多個事務對於同一數據能夠共享一把鎖,都能訪問到數據,可是隻能讀不能修改。
排他鎖:又稱寫鎖/X鎖,排它鎖是在執行update,delete等對數據有修改操做時使用的鎖。排他鎖就是不能與其餘鎖並存,如一個事務獲取了一個數據行的排他鎖,其餘事務就不能再獲取該行的其餘鎖,包括共享鎖和排他鎖,可是獲取排他鎖的事務是能夠對數據就行讀取和修改。

共享鎖(S鎖):用於讀取資源所加的鎖。擁有共享鎖的資源不能被修改。共享鎖默認狀況下是讀取了資源立刻被釋放。

排他鎖(X鎖): 和其它任何鎖都不兼容,包括其它排他鎖。排它鎖用於數據修改,當資源上加了排他鎖時,其餘請求讀取或修改這個資源的事務都會被阻塞,知道排他鎖被釋放爲止。

 

共享鎖:

T1:  select * from A;(加共享鎖A)
T2:  select * from A;(加共享鎖B)

* 什麼是樂觀鎖?

樂觀鎖機制採起了更加寬鬆的加鎖機制。樂觀鎖是相對悲觀鎖而言,也是爲了不數據庫幻讀、業務處理時間過長等緣由引發數據處理錯誤的一種機制,但樂觀鎖不會刻意使用數據庫自己的鎖機制,而是依據數據自己來保證數據的正確性。

實現樂觀鎖通常來講有如下2種方式:
a. 使用版本號
使用數據版本(Version)記錄機制實現,這是樂觀鎖最經常使用的一種實現方式。通常是在數據表中加上一個數據版本號version字段,表示數據被修改的次數,當數據被修改時,version值會加一。當咱們提交更新的時候,判斷數據庫表對應記錄的當前版本信息與第一次取出來的version值進行比對,若是數據庫表當前版本號與第一次取出來的version值相等,則予以更新,不然認爲是過時數據。
b. 使用時間戳
樂觀鎖定的第二種實現方式和第一種差很少,一樣是在須要樂觀鎖控制的table中增長一個字段,字段類型使用時間戳(timestamp), 和上面的version相似,也是在更新提交的時候檢查當前數據庫中數據的時間戳和本身更新前取到的時間戳進行對比,若是一致則OK,不然就是版本衝突。
* 悲觀鎖與樂觀鎖區別與聯繫?
* 悲觀鎖與樂觀鎖的使用場景?

悲觀鎖:比較適合寫入操做比較頻繁的場景,若是出現大量的讀取操做,每次讀取的時候都會進行加鎖,這樣會增長大量的鎖的開銷,下降了系統的吞吐量。
樂觀鎖:比較適合讀取操做比較頻繁的場景,若是出現大量的寫入操做,數據發生衝突的可能性就會增大,爲了保證數據的一致性,應用層須要不斷的從新獲取數據,這樣會增長大量的查詢操做,下降了系統的吞吐量。

實際生產環境裏邊,若是併發量不大且不容許髒讀,徹底可使用悲觀鎖定的方法,這種方法使用起來很是方便和簡單。可是若是系統的併發很是大的話,悲觀鎖定會帶來很是大的性能問題,因此就要選擇樂觀鎖定的方法。

悲觀鎖假定其餘用戶企圖訪問或者改變你正在訪問、更改的對象的機率是很高的,所以在悲觀鎖的環境中,在你開始改變此對象以前就將該對象鎖住,而且直到你提交了所做的更改以後才釋放鎖。悲觀的缺陷是不管是頁鎖仍是行鎖,加鎖的時間可能會很長,這樣可能會長時間的限制其餘用戶的訪問,也就是說悲觀鎖的併發訪問性很差。

樂觀鎖則認爲其餘用戶企圖改變你正在更改的對象的機率是很小的,所以樂觀鎖直到你準備提交所做的更改時纔將對象鎖住,當你讀取以及改變該對象時並不加鎖。可見樂觀鎖加鎖的時間要比悲觀鎖短,樂觀鎖能夠用較大的鎖粒度得到較好的併發訪問性能。可是若是第二個用戶剛好在第一個用戶提交更改以前讀取了該對象,那麼當他完成了本身的更改進行提交時,數據庫就會發現該對象已經變化了,這樣,第二個用戶不得不從新讀取該對象並做出更改。這說明在樂觀鎖環境中,會增長併發用戶讀取對象的次數。


樂觀鎖例子:

T1:(用戶A) begin tran1
T1:(用戶A) select * from user where id = 3; (version=1)
T2:(用戶B) begin tran2
T2:(用戶B) select * from user where id = 3; (version=1)
T1:(用戶A) update user set name='amy', version=version+1 where id = 3 and version = 1;(受影響的行: 1)
T2:(用戶B) update user set name='tom', version=version+1 where id = 3 and version = 1;(受影響的行: 0)
T1:(用戶A) end tran1
T2:(用戶B) end tran2

 

 

 

如何查看鎖

一、使用sys.dm_tran_locks這個DMV

SELECT  l.request_session_id,
2         DB_NAME(l.resource_database_id),OBJECT_NAME(p.object_id),
3         l.resource_description,l.request_type,
4         l.request_status,request_mode
5 FROM sys.dm_tran_locks AS l
6 LEFT JOIN sys.partitions AS p
7 ON l.resource_associated_entity_id=p.hobt_id

二、使用Profiler來捕捉鎖信息

 

如何查看死鎖

 

問題再現:使用SQL Server2008數據庫,右鍵點擊tempdb數據庫,查看屬性。

 

 

  • 有時會彈出錯誤提示框:已超過了鎖請求超時時段。 (Microsoft SQL Server,錯誤: 1222)

  • 經過SQL命令行,查看是否有死鎖進程,具體命令如圖所示,其中【tempdb】是要訪問的數據庫名。通過查詢,得知存在一個死鎖進程【2973】,佔用了資源,使正常的請求沒法獲得及時響應。

  • 執行Kill進程命令,解鎖進程,釋放資源,具體代碼如圖所示。

  • 5

    執行完kill進程命令後,再查詢一次進程,發現無死鎖進程。數據庫訪問恢復正常。

    如何解決SQLServer已超過了鎖請求超時時段

 

 

 

 

 

3、死鎖的預防與優化

 

預防死鎖

 

預防死鎖就是破壞四個必要條件中的某一個和幾個,使其不能造成死鎖。有以下幾種辦法:

 

1)破壞互斥條件

 

破壞互斥條件有比較嚴格的限制,在SQL Server中,若是業務邏輯上容許髒讀,則能夠經過將隔離等級改成未提交讀或使用索引提示。這樣使得讀取不用加S鎖,從而避免了和其它查詢所加的與S鎖不兼容的鎖互斥,進而減小了死鎖出現的機率。

 

2)破壞請求和等待條件

 

這點因爲事務存在原子性,是不可破壞的,由於解決辦法是儘可能的減小事務的長度,事務內執行的越快越好。這也能夠減小死鎖出現的機率。

 

3)破壞不剝奪條件

 

因爲事務的原子性和一致性,不剝奪條件一樣不可破壞。但咱們能夠經過增長資源和減小資源佔用兩個角度來考慮。

 

增長資源:好比說經過創建非彙集索引,使得有了額外的資源,查詢不少時候就再也不索要鎖基本表,轉而鎖非彙集索引,若是索引可以"覆蓋(Cover)"查詢,那更好不過。所以索引Include列不只僅減小書籤查找來提升性能,還能減小死鎖。

 

減小資源佔用:好比說查詢時,能用select col1,col2這種方式,就不要用select * .這有可能帶來沒必要要的書籤查找

 

 

 

最大限度減小死鎖的方法

 

  1. 按同一順序訪問對象: 按同一順序訪問對象也就是:第一個事務提交或回滾後,第二個事務繼續進行,這樣不會發生死鎖。
  2. 避免事務中的用戶交互: 避免編寫包含用戶交互的事務,由於運行沒有用戶交互的批處理的速度要遠遠快於用戶手動響應查詢的速度,例如答覆應用程序請求參數的提示。例如,若是事務正在等待用戶輸入,而用戶去吃午飯了或者甚至回家過週末了,則用戶將此事務掛起使之不能完成。這樣將下降系統的吞吐量,由於事務持有的任何鎖只有在事務提交或回滾時纔會釋放。即便不出現死鎖的狀況,訪問同一資源的其它事務也會被阻塞,等待該事務完成。
  3. 保持事務簡短並在一個批處理中: 在同一數據庫中併發執行多個須要長時間運行的事務時一般發生死鎖。事務運行時間越長,其持有排它鎖或更新鎖的時間也就越長,從而堵塞了其它活動並可能致使死鎖。 保持事務在一個批處理中,能夠最小化事務的網絡通訊往返量,減小完成事務可能的延遲並釋放鎖。
  4. 使用低隔離級別: 肯定事務是否能在更低的隔離級別上運行,執行提交讀取容許事務讀取另外一個事務已讀取(未修改)的數據,而沒必要等待第一個事務完成。使用較低的隔離級別(例如提交讀取)而不使用較高的隔離級別(例如可串行讀)能夠縮短持有共享鎖的時間,從而下降了鎖定爭奪。
  5. 使用綁定鏈接: 使用綁定鏈接使同一應用程序所打開的兩個或多個鏈接能夠相互合做。次級鏈接所得到的任何鎖能夠象由主鏈接得到的鎖那樣持有,反之亦然,所以不會相互阻塞。

按照同一順序訪問數據庫資源,上述例子就不會發生死鎖啦

  • 保持是事務的簡短,儘可能不要讓一個事務處理過於複雜的讀寫操做。事務過於複雜,佔用資源會增多,處理時間增加,容易與其它事務衝突,提高死鎖機率。
  • 儘可能不要在事務中要求用戶響應,好比修改新增數據以後在完成整個事務的提交,這樣延長事務佔用資源的時間,也會提高死鎖機率。
  • 儘可能減小數據庫的併發量。
  • 儘量使用分區表,分區視圖,把數據放置在不一樣的磁盤和文件組中,分散訪問保存在不一樣分區的數據,減小由於表中放置鎖而形成的其它事務長時間等待。
  • 避免佔用時間很長而且關係表複雜的數據操做。
  • 使用較低的隔離級別,使用較低的隔離級別比使用較高的隔離級別持有共享鎖的時間更短。這樣就減小了鎖爭用。

 

優化死鎖的一些建議

 

(1)對於查詢頻繁的表儘可能使用匯集索引;

 

(2)設法避免一次性影響大量記錄的SQL語句,特別是INSERT和UPDATE語句;

 

(3)設法讓UPDATE和DELETE語句使用合適的索引;

 

(4)使用嵌套事務時,避免提交和回退衝突;

 

(5)對數據一致性要求不高的查詢使用 WITH(NOLOCK)

 

(6)減少事務的體積,事務應最晚開啓,最先關閉,全部不是必須使用事務的操做必須放在事務外。

 

(7)查詢只返回你須要的列,不建議使用 SELECT * FROM 這種寫法。

 

 

 

 

參考文章: http://www.matools.com/blog/190438028

 

 

---------------------

 

做者:想做會飛的魚

 

來源:CSDN

 

原文:https://blog.csdn.net/xiaokang123456kao/article/details/75268240

相關文章
相關標籤/搜索