1.數據庫SQL語句執行原理
1.1.當客戶端執行一條T-SQL語句給SQL Server服務器時,會首先到達服務器的網絡接口,網絡接口和客戶端之間有協議層。
1.2.客戶端和網絡接口之間建立連接。使用稱爲“表格格式數據流”(TDS) 數據包的Microsoft 通信格式來格式化通信數據。
1.3.客戶端發送TDS包給協議層。協議層接收到TDS包後,解壓並分析包裏面包含了什麼請求。
1.4.命令解析器解析T-SQL語句。命令解析器會做下面幾件事情:
A.檢查語法。發現有語法錯誤就返回給客戶端。下面的步驟不執行。
B.檢查緩衝池(Buffer Pool)中是否存在一個對應該T-SQL語句的執行計劃緩存。
C.如果找到已緩存的執行計劃,就從執行計劃緩存中直接讀取,並傳輸給查詢執行器執行。
D.如果未找到執行計劃緩存,則在查詢執行器中進行優化併產生執行計劃,存放到Buffer Pool中。
1.5.查詢優化器優化SQL語句
當Buffer Pool中沒有該SQL語句的執行計劃時,就需要將SQL傳到查詢優化器,通過一定的算法,分析SQL語句,產生一個或多個候選執行計劃選出開
銷最小的計劃作爲最終執行計劃。然後將執行計劃傳給查詢執行器。
1.6.查詢執行器執行查詢(查詢執行器把執行計劃通過OLE DB接口傳給存儲引擎的數據訪問方法。
1.7.數據訪問方法生成執行代碼(數據訪問方法將執行計劃生成SQL Server可操作數據的代碼,不會實際執行這些代碼,傳送給緩衝區管理器來執行。)
1.8.緩衝區管理器讀取數據。
先在緩衝池的數據緩存中檢查是否存在這些數據,如果存在,就把結果返回給存儲引擎的數據訪問方法;如果不存在,則從磁盤(數據文件)中讀出數據並
放入數據緩存中,然後將讀出的數據返回給存儲引擎的數據訪問方法。
1.9.對於讀取數據,將會申請共享鎖,事務管理器分配共享鎖給讀操作。
1.10.存儲引擎的數據訪問方法將查詢到的結果返回關係引擎的查詢執行器。
1.11.查詢執行器將結果返回給協議層。
1.12.協議層將數據封裝成TDS包,然後協議層將TDS包傳給客戶端。
2.事務
是併發控制的單位,是用戶定義的一個操作序列。這些操作要麼都做,要麼都不做,是一個不可分割的工作單位。通過事務,SQLServer能將邏輯相關的一組操作綁定在一起,以便服務器保持數據的完整性。
2.1 事務4大屬性: A:原子性(Atomicity)
事務是數據庫的邏輯工作單位,事務中包括的所有操作要麼全做,要麼全不做。
事務在執行過程中發生錯誤,會被回滾(Rollback)到事務開始前的狀態,就像這個事務從來沒有執行過一樣。
B:一致性(Consistency)
事務執行的結果必須是使數據庫從一個一致性狀態變到另一個一致性狀態。一致性與原子性是密切相關的。一個事務可以封裝狀態改變(除非它是一個只讀的)。事務必須始終保持系統處於一致的狀態,不管在任何給定的時間併發事務有多少。也就是說:如果事務是併發多個,系統也必須如同串行事務一樣操作。其主要特徵是保護性和不變性(Preserving an Invariant),以保證數據的一致.
以轉賬案例爲例,假設有五個賬戶,每個賬戶餘額是100元,那麼五個賬戶總額是500元,如這個5個賬戶之間同時發生多個轉賬,無論併發多少個,比如在A與B賬戶之間轉賬5元,在C與D賬戶之間轉賬10元,在B與E之間轉賬15元,五個賬戶總額也應該還是500元,這就是保護數據的一致性。
C:隔離性(Isolation)
一個事務的執行不能被其他事務干擾。隔離狀態執行事務,使它們好像是系統在給定時間內執行的唯一操作。如果有兩個事務,運行在相同的時間內,執行相同的功能,事務的隔離性將確保每一事務在系統中認爲只有該事務在使用系統。這種屬性有時稱爲串行化,爲了防止事務操作間的混淆,必須串行化或序列化請求,使得在同一時間僅有一個請求用於同一數據。
D:持續性/永久性(Durability)
一個事務一旦提交,它對數據庫中數據的改變就應該是永久性的,不會被回滾。
2.2 事務分類:
A:顯式事務
用begintransaction明確指定事務的開始。
用Committransaction提交事務
用rollbacktrans回滾事務
另顯示事務中還有一個特殊命令語句
Set xact_abort on/off , 指定是否回滾當前事務,爲on時如果當前sql出錯,回滾整個事務,爲off時如果sql出錯回滾當前sql語句,其它語句照常運行。
B:隱性事務
打開隱性事務:set implicit_transactions on,當以隱性事務模式操作時,SQL Servler將在提交或回滾事務後自動啓動新事務。無法描述事務的開始,只需要提交或回滾事務。
C:自動提交事務
SQL Server的默認模式,它將每條單獨的T-SQL語句視爲一個事務。如果成功執行,則自動提交,否則回滾。
2.3事務的隔離級別
A.未提交讀(Read uncommitted):在未提交讀級別,事務中的修改,即使沒有提交,對其他事務也都是可見的。事務可以讀取未提交的數據,這也被稱爲髒讀(Dirty Read)。這個級別會導致很多問題,從性能上來說,未提交讀不會比其他的級別好太多,但是缺乏其他級別的很多好處,在實際應用中一般很少使用。
B.已提交讀(Read committed):大多數數據庫系統的默認隔離級別都是提交讀(mySQL不是)。提交讀滿足前面提到的隔離性的簡單定義:一個事務開始時,只能“看見”已經提交的事務所做的修改。換句話說,一個事務從開始直到提交之前,所做的任何修改對其他事務都是不可見的。這個級別有時候也叫做不可重複讀(nonrepeatable read),因爲兩次執行同樣的查詢,可能會得到不一樣的結果。
C.可重複讀(Repeatable read):可重複讀解決了髒讀的問題。該級別保證了在同一個事務中多次讀取同樣記錄的結果是一致的。但是理論上,可重複讀隔離級別還是無法解決另外一個幻讀(Phantom read)問題。所謂幻讀,指的是當某個事務在讀取某個範圍內的記錄時,另外一個事務中又在該範圍插入了新的記錄,當之前的事務再次讀取該範圍的記錄時,會產生幻行(Phantom row)即沒有修改成功的數據。可重複讀是MySQL的默認事務隔離級別。
D.可序列化(Serializable):可序列化是最高的隔離級別。它通過強制事務串行執行,避免了前面所說的幻讀問題。簡單來說,可串行化會在讀取的每一行數據上都加上鎖,所以可能導致大量的超時和鎖爭用問題。實際應用中也很少用到這個隔離級別,只有在非常需要確保數據的一致性的情況下,才考慮用該級別。
E.快照(SanpShot):SanpShot和Read
Committed
SanpShot兩種隔離(可以把事務已經提交的行的上一版本保存在TEMPDB數據庫中)SanpShot隔離級別在邏輯上與Serializable類似
Read
Committed SanpShot隔離級別在邏輯上與
Read
Committed類似
不過在快照隔離級別下讀操作不需要申請獲得共享鎖,所以即便是數據已經存在排他鎖也不影響讀操作。而且仍然可以得到和Serializable與Read
Committed隔離級別類似的一致性;如果目前版本與預期的版本不一致,讀操作可以從TEMPDB中獲取預期的版本。
如果啓用任何一種基於快照的隔離級別,DELETE和UPDATE語句在做出修改前都會把行的當前版本複製到TEMPDB中,而INSERT語句不需要在
TEMPDB中進行版本控制,因爲此時還沒有行的舊數據,無論啓用哪種基於快照的隔離級別都會對更新和刪除操作產生性能的負面影響,但是有利於
提高讀操作的性能因爲讀操作不需要獲取共享鎖
3.鎖:
3.1爲什麼要引入鎖?
解決多個用戶同時對數據庫的併發操作時會帶來以下數據不一致的問題,所以需要引入鎖。
A:丟失更新
A,B兩個用戶讀同一數據並進行修改,其中一個用戶的修改結果破壞了另一個修改的結果
B:髒讀
A用戶修改了數據,隨後B用戶又讀出該數據,但A用戶因爲某些原因取消了對數據的修改,數據恢復原值,此時B得到的數據就與數據庫內的數據產生了不一致
C:不可重複讀
A用戶讀取數據,隨後B用戶讀出該數據並修改,此時A用戶再讀取數據時發現前後兩次的值不一致
D: 幻象讀(虛瀆)
是指當事務不是獨立執行時發生的一種現象,例如A事務對一個表中的數據進行了批次修改。同時,B事務也修改這個表中的數據,這種修改是向表中插入新的數據。那麼,以後事後A事務的用戶就發現表中還有沒有修改的數據行,就好象發生了幻覺一樣。
3.2 鎖的分類
鎖的類別有兩種分法:
A:從數據庫系統的角度來看
分爲共享鎖,獨佔鎖(即排它鎖),和更新鎖
共享鎖(S鎖): 用於不更改或不更新數據的操作(只讀操作),如 SELECT 語句。
共享 (S) 鎖允許事務併發讀取(SELECT) 一個資源。資源上存在共享 (S) 鎖時,任何其它事務都不能修改數據。在較低的事務隔離級別中一旦已經讀取數據,便立即釋放資源的的共享 (S) 鎖(所以共享鎖的上鎖時間一般來說是很短的)。
更新鎖(U鎖): 用於可更新的操作中。防止當多個會話在讀取、隨後可能進行的資源更新時發生常見形式的死鎖。
更新鎖更新 (U) 鎖可以防止通常形式的死鎖。一般更新模式由一個事務組成,此事務讀取記錄,獲取資源(頁或行)的共享 (S) 鎖,然後修改行,此操作要求鎖轉換爲排它 (X) 鎖。(在可重複讀或可序列化事務中)假如兩個事務分別獲得了資源上的共享模式鎖,然後試圖同時更新數據,則一個事務嘗試將鎖轉換爲排它 (X) 鎖。共享模式到排它鎖的轉換必須等待一段時間,因爲一個事務的排它鎖與其它事務的共享模式鎖不兼容;發生鎖等待。第二個事務試圖獲取排它 (X) 鎖以進行更新。由於兩個事務都要轉換爲排它 (X) 鎖,並且每個事務都等待另一個事務釋放共享模式鎖,因此發生死鎖。若要避免這種潛在的死鎖問題,請使用更新 (U) 鎖。一次只有一個事務可以獲得資源的更新 (U) 鎖。如果事務修改資源,則更新 (U) 鎖轉換爲排它 (X) 鎖。否則,鎖轉換爲共享鎖。
排它鎖(X鎖): 用於數據修改操作,例如 INSERT、UPDATE 或DELETE。確保同一時刻同一資源不會進行多重更新(做以上操作時,數據庫會自動轉換成排他鎖)。
常用鎖的關鍵字:
Nolock:用於select語句,指定不加共享鎖
Holdlock:保持鎖,使鎖在整個事務中都有效.
Rowlock: 使用行級鎖,而不使用粒度更粗的頁級鎖和表級鎖。
SERIALIZABLE 用與運行在可串行讀隔離級別的事務相同的鎖語義執行掃描。等同於 HOLDLOCK。
Tablock: 使用表鎖代替粒度更細的行級鎖或頁級鎖。在語句結束前,SQL Server 一直持有該鎖。但是,如果同時指定 HOLDLOCK,
那麼在事務結束之前,鎖將被一直持有TABLOCKX 使用表的排它鎖。該鎖可以防止其它事務讀取或更新表,並在語句或事務結束前一
直持有。
Updlock: 讀取表時使用更新鎖,而不使用共享鎖,並將鎖一直保留到語句或事務的結束
另外還有意向鎖
意向鎖表示 SQL Server 需要在層次結構中的某些底層資源上獲取共享 (S)
鎖或排它 (X) 鎖。例如,放置在表級的共享意向鎖表示事務打算在表中的頁或行上放置共享 (S)
鎖。在表級設置意向鎖可防止另一個事務隨後在包含那一頁的表上獲取排它 (X) 鎖。意向鎖可以提高性能,因爲 SQL Server
僅在表級檢查意向鎖來確定事務是否可以安全地獲取該表上的鎖。而無須檢查表中的每行或每頁上的鎖以確定事務是否可以鎖定整個表。詳情請參考連接
1.http://www.cnblogs.com/woodytu/p/5500270.html
2.http://www.cnblogs.com/xwdreamer/archive/2012/09/19/2694161.html
3.http://www.jb51.net/softjc/126050.html
B.從程序員的角度看:
分爲樂觀鎖和悲觀鎖。
樂觀鎖:程序員自己管理數據或對象上的鎖處理(例如用字段標識值來判斷)。
悲觀鎖:完全依靠數據庫的鎖來管理鎖的工作。
悲觀鎖及樂觀鎖之間的選擇時,一般我們可以從如下幾個方面來判斷:
1.響應速度:如果需要非常高的響應速度,建議採用樂觀鎖方案,成功就執行,不成功就失敗,不需要等待其他併發去釋放鎖
2.衝突頻率:(衝突是指對同一資源的需求),如果衝突頻率非常高,建議採用悲觀鎖,保證成功率,如果衝突頻率大,樂觀鎖會需要多次重試才能成功,代價比較大
3.重試代價:如果重試代價大,建議採用悲觀鎖
4.事務時間:如果事務所花費的時候是很少的,建議採用悲觀鎖。
3.3鎖的粒度和資源的類型層次結構
Microsoft SQLServer 數據庫引擎具有多粒度鎖定,允許一個事務鎖定不同類型的資源。 爲了儘量減少鎖定的開銷,數據庫引擎自動將資源鎖定在適合任務的級別。 鎖定在較小的粒度(例如行)可以提高併發度,但開銷較高,因爲如果鎖定了許多行,則需要持有更多的鎖。 鎖定在較大的粒度(例如表)會降低了併發度,因爲鎖定整個表限制了其他事務對錶中任意部分的訪問。 但其開銷較低,因爲需要維護的鎖較少。數據庫引擎通常必須獲取多粒度級別上的鎖才能完整地保護資源。 這組多粒度級別上的鎖稱爲鎖層次結構。 例如,爲了完整地保護對索引的讀取,數據庫引擎實例可能必須獲取行上的共享鎖以及頁和表上的意向共享鎖。
RID、KEY(行)、PAGE(頁)、對象(例如表)、數據庫、EXTENT(區)、分配單元(ALLOCATION_UNIT)、堆(HEAP)、以及B樹(B-tree)。
4.死鎖:
死鎖是不同事務之間爲了搶佔數據資源,而自己所需要的資源都被別人佔用,所以造成永久的等待,即夠成死鎖。
4.1死鎖的四個必要條件
A:互斥條件(Mutualexclusion):資源不能被共享,只能由一個進程使用。
B:請求與保持條件(Hold and wait):已經得到資源的進程可以再次申請新的資源。
C:非剝奪條件(No pre-emption):已經分配的資源不能從相應的進程中被強制地剝奪。
D:循環等待條件(Circular wait):系統中若干進程組成環路,該環路中每個進程都在等待相鄰進程正佔用的資源
4.2常見的死鎖情形
A:事務A擁有對A表的X(獨佔)鎖,事務B擁有對B表的X(獨佔)鎖,在某一時間,事務
A需要B表的資源,而事務B需要A表的資源,此時即夠同了死鎖
示例代碼:
事務A:
begintransaction
update a set address='TT'fromtesta a where id=1
waitfordelay'00:00:10'
select*fromtestb where id=1
committransaction
事務B:
begintransaction
update a set address='TT'fromtestb a where id=1
waitfordelay'00:00:10'
select*fromtesta where id=1
committransaction
B.在同一個表中由書籤查找產生的死鎖
如果表有聚集索引(區段結構),那麼書籤就是從非聚集索引找到聚集索引後,利用聚集索引定位到數據。此處的書籤就是聚集索引。如果表沒有聚集索引(堆結構),那麼掃描非聚集索引後,通過RID定位到數據,那麼此處書籤就是RID。
這類死鎖產生的原因是書籤查找和更新數據產生的僵持狀態。簡單來說,就是由於Update語句對基本表產生X鎖,然後需要對錶上的索引也進行更新,而表上的索引正好被另一個連接進行查找,加了S鎖,此時又產生書籤查找去基本表加了X鎖的數據進行書籤查找,此時形成死鎖。
我們先看一下update和select語句的執行計劃
Setstatisticsprofileon
UPDATE testa SET age =age+1 WHEREid = 1
selectid,name,age,addressfromtesta where name='joe'
爲什麼會產生嵌套循環查詢
請參考
http://www.cnblogs.com/lzrabbit/archive/2012/05/21/2499389.html
--高頻率update
while(1=1)execp1
--高頻率select
while(1=1)execp2
如何消除書籤查找?
1.使用聚集索引查找,聚集索引的葉子節點就是數據行本身,因此不存在書籤查找
2.堆表聚集索引掃描、表掃描,說白了就是啥索引都不建直接全表掃描,肯定不會發生書籤查找,不過效率肯定不高.
3.使用非聚集索引的鍵列包含所有查詢或返回的列,這個不靠譜,非聚集索引最大鍵列數爲16,最大索引鍵大小爲900字節,就算你有勇氣在16列上全部建立索引,那如果表的列數超過16列了你咋辦,還有索引列長度之和不能超過900字節,所以不可能讓非聚集索引包含所有列,而且索引涉及到得列越多維護索引的開銷也就越大。
4.使用include,索引做到只能包含16列且不能超過900字節,include不受此限制,最多可以包含1023列怎麼也夠用了,而且對長度也沒有限制你可以隨心所欲的包含nvarchar(max)這也的列,但是text,image類型的列是不行的。(所以創建數據庫表時注意,像我們現在系統中就很多表結構都是text字段類型,會對現有的性能及後續的優化帶來不必要的麻煩,今後最少儘量少用text字段)
5.SQL相關操作(附錄)
--開啓事務隔離的方法,如
declare @sql varchar(8000)
select @sql = '
ALTERDATABASE ' + DB_NAME() + ' SETSINGLE_USER WITH ROLLBACK IMMEDIATE ;
ALTERDATABASE ' + DB_NAME() + ' SETTRANSACTIONISOLATIONLEVELRepeatableread;
ALTERDATABASE ' + DB_NAME() + ' SETMULTI_USER;'
Exec(@sql)
--查詢事務隔離級別信息
DBCC Useroptions
--清除緩存
DBCCDROPCLEANBUFFERS
DBCCFREEPROCCACHE
DBCCFREESYSTEMCACHE( 'ALL' )
--開啓SQL性能分析
Set statistics profile on
--開啓SQL執行時間統計
set statistics time on
--開啓磁盤的讀寫統計
set statistics io on
--查看錶鎖及鎖的類型
SELECT request_session_id,resource_type, resource_associated_entity_id,
request_status,request_mode, resource_description
FROM sys.dm_tran_locks
select request_session_id spid,OBJECT_NAME(resource_associated_entity_id)tableName
from sys.dm_tran_locks whereresource_type='OBJECT'
sp_who
select * from sys.sysprocesses
--查詢進程正在執行的SQL語句
Dbcc inputbuffer(spid)
數據存儲原理相關的文章
http://blog.jobbole.com/100349/
SQL 最小存儲單位 頁
http://blog.csdn.net/irelands/article/details/7348682
鎖的兼容圖