當用戶併發對數據庫進行操做時會帶來數據不一致的問題,例如:sql
因此在出現用戶併發操做的時候,應該提供鎖,就是在一段時間內禁止用戶作某些操做以免產生數據不一致。數據庫
共享鎖 (S):安全
共享 (S) 鎖容許併發事務讀取 (SELECT) 一個資源。資源上存在共享 (S) 鎖時,任何其它事務都不能修改數據。一旦已經讀取數據,便當即釋放資源上的共享 (S) 鎖,除非將事務隔離級別設置爲可重複讀或更高級別,或者在事務生存週期內用鎖定提示保留共享 (S) 鎖bash
更新鎖網絡
更新 (U) 鎖能夠防止一般形式的死鎖。通常更新模式由一個事務組成,此事務讀取記錄,獲取資源(頁或行)的共享 (S) 鎖,而後修改行,此操做要求鎖轉換爲排它 (X) 鎖。若是兩個事務得到了資源上的共享模式鎖,而後試圖同時更新數據,則一個事務嘗試將鎖轉換爲排它 (X) 鎖。共享模式到排它鎖的轉換必須等待一段時間,由於一個事務的排它鎖與其它事務的共享模式鎖不兼容;發生鎖等待。第二個事務試圖獲取排它 (X) 鎖以進行更新。因爲兩個事務都要轉換爲排它 (X) 鎖,而且每一個事務都等待另外一個事務釋放共享模式鎖,所以發生死鎖。session
若要避免這種潛在的死鎖問題,請使用更新 (U) 鎖。一次只有一個事務能夠得到資源的更新 (U) 鎖。若是事務修改資源,則更新 (U) 鎖轉換爲排它 (X) 鎖。不然,鎖轉換爲共享鎖。併發
排它鎖工具
排它 (X) 鎖能夠防止併發事務對資源進行訪問。其它事務不能讀取或修改排它 (X) 鎖鎖定的數據sqlserver
意向鎖 意向鎖表示 SQL Server 須要在層次結構中的某些底層資源上獲取共享 (S) 鎖或排它 (X) 鎖。例如,放置在表級的共享意向鎖表示事務打算在表中的頁或行上放置共享 (S) 鎖。在表級設置意向鎖可防止另外一個事務隨後在包含那一頁的表上獲取排它 (X) 鎖。意向鎖能夠提升性能,由於 SQL Server 僅在表級檢查意向鎖來肯定事務是否能夠安全地獲取該表上的鎖。而無須檢查表中的每行或每頁上的鎖以肯定事務是否能夠鎖定整個表。性能
意向鎖包括意向共享 (IS)、意向排它 (IX) 以及與意向排它共享 (SIX)
意向共享 (IS) 經過在各資源上放置 S 鎖,代表事務的意向是讀取層次結構中的部分(而不是所有)底層資源。
意向排它 (IX) 經過在各資源上放置 X 鎖,代表事務的意向是修改層次結構中的部分(而不是所有)底層資源。IX 是 IS 的超集。
與意向排它共享 (SIX) 經過在各資源上放置 IX 鎖,代表事務的意向是讀取層次結構中的所有底層資源並修改部分(而不是所有)底層資源。容許頂層資源上的併發 IS 鎖。例如,表的 SIX 鎖在表上放置一個 SIX 鎖(容許併發 IS 鎖),在當前所修改頁上放置 IX 鎖(在已修改行上放置 X 鎖)。雖然每一個資源在一段時間內只能有一個 SIX 鎖,以防止其它事務對資源進行更新,可是其它事務能夠經過獲取表級的 IS 鎖來讀取層次結構中的底層資源
過後處理
--(數據版本/時間戳)事前處理
--(使用數據庫的鎖機制)鎖粒度是被封鎖目標的大小,封鎖粒度小則併發性高,但開銷大,封鎖粒度大則併發性低但開銷小,SQL Server支持的鎖粒度能夠分爲爲行、頁、鍵、鍵範圍、索引、表或數據庫獲取鎖
總結,
粒度鎖:PAGLOCK, TABLOCK, TABLOCKX, ROWLOCK, NOLOCK
模式鎖:HOLDLOCK, UPDLOCK, XLOCK
複製代碼
如何避免死鎖,最小化鎖競爭
使用事務時,儘可能縮短事務的邏輯處理過程,及早提交或回滾事務,事務持有鎖的時間越短,鎖競爭發生的機會就越少;將不是事務所管理的工做單元鎖必需的命令移出事務;
設置死鎖超時參數爲合理範圍,如:3分鐘-10分種;超過期間,自動放棄本次操做,避免進程懸掛;
優化程序,檢查並避免死鎖現象出現;
通常不要修改SQL SERVER事務的默認級別。不推薦強行加鎖
將組成事務的語句做爲一個的單獨的批命令處理,以消除 BEGIN TRAN 和 COMMIT TRAN 語句之間的網絡延遲形成的沒必要要的延遲。
考慮徹底地使用存儲過程編寫事務代碼。典型地,存儲過程比批命令運行更快。
在遊標中儘可早地Commit更新。由於遊標處理比面向集合的處理慢得多,所以致使鎖被持有的時間更久。
使用每一個進程所需的最低級別的鎖隔離。好比說,若是髒讀是可接受的而且不要求結果必須精確,那麼能夠考慮使用事務隔離級別0(Read Uncommitted),僅在絕對必要時才使用Repeatable Read or Serializable隔離級別。
在 BEGIN TRAN 和 COMMIT TRAN 語句之間,毫不容許用戶交互,由於這樣作可能鎖被持有無限期的時間。
新建一個數據庫dblock,並執行一下SQL
DROP TABLE dbo.LockTest
CREATE TABLE LockTest(ID INT IDENTITY,NAME CHAR(4000) DEFAULT 'name')
--插入6條數據,恰好3個數據頁
--4000字節 兩條數據就是一個數據頁
INSERT INTO dbo.LockTest DEFAULT VALUES
INSERT INTO dbo.LockTest DEFAULT VALUES
INSERT INTO dbo.LockTest DEFAULT VALUES
INSERT INTO dbo.LockTest DEFAULT VALUES
INSERT INTO dbo.LockTest DEFAULT VALUES
INSERT INTO dbo.LockTest DEFAULT VALUES
複製代碼
分如下多種狀況進行,看看鎖是如何工做
這個工具它對SQL Server的監視能力能夠說是無所不能,以下圖咱們新建事件勾選兩項,分別是鎖的獲取和釋放:
LockTest
中有三個數據頁,後面還會繼續在監視中看到瞭解基本的加鎖狀況
經過查詢和更新/刪除,查看Profiler中的顯示狀況
SELECT * FROM LockTest
複製代碼
產生了意向鎖(IS) , 鎖的粒度分別是 TABLE,PAGE 這裏就已經能夠看出來是3個數據頁了。先是Object 鎖 而後逐頁添加 Page 鎖 掃描一數據頁 釋放一個 IS鎖
索引解決剛剛出現的查詢阻塞
UPDATE SET NAME = '3' where ID = '1'
複製代碼
先是Object IX 鎖 page的 IU鎖 rid u鎖 掃描到指定須要更改的 page IX rid X 最後掃描完 再釋放 rid x page IX object IX。這條是須要注意鎖釋放的時間
經過事務演示阻塞問題
-- 會話1
BEGIN TRAN
UPDATE LockTest SET NAME='4' WHERE ID = '1'
ROLLBACK TRAN
-- 會話2
SELECT * FROM LockTest WHERE ID=2
複製代碼
如圖所示,顯示會在78頁獲取IS鎖時候發生等待釋放X鎖而發生阻塞
SELECT l.request_session_id,
DB_NAME(l.resource_database_id),OBJECT_NAME(p.object_id),
l.resource_description,l.request_type,
l.request_status,request_mode
FROM sys.dm_tran_locks AS l
LEFT JOIN sys.partitions AS p
ON l.resource_associated_entity_id=p.hobt_id
複製代碼
以上SQL 能夠顯示出 發生阻塞再等待
利用索引跳過阻塞
-- 建立索引
CREATE INDEX idx_LockTest ON dbo.LockTest(ID)
-- 顯示索引
SELECT * FROM LockTest WITH(INDEX(idx_LockTest)) WHERE ID=2
複製代碼
能夠看出在執行ROLLBACK TRAN
前,未釋放X鎖的前提下SELECT
經過索引直接查出告終果
使用主鍵同樣能夠實現以上
數據庫在建立主鍵同時,會自動創建一個惟一索引。若是這個表以前沒有彙集索引,同時創建主鍵時候沒有強制指定使用非彙集索引,則創建主鍵時候,同時創建一個惟一的彙集索引
複製代碼
經過SQL Server Profiler能夠對以上粒度鎖進行一一查看,更加深刻的瞭解每種粒度
在使用SQL 語句的時候,常常會遇到在一個事務的中,解決不可重複讀或者是丟失更新的問題
通常解決辦法就是使用鎖和事物的聯合機制:
BEGIN TRAN
SELECT * FROM Table WITH(UPDLOCK)
--或者 SELECT * FROM Table WITH(TABLOCKX, READPAST) 具體狀況而定。
UPDATE ....
COMMIT TRAN
複製代碼
全部Select加 With (NoLock)解決阻塞死鎖,在查詢語句中使用 NOLOCK 和 READPAST 處理一個數據庫死鎖的異常時候,其中一個建議就是使用 NOLOCK 或者 READPAST 。有關 NOLOCK 和 READPAST的一些技術知識點: 對於非銀行等嚴格要求事務的行業,搜索記錄中出現或者不出現某條記錄,都是在可容忍範圍內,因此碰到死鎖,應該首先考慮,咱們業務邏輯是否能容忍出現或者不出現某些記錄,而不是尋求對雙方都加鎖條件下如何解鎖的問題。 NOLOCK 和 READPAST 都是處理查詢、插入、刪除等操做時候,如何應對鎖住的數據記錄。可是這時候必定要注意NOLOCK 和 READPAST的侷限性,確認你的業務邏輯能夠容忍這些記錄的出現或者不出現: 簡單來講:
1.NOLOCK 可能把沒有提交事務的數據也顯示出來 2.READPAST 會把被鎖住的行不顯示出來
不使用 NOLOCK 和 READPAST ,在 Select 操做時候則有可能報錯誤:事務(進程 ID **)與另外一個進程被死鎖在 鎖 資源上,而且已被選做死鎖犧牲品。
SELECT * FROM Table WITH(NOLOCK) SELECT * FROM Table WITH(READPAST)
以下圖能夠看到發生死鎖,咱們能夠經過上面所說的 設置優先級別和超時來控制發生鎖競爭的結果,分別會顯示「而且已被選做死鎖犧牲品」,「已超過了鎖請求超時時段」
"表鎖"鎖定對該表的Select、Update、Delete操做,但不影響對該表的Insert操做也不影響以主鍵Id爲條件的Select,因此Select若是不想等待就要在Select後加With(Nolock),但這樣會產生髒數據就是其餘事務已更新但並無提交的數據,若是該事務進行了RollBack則取出的數據就是錯誤的,因此好本身權衡利弊,通常狀況下90%以上的Select都容許髒讀,只有帳戶金額相關的不容許。
"表鎖"鎖定對該表的Select、Update、Delete操做,但不影響對該表的Insert操做也不影響以主鍵Id爲條件的Select
"行鎖+表鎖"鎖定對該表的Select、Update、Delete操做,但不影響對該表的Insert操做也不影響以主鍵Id爲條件的Select、Update、Delete,也不影響以索引列Name爲條件的Update、Delete但不能夠Select
"行鎖+表鎖"鎖定對該表的Select、Update、Delete操做,但不影響對該表的Insert操做也不影響以主鍵Id爲條件的Select、Update、Delete
經過版本號或者時間戳,或者使用樂觀鎖的兩種快照事務隔離級別(READ COMMITTED SNAPSHOT/SNAPSHOT)