sqlserver調優-鎖

繼續接着上一篇文章,這篇文章我們主要看看模擬多客戶的環境,同樣是sqlserver,爲什麼有些表幾千萬-甚至上億數據量依然響應很快?爲什麼有些表幾十萬-幾百萬就延時很大甚至拖垮應用程序。沒錯確實有這樣的事情發生,我們今天就來看看到底是怎麼發生的?今天的討論拋開硬件資源的問題,因爲大家知道數據庫最大的瓶頸在內存。說到數據庫就不得不說一下事務,可以這麼說事務是關係型數據庫的一個最基礎的功能,我們常說的數據庫ACID。試想一下ACID的基礎靠誰保證?沒錯就是數據庫引擎提供的鎖機制。事務我們簡單的發散一下,因爲在.Net技術平臺上對事務的支持還是不錯的,比如我們的ADO.NET它就集成了數據庫事務,包括後期的EF EFCORE等等因爲他們的數據訪問底層還是ADO.NET。還有甚至分佈式事務DTC,比如WCF平臺事務流,它就是基於windows平臺提供的DTC分佈式事務的基礎結構實現的,我們知道在分佈式事務DTC中,有一個資源管理器的角色,而我們一般接觸的這個資源管理器就是數據庫。我們繼續正題。
事務隔離級別
我們瞭解事務的隔離是依靠鎖來實現,在sqlserver數據庫裏面支持4中事務隔離級別(隔離級別有高有低),這個很好理解,sqlserver數據庫爲了應付不同的行業領域設計的。比如銀行可能就需要最高級別的事務隔離。我們今天討論的內容就是基於可重複讀這個事務隔離級別(數據庫默認是已提交讀)。可重複讀簡單來說就是1.事務修改尚未提交的行,其他事務不能讀取。2.其他事務不能修改當前事務讀取的行,直到當前事務完成,提交或回滾。事務隔離級別就簡單介紹到這裏,下面簡單介紹下鎖的一些概念性的東西。
Sqlserver數據庫引擎可以鎖定的資源大概有一下幾種。
RID:鎖定堆表的某一行;
KEY:鎖定索引上的某一行;
PAGE:鎖定8k的數據頁或者索引頁;
TABLE:鎖定整個表;
DATABASE:鎖定整個數據庫;
以上就是數據庫引擎可以鎖定的資源,我只列出了我熟悉的並且比較常用的,其他還有架構啥的等等,這裏不討論。
Sqlserver數據庫引擎資源鎖類型大概有一下4種。
共享(S)鎖:讀取數據select操作,如果資源上存在S鎖,其他事務不能修改改資源;
更新(U)鎖:更新數據update操作防止死鎖的一把鎖,我們可以這麼理解,更新操作需要先讀取資源獲取S鎖之後再升級爲X鎖,那麼問題來了,S鎖兼容,在可重複讀或以上級別,S鎖需要事務完成纔會釋放,如果這個時候同一個資源有多把S鎖,並且需要修改數據升級爲X鎖,因爲X鎖不兼容,最後發生死鎖。U鎖不同,在同一個資源上只能有一把U鎖,這樣如果發生update操作就能順利升級爲X鎖,完成更新操作。
排他(X)鎖:最高級別的隔離鎖,資源獲取X鎖,任何鎖資源都無法兼容,也就是獨佔,除開最低的隔離級別未提交讀,我相信沒人會用這個隔離級別;
意向(I)鎖:I鎖主要作用於提高訪問性能,主要表級資源。I鎖又分三種,意向共享(IS),意向排他(IX),意向共享排他(SIX)。
Sqlserver鎖資源兼容性
不解釋,都在圖裏了。概念就到這,下面看實例效果,演示鎖部分,我隨便創建了兩張表如下。
 
