- 每個事務都是記錄在事務日志中,數據修改首先寫到事務日志中,然后在寫到數據庫中,如果事務的任何部分失敗,修改全部回滾,數據庫保持在原來的狀態,事務使用鎖阻止其他用戶讀取或者修改還沒有提交的數據
- SQL Server默認是自動提交,即每個T-SQL語句執行成功就提交,執行失敗就回滾,除非顯式開啟事務;默認是只回滾產生錯誤的語句,如果XACT_ABORT設置為ON的話,則出現錯誤時回滾整個批處理
- 使用WITH MARK選項可以為事務指定一個描述,描述被標記在事務日志中,這個事務日志標記用於restore數據庫,表明想要restore的點;SAVE TRANSACTION語句創建一個save point,這樣回滾數據庫時,就可以回滾到這個點上,而不是事務最開始處
- 所有T-SQL語句被執行時,它都要被記錄到事務日志中,SQL Server每隔一段時間執行一次checkpoints,checkpoints被標記到數據庫中,當產生一個新的checkpoint時,從上一個checkpoint開始的所有內存中被修改的數據頁都被寫到數據庫中;如果在事務中發生錯誤,SQL Server使用日志中的信息回滾事務,這樣的回滾不會影響其他事務;有些錯誤自動回滾事務,比如死鎖、網絡中斷;每次SQL Server啟動的時候都自動運行恢復進程,自動恢復進程使用事務日志前滾已經提交的任何事務,回滾任何未提交的事務,日志使用最近一次checkpoint作為開始標記點,這個點之前提交的事務都被寫到數據庫中,這個點之前開始但仍然active的需要回滾
- 使用事務時需要考慮的因素:
- 事務盡可能短,長事務增加了用戶不能訪問被鎖數據的可能性
- 不能在事務中要求用戶輸入,一條黃金法則就是不能在用戶交互中保持事務
- 盡可能不要在瀏覽數據時打開事務,事務應該在所有需要數據都准備好以后在開啟
- 事務應該盡可能影響最少的行,一個事務不應該比一個邏輯工作單元小
- 事務中應該盡可能訪問最少行,減少被鎖的行
- 保證適當的索引,因為這減少需要訪問和被鎖的數據頁
- 試圖使用同樣的順序訪問資源,幫助避免死鎖,這也不總是可以的
- 考慮嵌套事務
- 使用嵌套事務時要非常小心
- 使用@@trancount查看事務嵌套層次
- 語法上支持事務嵌套,但不是真正支持,回滾嵌套事務將回滾真個事務,而不僅僅只回滾當前事務
- 並發控制就是管理多個用戶同時訪問相同的數據,以便於他們之間不會相互影響,通過指定隔離級別來指定並發控制的類型,共有兩種並發控制的類型:
- 保守式並發控制:為了修改而讀取數據時,對數據上鎖,只有釋放鎖以后,其他用戶才可以訪問數據,用於高數據競爭的情形
- 開放式並發控制:起初不對數據加鎖,當執行更新時對數據加鎖,如果數據和最初讀取時發生了修改,就返回錯誤,用於低數據競爭的情形
- 鎖就是同步多個用戶同時對同樣的數據訪問的機制,主要有兩種類型的鎖:
- Read Locks:允許其他用戶讀取數據,但是不允許寫
- Write Locks:禁止其他用戶讀寫數據
- 事務賦予的鎖不能與數據上已經存在的鎖沖突,否則事務請求會被掛起知道先前的鎖被釋放,鎖持續的時間取決於鎖的隔離級別設置
- 鎖阻止更新沖突:
- 保證事務是順序執行的
- 鎖是原子的
- 保證數據使用的一致性
- 如果沒有鎖,會發生如下問題:
- 更新丟失,兩個事務同時更新同一行,后一個事務更新覆蓋前一個事務更新
- 臟讀取,讀取了其他事務未提交的數據,如果其他事務回滾的話,就讀取了臟數據
- 不可重復讀,兩次檢索同一行,每次讀取數據都不相同,當前事務第一次讀取數據后,另外一個事務修改了數據,並提交,當前事務再次讀取時,讀取到的數據就和第一次不同(這也就是不可重復讀的意義,正常應該讀取的是重復值才對)
- 幻讀,事務進行兩次查詢,第二次查詢包含了第一次查詢沒有的數據或者缺少了第一次查詢的數據,因為另外一個事務在這個事務讀取的過程中插入或者刪除了數據
- 由於行更新,丟失讀取或者重復讀取
- 可以鎖定的資源:
- RID,行標識符,用來鎖定堆上的一行
- KEY,鎖定索引里的一行,用來保護一個key的范圍被一致性訪問
- PAGE,數據庫中8KB的數據頁
- EXTENT,8個連續的數據頁組成的一個組
- HoBT,A heap or B-tree,用來保護一個B-tree或者表上沒有聚集索引的堆
- TABLE,整個數據表,包括所有數據和索引
- FILE,一個數據庫文件
- APPLICATION,一個應用程序指定的資源
- METADATA
- ALLOCATION_UNIT,一組數據頁集合,數量可少可多
- DATABASE,整個數據庫
- SQL Server使用不同的鎖模式決定資源如何被重入事務訪問,分為兩類主要類型:
- 基本鎖,通常來說,讀操作獲取共享鎖,寫操作獲取獨占鎖
- Shared locks,共享鎖,用於只讀數據的操作,在一個資源上應用共享鎖后,第二個事務還可以獲取一個共享鎖,數據不可以被修改,SQL Server在讀取下一條記錄時釋放本條記錄共享鎖,在滿足查詢條件的所有行返回客戶端時釋放共享鎖
- Exclusive locks,獨占鎖,用於增、刪、改操作,在一個資源上只有一個事務可以獲取一個獨占鎖,一個事務不能在已經擁有獨占鎖的資源上獲得獨占鎖,直到該獨占鎖被釋放才可以獲得
- 特殊用途的鎖
- Intent locks,用於最小化鎖沖突,用於建立鎖層級,使得其他事務只能獲得更加嚴格的鎖,比如一個事務獲取了行級別的獨占鎖,其他事務就不能獲取表級別的獨占鎖,包括intent share、intent exclusive、shared with intent exclusive
- Update locks,在將要修改數據頁時使用更新鎖,在更新數據頁前提升為獨占鎖,在數據頁第一次被讀取更新操作最初始部分獲取更新鎖,和共享鎖兼容(是不是可以理解為開始時是個共享鎖,提交數據時提升為獨占鎖)
- Schema locks,用來保證表或者索引不被刪除或者不會引用正在修改的架構,包括兩種類型:Schema stability,保證資源不被刪除,Schema modification,保證其他會話不會引用正在修改的資源
- Bulk update locks,用來在批量拷貝數據時,保證其他進程不會訪問同樣的數據,在以下情況下使用批量更新鎖:TABLOCK hint(比如INSERT INTO Production.Location WITH (BULOCK)...)或者批量加載選項
- 一些鎖和其他鎖兼容,一些鎖則不兼容,共享鎖和獨占鎖不兼容,更新鎖除了共享鎖以外都不兼容,獨占鎖和其他鎖都不兼容
- SET LOCK_TIMEOUT指定等待鎖釋放的毫秒數,默認是-1,表示永遠等待,超時后返回錯誤並回滾所有語句;READPAST告訴SQL Server忽略因為被鎖住無法讀取的行,很少使用
- Lock escalation,鎖升級,就是將許多細粒度鎖轉化為更少粗粒度鎖的過程,降低系統開銷,增加了並發爭用的可能性;經常需要處理大量的數據行,這樣就需要大量的鎖,獲取和釋放大量的鎖對處理性能和內存使用有重要的影響,因此SQL Server會將行級別的鎖提升為表級別的鎖,對於分區表來說,會提升為分區級別的鎖
- SQL Server處理死鎖的過程:
- 回滾發生死鎖victim的事務,在一個死鎖中,SQL Server給予處理時間長的事務高優先級,回滾執行時間短的事務(即死鎖victim)
- 通過消息編號1205通知死鎖victim應用程序
- 取消死鎖victim的當前請求
- 允許其他事務繼續
- 鎖相關的Table Hints,盡量不要使用這些Table Hints,如果使用的話,要非常小心:
- HOLDLOCK,持有共享鎖,直到事務結束
- NOLOCK,不使用鎖
- PAGLOCK,使用頁級別鎖,而不是使用行級別鎖
- ROWLOCK,使用行級別鎖,而不是頁級別或者表級別鎖
- TABLOCK,使用表級別鎖
- TABLOCKX,在整個表使用一個獨占鎖
- UPDLOCK,使用一個更新鎖,直到事務結束
- XLOCK,使用獨占鎖,直到事務結束
- 查看死鎖的方式:
- DMV,sys.dm_tran_locks檢索數據庫引擎當前持有的鎖信息,每行返回當前授予的鎖和當前請求的鎖,sys.dm_tran_active_transactions,sys.dm_tran_session_transactions,sys.dm_tran_current_transaction
- SQL Server Profiler
- Reliability and Performance Monitor
- SSMS
- 隔離級別保護事務免受其他重入的事務影響,當你設置隔離級別時,你為當前會話所有語句設置了默認鎖定行為;隔離級別越高,數據完整性問題越低,但是鎖相應的被持有的時間越長;這里的快照隔離指的是在事務開始那一刻對數據庫進行快照(這些快照被保存在tempdb中),不會在快照的基礎數據行或者數據頁上獲取鎖,這樣其他事務就可以執行更新操作,這些執行更新操作的事務也不會影響快照事務,但是當快照事務提交數據時,如果數據較讀取之前已經發生了更改,將進行回滾並引發錯誤,在使用快照隔離前,必須設置ALLOW_SNAPSHOT_ISOLATION ON來啟用快照隔離
隔離級別 |
臟讀取 |
非重復讀取 |
幻讀 |
Read uncommitted |
Yes |
Yes |
Yes |
Read committed(默認) |
No |
Yes |
Yes |
Repeatable read |
No |
No |
Yes |
Snapshot |
No |
No |
No |
Serializable |
No |
No |
No |
- Read Committed Snapshot是一個數據庫選項,不需要修改應用程序,當開啟這個選項時,即使沒有事務使用快照隔離級別時,也會導致DML語句開始生成row versions,指定了Read committed的事務自動使用Read Committed Snapshot,即自動使用row versions,而不是鎖,所有語句看到的都是語句開始時的數據快照,通過READ_COMMITTED_SNAPSHOT開關設置該選項,這個選項和快照隔離級別是不一樣的,使用這個選項時快照只存在於每個語句執行期間,而快照隔離界別則是在整個事務執行期間
- 隔離相關的Table Hints,READCOMMITTED、READUNCOMMITTED、REPEATABLEREAD、SERIALIZABLE對象相應的隔離級別,READCOMMITTEDLOCK,指定讀取操作都采用READCOMMITTED隔離級別,不管Read Committed Snapshot的設置,這些Table Hints同樣不建議使用