閱讀目錄html
之前老是追求新東西,發現基礎纔是最重要的,今年主要的目標是精通SQL查詢和SQL性能優化。sql
本系列主要是針對T-SQL的總結。數據庫
【T-SQL基礎】02.聯接查詢性能優化
【T-SQL基礎】04.表表達式-上篇session
【T-SQL基礎】06.透視、逆透視、分組集性能
【T-SQL基礎】07.數據修改
【T-SQL基礎】09.可編程對象
----------------------------------------------------------
【T-SQL進階】01.好用的SQL TVP~~獨家贈送[增-刪-改-查]的例子
----------------------------------------------------------
【T-SQL性能調優】02.Transaction Log的使用和性能問題
【T-SQL性能調優】03.執行計劃
【T-SQL性能調優】04.死鎖分析
持續更新......歡迎關注我!
本篇主要是對SQL中事務和併發的詳細講解。
爲單個工做單元而執行的一系列操做。如查詢、修改數據、修改數據定義。
(1)顯示定義事務的開始、提交
1 2 3 4 |
BEGIN TRAN INSERT INTO b(t1) VALUES(1) INSERT INTO b(t1) VALUES(2) COMMIT TRAN |
(2)隱式定義
若是不顯示定義事務的邊界,則SQL Server會默認把每一個單獨的語句做爲一個事務,即在執行完每一個語句以後就會自動提交事務。
(1)原子性Atomicity
1.事務必須是原子工做單元。事務中進行的修改,要麼所有執行,要麼全都不執行;
2.在事務完成以前(提交指令被記錄到事務日誌以前),系統出現故障或從新啓動,SQL Server將會撤銷在事務中進行的全部修改;
3.事務在處理中遇到錯誤,SQL Server一般會自動回滾事務;
4.少數不太嚴重的錯誤不會引起事務的自動回滾,如主鍵衝突、鎖超時等;
5.可使用錯誤處理來捕獲第4點提到的錯誤,並採起某種操做,如把錯誤記錄在日誌中,再回滾事務;
6.SELECT @@TRANCOUNT可用在代碼的任何位置來判斷當前使用SELECT @@TRANCOUNT的地方是否位於一個打開的事務當中,若是不在任何打開的事務範圍內,則該函數返回0;若是在某個打開的事務返回範圍內,則返回一個大於0的值。打開一個事務,@@TRANCOUNT=@@TRANCOUNT+1;提交一個事務,@@TRANCOUNT-1。
(2)一致性Consiitency
1.同時發生的事務在修改和查詢數據時不發生衝突;
2.一致性取決於應用程序的須要。後面會講到一致性級別,以及如何對一致性進行控制。
(3)隔離性Isolation
1.用於控制數據訪問,確保事務只訪問處於指望的一致性級別下的數據;
2.使用鎖對各個事務之間正在修改和查詢的數據進行隔離。
(4)持久性Durability
1.在將數據修改寫入到磁盤上數據庫的數據分區以前會把這些修改寫入到磁盤上數據庫的事務日誌中,把提交指令記錄到磁盤的事務日誌中之後,及時數據修改尚未應用到磁盤的數據分區,也能夠認爲事務時持久化的。
2.系統從新啓動(正常啓動或在發生系統故障以後啓動),SQL Server會每一個數據庫的事務日誌,進行回覆處理。
3.恢復處理包含兩個階段:重作階段和撤銷階段。
4.前滾:在重作階段,對於提交指令已經寫入到日誌的事務,但數據修改尚未應用到數據分區的事務,數據庫引擎會重作這些食物所作的全部修改。
5.回滾:在撤銷階段,對於提交指令沒有寫入到日誌中的事務,數據庫引擎會撤銷這些事務所作的修改。(這句話須要research,多是不正確的。由於提交指令沒有寫入到數據分區,撤銷修改是指撤銷哪些修改呢???)
(1)SQL Server使用鎖來實現事務的隔離。
(2)事務獲取鎖這種控制資源,用於保護數據資源,防止其餘事務對數據進行衝突的或不兼容的訪問。
(1)排他鎖
a.當試圖修改數據時,事務只能爲所依賴的數據資源請求排他鎖。
b.持有排他鎖時間:一旦某個事務獲得了排他鎖,則這個事務將一直持有排他鎖直到事務完成。
c.排他鎖和其餘任何類型的鎖在多事務中不能在同一階段做用於同一個資源。
如:當前事務得到了某個資源的排他鎖,則其餘事務不能得到該資源的任何其餘類型的鎖。其餘事務得到了某個資源的任何其餘類型的鎖,則當前事務不能得到該資源的排他鎖。
(2)共享鎖
a.當試圖讀取數據時,事務默認會爲所依賴的數據資源請求共享鎖。
b.持有共享鎖時間:從事務獲得共享鎖到讀操做完成。
c.多個事務能夠在同一階段用共享鎖做用於同一數據資源。
d.在讀取數據時,能夠對如何處理鎖定進行控制。後面隔離級別會講到如何對鎖定進行控制。
(1)若是數據正在由一個事務進行修改,則其餘事務既不能修改該數據,也不能讀取(至少默認不能)該數據,直到第一個事務完成。
(2)若是數據正在由一個事務讀取,則其餘事務不能修改該數據(至少默認不能)。
RID、KEY(行)、PAGE(頁)、對象(例如表)、數據庫、EXTENT(區)、分配單元(ALLOCATION_UNIT)、堆(HEAP)、以及B樹(B-tree)。
RID: 標識頁上的特定行
格式: fileid: pagenumber: rid (1:109:0 )
其中fileid標識包含頁的文件, pagenumber標識包含行的頁,rid標識頁上的特定行。
fileid與sys.databases_files 目錄視圖中的file_id列相匹配
例子:
在查詢視圖sys.dm_tran_locks的時候有一行的resource_description列顯示RID 是1:109:0 而status列顯示wait,
表示第1個數據文件上的第109頁上的第0行上的鎖資源。
SQL Server能夠先得到細粒度的鎖(例如行或頁),在某些狀況下將細粒度鎖升級爲更粗粒度的鎖(例如,表)。
例如單個語句得到至少5000個鎖,就會觸發鎖升級,若是因爲鎖衝突而致使沒法升級鎖,則SQL Server每當獲取1250個新鎖時出發鎖升級。
當多個事務都須要對某一資源進行鎖定時,默認狀況下會發生阻塞。被阻塞的請求會一直等待,直到原來的事務釋放相關的鎖。鎖定超時期限能夠限制,這樣就能夠限制被阻塞的請求在超時以前要等待的時間。
階段1:事務A請求資源S1,事務不對資源S1進行操做
階段2:事務A用鎖A鎖定資源S1,事務B請求對資源S1進行不兼容的鎖定(鎖B),鎖B的請求被阻塞,事務B將進入等待狀態
階段3:事務A正在釋放鎖A,事務B等待鎖A釋放,
階段4:事務A的鎖A已釋放,事務B用鎖B鎖定資源S1
例子:
(1)準備工做:
1.準備測試數據
1 2 3 4 5 6 7 8 |
--先建立一張表Product做爲測試。id爲表的主鍵,price爲product的價格 CREATE TABLE [dbo].[myProduct]( [id] [int] NOT NULL, [price] [money] NOT NULL ) ON [PRIMARY] GO --插入一條數據,id=1,price=10 INSERT INTO [TSQLFundamentals2008].[dbo].[myProduct]([id],[price])VALUES(1,10) |
2.模擬阻塞發生的狀況
在SQL Server中打開三個查詢窗口Connection1、Connection2、Connection3,分別按順序執行表格中的執行語句。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
--Connection1 BEGIN TRAN UPDATE dbo.myProduct SET price = price + 1 WHERE id=1
--Connection2 SELECT * FROM dbo.myProduct WHERE id=1
--Connection3 SELECT request_session_id AS 會話id , resource_type AS 請求鎖定的資源類型 , resource_description AS 描述 , request_mode AS 模式 , request_status AS 狀態 FROM sys.dm_tran_locks |
查詢窗口 |
服務器進程標識符SPID |
執行語句 |
結果 |
說明 |
||
Connection1 |
52 |
|
|
爲了更新id=1這一行數據,會話必須先得到一個排他鎖。事務處於一直打開狀態,沒有提交,因此事務一直持有排他鎖,直到事務提交併完成。 |
||
Connection2 |
56 |
|
|
事務爲了讀取數據,須要請求一個共享鎖,可是這一行已經被其餘會話持有的排他鎖鎖定,並且共享鎖和排他鎖不是兼容的,因此會話被阻塞,進入等待狀態 |
||
Connection3 |
57 |
|
|
會話56: |
(2)分析阻塞
★ 1.sys.dm_tran_locks 視圖
(1)該動態視圖能夠查詢出哪些資源被哪一個進程ID鎖了
(2)查詢出對資源授予或正在等待的鎖模式
(3)查詢出被鎖定資源的類型
上面的查詢語句3已經用到了這個視圖,能夠參考上圖中的分析說明。
★ 2.sys.dm_exec_connections 視圖
(1)查詢出該動態視圖能夠查詢出進程相關的信息
(2)查詢出最後一次發生讀操做和寫操做的時間last_read,last_write
(3)查詢出進程執行的最後一個SQL批處理的二進制標記most_recent_sql_handle
查詢窗口 |
服務器進程標識符SPID |
執行語句 |
結果 |
說明 |
||
Connection3 |
57 |
|
|
會話52: |
★ 3.sys.dm_exec_sql_text 表函數
(1)該函數能夠將二進制標記most_recent_sql_handle做爲參數,而後返回SQL代碼。
(2)阻塞進程在不斷地運行,因此在代碼中看到的最後一個操做不必定是致使問題的語句。在本例中最後一條執行語句是致使阻塞的語句。
查詢窗口 |
服務器進程標識符SPID |
執行語句 |
結果 |
說明 |
||||||
Connection3 |
57 |
|
|
會話52:
|
★ 4.sys.dm_exec_sessions 視圖
(1)會話創建的時間login_time
(2)特定於會話的客戶端工做站名稱host_name
(3)初始化會話的客戶端程序的名稱program_name
(4)會話所使用的SQL Server登陸名login_name
(5)最近一次會話請求的開始時間last_request_start_time
(6)最近一次會話請求的完成時間last_request_end_time
查詢窗口 |
服務器進程標識符SPID |
執行語句 |
結果 |
說明 |
||
Connection3 |
57 |
|
|
|
★ 5.sys.dm_exec_requests 視圖
(1)識別出阻塞鏈涉及到的會話、爭用的資源、被阻塞會話等待了多長時間
查詢窗口 |
服務器進程標識符SPID |
執行語句 |
結果 |
說明 |
||
Connection3 |
57 |
|
|
會話56: |
★ 6.Lock_TIMEOUT 選項
(1)設置會話等待鎖釋放的超時期限
(2)默認狀況下會話不會設置等待鎖釋放的超時期限
(3)設置會話超時期限爲5秒, SET Lock_TIMEOUT 5000
(4)鎖定若是超時,不會引起事務回滾
(5)取消會話超時鎖定的設置,SET LOCK_TIMEOUT -1
若是超時,將顯示如下錯誤:
★7.KILL <spid> 命令
(1)殺掉會話52,KILL 52
(2)殺掉會話,會引發事務回滾,同時釋放排他鎖
(1)隔離級別用來作什麼
a.隔離級別用於決定如何控制併發用戶讀寫數據的操做
(2)寫操做
a.任何對錶作出修改的語句
b.使用排他鎖
c.不能修改讀操做得到的鎖和鎖的持續時間
(3)讀操做:
a.任何檢索數據的語句
b.默認使用共享鎖
c.使用隔離級別來控制讀操做的處理方式
(1)未提交讀 (READ UNCOMMITTED)
(2)已提交讀(READ COMMITTED)(默認值)
(3)可重複讀(REPEATABLE READ)
(4)可序列化(SERIALIZABLE)
(5)快照(SNAPSHOT)
(6)已經提交讀快照(READ_COMMITTED_SNAPSHOT)
(1)設置整個會話的隔離級別
1 |
SET TRANSACTION ISOLATION LEVEL <isolation name>; |
1 |
SET TRANSACTION ISOLATION LEVEL READ COMMITTED; |
(2)用表提示設置查詢的隔離級別
1 2 |
SELECT ... FROM <table> WITH (<isolation name>);<br data-filtered="filtered"> SELECT * FROM dbo.myProduct WITH (READCOMMITTED); |
注意:
1.設置會話選項的隔離級別時,隔離級別中的每一個單詞之間須要用空格分隔
2.用表提示的隔離級別時,隔離級別中的每一個單詞之間不須要用空格分隔
3.表提示的隔離級別有同義詞,如:NOLOCK->READUNCOMMITTED,HOLDLOCK->REPEATABLEREAD
4.隔離級別的嚴格性:1.未提交讀<2.已提交讀<3.可重複讀<4.可序列化
5.隔離級別越高,一致性越高,併發性越低
6.基於快照的隔離級別,SQL Server將提交過的行保存到tempdb數據庫中,當讀操做發現行的當前版本和它們預期的不一致時,能夠當即獲得行的之前版本,從而不用請求共享鎖也能取得預期的一致性。
★ 1.未提交讀 (READ UNCOMMITTED)
打開兩個查詢窗口,Connetion1,connection2
Step1: 執行Connection1的階段2的SQL 語句,而後執行connection2的SQL語句
Step2: 執行Connection1的階段3的SQL 語句,執行connection2的SQL語句
Step3: 執行Connection1的階段4的SQL 語句,執行connection2的SQL語句
查詢窗口 |
事務 |
執行語句 |
||
Connetion1 |
A |
|
||
Connection2 |
B |
|
兩個事務的流程圖:
階段1:Price=10,事務A對myProduct表請求排他鎖
階段2:事務A對myProduct表使用了排他鎖,更新price = price + 1,而後事務A查詢price的價格: price=11。事務B不請求任何鎖,事務B在A更新Price以後進行查詢,price=11
階段3:事務A更新price = price + 5,而後事務A查詢price的價格,price = 16。事務B查詢price的價格: price=16
階段4:事務A釋放排他鎖
階段5:事務A中查詢price的價格:price = 16。事務B查詢price的價格: price=16
你們能夠看到事務B有兩種結果,這就是「未提交讀 (READ UNCOMMITTED)」隔離級別的含義:
(1)讀操做能夠讀取未提交的修改(也稱爲髒讀)。
(2)讀操做不會妨礙寫操做請求排他鎖,其餘事務正在進行讀操做時,寫操做能夠同時對這些數據進行修改。
(3)事務A進行了屢次修改,事務B在不一樣階段進行查詢時可能會有不一樣的結果。
★ 2.已提交讀(READ COMMITTED)(默認值)
打開兩個查詢窗口,Connetion1,connection2
Step1: 執行Connection1的SQL 語句
Step2: 執行Connection2的SQL 語句
執行語句 |
|
執行語句 |
||
Connetion1 |
A |
|
||
Connection2 |
B |
|
兩個事務的流程圖:
階段1:Price=10,事務A對myProduct表請求排他鎖
階段2:事務A對myProduct表使用了排他鎖,更新price = price + 1,而後事務A查詢price的價格: price=11。而後事務B請求共享鎖進行讀操做,查詢price,
因爲在當前隔離級別下,事務A的排他鎖和事務B的共享鎖存在衝突,因此事務B須要等待事務A釋放排他鎖後才能讀取數據。
階段3:事務A提交事務(COMMIT TRAN)
階段4:事務A提交完事務後,釋放排他鎖
階段5:事務B得到了共享鎖,進行讀操做,price=11
「已提交讀 (READ UNCOMMITTED)」隔離級別的含義:
(1)必須得到共享鎖才能進行讀操做,其餘事務若是對該資源持有排他鎖,則共享鎖必須等待排他鎖釋放。
(2)讀操做不能讀取未提交的修改,讀操做讀取到的數據是提交過的修改。
(3)讀操做不會在事務持續期間內保留共享鎖,其餘事務能夠在兩個讀操做之間更改數據資源,讀操做於是可能每次獲得不一樣的取值。這種現象稱爲「不可重複讀」
★ 3.可重複讀(REPEATABLE READ)
打開兩個查詢窗口,Connetion1,connection2
Step1: 執行Connection1的SQL 語句
Step2: 執行Connection2的SQL 語句
執行語句 |
事務 |
執行語句 |
||
Connetion1 |
A |
|
||
Connection2 |
B |
|
兩個事務的流程圖:
階段1:Price=10,事務A對myProduct表請求共享鎖
階段2:事務A對myProduct表使用了共享鎖,事務A查詢price的價格: price=10,事務A一直持有共享鎖直到事務A完成爲止。而後事務B請求排他鎖進行寫操做price=price+1,
因爲在當前隔離級別下,事務A的共享鎖和事務B請求的排他鎖存在衝突,因此事務B須要等待事務A釋放共享鎖後才能修改數據。
階段3:事務A查詢price, price=10, 說明事務B的更新操做被阻塞了,更新操做沒有被執行。而後事務A提交事務(COMMIT TRAN)
階段4:事務A提交完事務後,釋放共享鎖
階段5:事務B得到了排他鎖,進行寫操做,price=11
「可重複讀 (REPEATABLE READ)」隔離級別的含義:
(1)必須得到共享鎖才能進行讀操做,得到的共享鎖將一直保持直到事務完成之止。
(2)在得到共享鎖的事務完成以前,沒有其餘事務可以得到排他鎖修改這一數據資源,這樣能夠保證明現可重複的讀取。
(3)兩個事務在第一次讀操做以後都將保留它們得到的共享鎖,因此任何一個事務都不能得到爲了更新數據而須要的排他鎖,這種狀況將會致使死鎖(deadlock),不過卻避免了更新衝突。
★ 4.可序列化(SERIALIZABLE)
打開兩個查詢窗口,Connetion1,connection2
Step1: 執行Connection1的SQL 語句
Step2: 執行Connection2的SQL 語句
執行語句 |
事務 |
|
||
Connetion1 |
A |
|
||
Connection2 |
B |
|
兩個事務的流程圖:
階段1:Price=10,事務A對myProduct表請求共享鎖
階段2:事務A對myProduct表使用了共享鎖,事務A查詢id=1的price的價格:1行記錄,price=10,事務A一直持有共享鎖直到事務A完成爲止。而後事務B請求排他鎖進行插入操做id=1,price=20,
因爲在當前隔離級別下,事務B試圖增長可以知足事務A的讀操做的查詢搜索條件的新行,因此事務A的共享鎖和事務B請求的排他鎖存在衝突,事務B須要等待事務A釋放共享鎖後才能插入數據。
階段3:事務A查詢出id=1的數據只有1行,說明事務B的插入操做被阻塞了,插入操做沒有被執行。而後事務A提交事務(COMMIT TRAN)
階段4:事務A提交完事務後,釋放共享鎖
階段5:事務B得到了排他鎖,進行插入操做,插入成功,查詢出id=1的數據有兩條
「可序列化(SERIALIZABLE)」隔離級別的含義:
(1)必須得到共享鎖才能進行讀操做,得到的共享鎖將一直保持直到事務完成之止。
(2)在得到共享鎖的事務完成以前,沒有其餘事務可以得到排他鎖修改這一數據資源,且當其餘事務增長可以知足當前事務的讀操做的查詢搜索條件的新行時,其餘事務將會被阻塞,直到當前事務完成而後釋放共享鎖,其餘事務才能得到排他鎖進行插入操做。
(3)事務中的讀操做在任何狀況下讀取到的數據是一致的,不會出現幻影行。
(4)範圍鎖:讀操做鎖定知足查詢搜索條件範圍的鎖
髒讀:讀取未提交的更改。
不可重複讀:讀操做不會在事務持續期間內保留共享鎖,其餘事務能夠在兩個讀操做之間更改數據資源,讀操做於是可能每次獲得不一樣的取值。
丟失更新:兩個事務進行讀操做,得到資源上的共享鎖,讀取完數據後,再也不持有資源上的任何鎖,兩個事務都能更新這個值,
最後進行更新的事務將會覆蓋其餘事務作的更改,致使其餘事務更改的數據丟失。
幻讀:第一次和第二次讀取到的數據行數不一致。
範圍鎖:讀操做鎖定知足查詢搜索條件範圍的鎖
隔離級別 |
是否讀取未提交的行 |
是否不可重複讀 |
是否丟失更新 |
是否幻讀 |
共享鎖持續時間 |
是否持有範圍鎖 |
未提交讀 READ UNCOMMITTED |
Y |
Y |
Y |
Y |
當前語句 |
N |
已提交讀 READ COMMITTED |
N |
Y |
Y |
Y |
當前語句 |
N |
可重複讀REPEATABLE READ |
N |
N |
N |
Y |
事務開始到事務完成 |
N |
可序列化SERIALZABLE |
N |
N |
N |
N |
事務開始到事務完成 |
Y |
死鎖是指一種進程之間互相永久阻塞的狀態,可能涉及兩個或更多的進程。
打開兩個查詢窗口,Connetion1,connection2
Step1: 執行Connection1的SQL 語句
Step2: 執行Connection2的SQL 語句
執行語句 |
事務 |
執行語句 |
||
Connetion1 |
A |
|
||
Connection2 |
B |
|
兩個事務的流程圖:
階段1:Price=10,事務A對myProduct表請求排他鎖。Customer = aaa,事務B對myOrder請求排他鎖
階段2:事務A對myProduct表使用了排他鎖,更新price = price + 1。而後事務B對myOrder表使用了排他鎖,更新customer=ddd。
階段3:事務A查詢myOrder表,對myOrder表請求共享鎖,由於事務A的請求的共享鎖與事務B的排他鎖衝突,因此事務A被阻塞。而後事務B查詢myProduct表,對myProduct表請求共享鎖,由於事務B的請求的共享鎖與事務A的排他鎖衝突,因此事務B被阻塞。
階段4:事務A等待事務B的排他鎖釋放,事務B等待事務A的排他鎖釋放,致使死鎖。事務A和事務B都被阻塞了。
階段5:SQL Server在幾秒以內檢測到死鎖,會選擇一個事務做爲死鎖的犧牲品,終止這個事務,並回滾這個事務所作的操做。在這個例子中,事務A被終止,提示信息:事務(進程 ID 53)與另外一個進程被死鎖在 鎖 資源上,而且已被選做死鎖犧牲品。請從新運行該事務。
「死鎖 (Dead Lock)」的一些注意事項:
(1)若是兩個事務沒有設置死鎖優先級,且兩個事務進行的工做量也差很少同樣時,任何一個事務都有可能被終止。
(2)解除死鎖要付出必定的系統開銷,由於這個過程會涉及撤銷已經執行過的處理。
(3)事務處理的時間時間越長,持有鎖的時間就越長,死鎖的可能性也就越大,應該儘量保持事務簡短,把邏輯上能夠不屬於同一個工做單元的操做移到事務之外。
(4)上面的例子中,事務A和事務B以相反順序訪問資源,因此發生了死鎖。若是兩個事務按一樣的順序來訪問資源,則不會發生這種類型的死鎖。在不改變程序的邏輯狀況下,能夠經過交換順序來解決死鎖的問題。
關於分析死鎖的問題,能夠參考前面寫的關於阻塞的內容。