兩張是關聯關係表,數據量均在10w左右,test1表id爲聚集索引,test2表只有一個非聚集索引tid。某個具體數據庫上面鎖資源的查看,我們可以通過sql查看代碼如下。
1 select  request_session_id,resource_type,resource_associated_entity_id,request_status,request_mode,
2 resource_description,p.object_id,object_name(p.object_id) as object_name, p.*
3 from sys.dm_tran_locks d left join sys.partitions p
4 on d.resource_associated_entity_id =p.hobt_id where resource_database_id=db_id('數據庫名')
5 order by request_session_id,resource_type,resource_associated_entity_id
後續查看鎖資源的操作我就不貼代碼了,好了準備工作就到這,我們先修改數據庫事務隔離級別爲重複讀提交,因爲它比較有代表性,修改事務隔離級別代碼。
1 SET TRANSACTION ISOLATION LEVEL REPEATABLE READ
2 GO
SELECT
1 begin tran
2 select id, name, age, createTime, createBy from t_test1
3 where id in(12, 10000)
開啓一個事務並且未提交,where in 查詢id=12和10000的兩個數據,選擇12和10000只是想錯開數據頁,下面我們看看計劃和鎖資源情況
說明一下,我當前的sessionid爲54,執行計劃內容說明是通過聚集索引seek查找,這個沒什麼懸念,id本來就算聚集索引,並且返回兩條數據。繼續看鎖資源表,1.首先在db級別申請了一把s鎖,其實目的就算防止db修改刪除操作。2.在table級別申請了一個is鎖,也是防止修改操作,比如添刪改字段索引等等操作。3.在兩個數據的聚集索引鍵上面分別申請了兩把s鎖,同樣防止修改操作。4.在數據頁上面同時也申請了兩把is鎖。此時如果另外一個用戶嘗試update這兩條數據會被阻塞,直到該事務完成,如果是讀提交級別是可以完成修改。操作看效果,我現在模擬另外一個用戶,嘗試修改id爲12的這條數據,看代碼。
1 delete from t_test1 where id = 12
執行結果
模擬的用戶sessionid爲64,執行del語句一直被阻塞,這個時候當我提交sessionid爲54的事務,sessionid64的刪除操作即可完成,這一步我就不測試了。接下來我們看看非聚集條件查詢會怎麼樣,我爲表test1字段name添加一條非聚集索引,看代碼。
1 begin tran
2 select id, name, age, createTime, createBy from t_test1
3 where name ='10000bbbb'
計劃和鎖資源情況
這裏返回1條數據,卻有兩個key被申請了s鎖。爲什麼?看上面的計劃,先由非聚集索引seek到1條數據(在這個key上加了s鎖),由於非聚集索引葉上只有非聚集字段值和聚集字段值所以還需要聚集索引seek數據,這樣聚集索引的key也就被加了s鎖,其他鎖資源情況一樣,其他操作情況跟上面也是一樣。以上兩條查詢是索引seek,下面我們來看一條索引scan的查詢,看看所資源怎麼樣。
1 begin tran
2 select id, name, age, createTime, createBy from t_test1
3 where [createTime] = '2015-07-06 22:28:55.000'
計劃和鎖資源情況
where條件篩選了未加索引的createTime字段,執行計劃沒辦法選擇了聚集索引掃描,最後返回了那麼一條數據。由於採用聚集索引scan,導致所到page都申請is鎖,並且還對聚集索引key申請了一個s鎖,我大概看了下page上申請的is鎖大概有1000個。是不是比較恐怖?SELECT最後看下join操作鎖資源會是什麼樣子的。
1 begin tran
2 select a.id, a.name, a.age, a.createTime, createBy, b.tid from t_test1  a
3 inner join [dbo].[t_test2] b
4 on a.id = b.tid
5 where a.id = 10000
計劃和所資源
最終返回一條數據,所資源情況也差不多,兩張表因爲都有聚集索引,分別在兩張表的聚集索引key上申請了s鎖,同時兩張表也申請了is鎖,page上的is鎖主要都是子表的,因爲子表用的是scan掃描。好了select操作就到這,接下來看看update操作。
UPDATE
1 begin tran
2 update t_test1 set createBy = 't' where name = 'ghf'
簡單說明該表有聚集索引,createBy沒有索引,name字段有非聚集索引。
計劃和所資源情況
通過非聚集索引seek到一條數據,所以在非聚集索引key上申請了一把u鎖,然後通過聚集索引做update操作,所以在聚集索引key鍵上申請了x鎖。同時其他資源上的鎖均有升級,聚集索引所在page上的鎖升級爲ix鎖,說明更新操作所有涉及到的資源情況都會升級更高的級別,表示兼容粒度越來越小。現在我們在sessionid64上面查詢id爲10000的數據看什麼效果。
1 select id, name, age, createTime, createBy from t_test1
2 where id =10000
結果
一直被阻塞,爲什麼?因爲聚集索引key鍵爲10000的這個數據申請了x鎖,所以session64這條查詢語句想要申請s鎖,不兼容所以阻塞等待。我們繼續修改上面update語句,set字段爲非聚集索引字段,where條件爲聚集索引,看看什麼效果。
1 begin tran
2 update t_test1 set name = 'y' where id = 10000
結果
奇怪的是有三條數據輸出在key上面申請了x鎖?我們結合執行計劃來分下一下,首先通過聚集索引seek到一條數據,並且需要修改的也是這條聚集索引對應的存放數據,所以在這個聚集key上申請了一把x鎖,同時set的字段爲非聚集字段name,因爲非聚集索引數據列裏有name字段,所以這個地方也會申請兩把x鎖,刪除老的和添加新的,這個很容易理解,非聚集字段不能直接修改,因爲需要排序。我們在來看下最後一種沒有索引的字段scan,update操作鎖資源會是什麼樣子。
1 begin tran
2 update t_test1 set name = 'a' where createTime = '2015-07-09 19:04:03.000'
結果
符合預期,除了多了很多page上面申請了iu鎖,其他幾乎一樣。最後我們看最後一個操作insert。
INSERT
1 insert into t_test1 values('t1', 20, getdate(), 'c1')
結果
insert感覺簡單多了,表上有幾個索引就針對索引key申請幾把x鎖。其實也不難理解,增刪改數據需要維護索引資源,所以增加一條數據時會把表上所有的索引key鍵添加一把x鎖。t_test1我剛刪除了一條非聚集索引,剩下兩條,所以有兩把x鎖。
簡單總結
通過上面的實例演示,是不是覺得,索引和鎖有密不可分的關係,同時對性能影響也是非常大的。回到我們開始的問題,試想如果一張表頻繁的增刪改查,並且因爲索引的利用率不高,是不是會發生幾十萬或者百萬數據就會停止服務?好了就到這吧,數據庫文件系統太複雜,我們要學的還有很多很多。不說了我看電視了。
 
 
 
 
 
 
 
 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章