聊一聊數據庫中的鎖

背景

數據庫中有一張叫後宮佳麗的表,天天都有幾百萬新的小姐姐插到表中,光陰荏苒,夜以繼日,日久生情,時間長了,表中就有了幾十億的小姐姐數據,看到幾十億的小姐姐,每到晚上,我可愁死了,這麼多小姐姐,我翻張牌呢?
辦法固然是精兵簡政,刪除那些age>18的,給年輕的小姐姐們留位置...
因而我在數據庫中添加了一個定時執行的小程序,每到週日,就自動運行以下的腳本sql

delete from `後宮佳麗` where age>18

一開始還自我感受良好,後面我就發現不對了,每到週日,這個腳本一執行就是一成天,運行的時間有點長是小事,重點是這大好週日,我再想讀這張表的數據,怎麼也讀不出來了,怎是一句空虛了得,我好難啊!數據庫

Alt text

爲何

編不下去了,真實背景是公司中遇到的一張有海量數據表,每次一旦執行歷史數據的清理,咱們的程序就由於讀不到這張表的數據,瘋狂地報錯,後面一查瞭解到,原來是由於定時刪除的語句設計不合理,致使數據庫中數據由行鎖(Row lock)升級爲表鎖(Table lock)了😂.
解決這個問題的過程當中把數據庫鎖相關的學習了一下,這裏把學習成果,分享給你們,但願對你們有所幫助.
我將討論SQL Server鎖機制以及如何使用SQL Server標準動態管理視圖監視SQL Server 中的鎖,相信其餘數據的鎖也大同小異,具備必定參考意義.小程序

鋪墊知識

在我開始解釋SQL Server鎖定體系結構以前,讓咱們花點時間來描述ACID(原子性,一致性,隔離性和持久性)是什麼。ACID是指數據庫管理系統(DBMS)在寫入或更新資料的過程當中,爲保證事務(transaction)是正確可靠的,所必須具有的四個特性:原子性(atomicity,或稱不可分割性)、一致性(consistency)、隔離性(isolation,又稱獨立性)、持久性(durability)。安全

ACID

原子性(Atomicity)

一個事務(transaction)中的全部操做,或者所有完成,或者所有不完成,不會結束在中間某個環節。事務在執行過程當中發生錯誤,會被回滾(Rollback)到事務開始前的狀態,就像這個事務歷來沒有執行過同樣。即,事務不可分割、不可約簡。session

一致性(Consistency)

在事務開始以前和事務結束之後,數據庫的完整性沒有被破壞。這表示寫入的資料必須徹底符合全部的預設約束、觸發器、級聯回滾等。併發

隔離性(Isolation)

數據庫容許多個併發事務同時對其數據進行讀寫和修改的能力,隔離性能夠防止多個事務併發執行時因爲交叉執行而致使數據的不一致。事務隔離分爲不一樣級別,包括未提交讀(Read uncommitted)、提交讀(read committed)、可重複讀(repeatable read)和串行化(Serializable)。app

持久性(Durability)

事務處理結束後,對數據的修改就是永久的,即使系統故障也不會丟失。ide

來源:維基百科 https://zh.wikipedia.org/wiki/ACID性能

事務 (Transaction:)

事務是進程中最小的堆棧,不能分紅更小的部分。此外,某些事務處理組能夠按順序執行,但正如咱們在原子性原則中所解釋的那樣,即便其中一個事務失敗,全部事務塊也將失敗。學習

鎖定 (Lock)

鎖定是一種確保數據一致性的機制。SQL Server在事務啓動時鎖定對象。事務完成後,SQL Server將釋放鎖定的對象。能夠根據SQL Server進程類型和隔離級別更改此鎖定模式。這些鎖定模式是:

鎖定層次結構

SQL Server具備鎖定層次結構,用於獲取此層次結構中的鎖定對象。數據庫位於層次結構的頂部,行位於底部。下圖說明了SQL Server的鎖層次結構。

Alt text

共享(S)鎖 (Shared (S) Locks)

當須要讀取對象時,會發生此鎖定類型。這種鎖定類型不會形成太大問題。

獨佔(X)鎖定 (Exclusive (X) Locks)

發生此鎖定類型時,會發生以防止其餘事務修改或訪問鎖定對象。

更新(U)鎖 (Update (U) Locks)

此鎖類型與獨佔鎖相似,但它有一些差別。咱們能夠將更新操做劃分爲不一樣的階段:讀取階段和寫入階段。在讀取階段,SQL Server不但願其餘事務有權訪問此對象以進行更改,所以,SQL Server使用更新鎖。

意圖鎖定 (Intent Locks)

當SQL Server想要在鎖定層次結構中較低的某些資源上獲取共享(S)鎖定或獨佔(X)鎖定時,會發生意圖鎖定。實際上,當SQL Server獲取頁面或行上的鎖時,表中須要設置意圖鎖。

SQL Server locking

瞭解了這些背景知識後,咱們嘗試再SQL Server找到這些鎖。SQL Server提供了許多動態管理視圖來訪問指標。要識別SQL Server鎖,咱們可使用sys.dm_tran_locks視圖。在此視圖中,咱們能夠找到有關當前活動鎖管理的大量信息。

在第一個示例中,咱們將建立一個不包含任何索引的演示表,並嘗試更新此演示表。

CREATE TABLE TestBlock
(Id INT ,
Nm VARCHAR(100))

INSERT INTO TestBlock
values(1,'CodingSight')
In this step, we will create an open transaction and analyze the locked resources.
BEGIN TRAN
UPDATE TestBlock SET   Nm='NewValue_CodingSight' where Id=1
select @@SPID

Alt text

再獲取到了SPID後,咱們來看看sys.dm_tran_lock視圖裏有什麼。

select * from sys.dm_tran_locks  WHERE request_session_id=74

Alt text

此視圖返回有關活動鎖資源的大量信息,可是是一些咱們難以理解的一些數據。所以,咱們必須將sys.dm_tran_locks join 一些其餘表。

SELECT dm_tran_locks.request_session_id,
       dm_tran_locks.resource_database_id,
       DB_NAME(dm_tran_locks.resource_database_id) AS dbname,
       CASE
           WHEN resource_type = 'OBJECT'
               THEN OBJECT_NAME(dm_tran_locks.resource_associated_entity_id)
           ELSE OBJECT_NAME(partitions.OBJECT_ID)
       END AS ObjectName,
       partitions.index_id,
       indexes.name AS index_name,
       dm_tran_locks.resource_type,
       dm_tran_locks.resource_description,
       dm_tran_locks.resource_associated_entity_id,
       dm_tran_locks.request_mode,
       dm_tran_locks.request_status
FROM sys.dm_tran_locks
LEFT JOIN sys.partitions ON partitions.hobt_id = dm_tran_locks.resource_associated_entity_id
LEFT JOIN sys.indexes ON indexes.OBJECT_ID = partitions.OBJECT_ID AND indexes.index_id = partitions.index_id
WHERE resource_associated_entity_id > 0
  AND resource_database_id = DB_ID()
 and request_session_id=74
ORDER BY request_session_id, resource_associated_entity_id

Alt text

在上圖中,您能夠看到鎖定的資源。SQL Server獲取該行中的獨佔鎖。(RID:用於鎖定堆中單個行的行標識符)同時,SQL Server獲取頁中的獨佔鎖和TestBlock表意向鎖。這意味着在SQL Server釋放鎖以前,任何其餘進程都沒法讀取此資源,這是SQL Server中的基本鎖定機制。

如今,咱們將在測試表上填充一些合成數據。

TRUNCATE TABLE    TestBlock
DECLARE @K AS INT=0
WHILE @K <8000
BEGIN
INSERT TestBlock VALUES(@K, CAST(@K AS varchar(10)) + ' Value' )
SET @K=@K+1
 END
--After completing this step, we will run two queries and check the sys.dm_tran_locks view.
BEGIN TRAN
 UPDATE TestBlock  set Nm ='New_Value' where Id<5000

Alt text

在上面的查詢中,SQL Server獲取每一行的獨佔鎖。如今,咱們將運行另外一個查詢。

BEGIN TRAN
 UPDATE TestBlock  set Nm ='New_Value' where Id<7000

Alt text

在上面的查詢中,SQL Server在表上建立了獨佔鎖,由於SQL Server嘗試爲這些將要更新的行獲取大量RID鎖,這種狀況會致使數據庫引擎中的大量資源消耗,所以,SQL Server會自動將此獨佔鎖定移動到鎖定層次結構中的上級對象(Table)。咱們將此機制定義爲Lock Escalation, 這就是我開篇所說的鎖升級,它由行鎖升級成了表鎖。

根據官方文檔的描述存在如下任一條件,則會觸發鎖定升級:

  • 單個Transact-SQL語句在單個非分區表或索引上獲取至少5,000個鎖。

  • 單個Transact-SQL語句在分區表的單個分區上獲取至少5,000個鎖,而且ALTER TABLE SET LOCK_ESCALATION選項設置爲AUTO。

  • 數據庫引擎實例中的鎖數超過了內存或配置閾值。

https://docs.microsoft.com/en-us/previous-versions/sql/sql-server-2008-r2/ms184286(v=sql.105)

如何避免鎖升級

防止鎖升級的最簡單,最安全的方法是保持事務的簡短,並減小昂貴查詢的鎖佔用空間,以便不超過鎖升級閾值,有幾種方法能夠實現這一目標.

將大批量操做分解爲幾個較小的操做

例如,在我開篇所說的在幾十億條數據中刪除小姐姐的數據:

delete from `後宮佳麗` where age>18

咱們能夠不要這麼心急,一次只刪除500個,能夠顯着減小每一個事務累積的鎖定數量並防止鎖定升級。例如:

SET ROWCOUNT 500
delete_more:
     delete from `後宮佳麗` where age>18
IF @@ROWCOUNT > 0 GOTO delete_more
SET ROWCOUNT 0

建立索引使查詢儘量高效來減小查詢的鎖定佔用空間

若是沒有索引會形成表掃描可能會增長鎖定升級的可能性, 更可怕的是,它增長了死鎖的可能性,而且一般會對併發性和性能產生負面影響。
根據查詢條件建立合適的索引,最大化提高索引查找的效率,此優化的一個目標是使索引查找返回儘量少的行,以最小化查詢的的成本。

若是其餘SPID當前持有不兼容的表鎖,則不會發生鎖升級

鎖定升級始老是升級成表鎖,而不會升級到頁面鎖定。若是另外一個SPID持有與升級的表鎖衝突的IX(intent exclusive)鎖定,則它會獲取更細粒度的級別(行,key或頁面)鎖定,按期進行額外的升級嘗試。表級別的IX(intent exclusive)鎖定不會鎖定任何行或頁面,但它仍然與升級的S(共享)或X(獨佔)TAB鎖定不兼容。
以下所示,若是有個操做始終在不到一小時內完成,您能夠建立包含如下代碼的sql,並安排在操做的前執行

BEGIN TRAN
SELECT * FROM mytable (UPDLOCK, HOLDLOCK) WHERE 1=0
WAITFOR DELAY '1:00:00'
COMMIT TRAN

此查詢在mytable上獲取並保持IX鎖定一小時,這可防止在此期間對錶進行鎖定升級。

Happy Ending

好了,不說了,小姐姐們由於不想離我開又打起來了(死鎖).

Alt text

參考文獻: SQL Server Transaction Locking and Row Versioning Guide https://docs.microsoft.com/en-us/previous-versions/sql/sql-server-guides/jj856598(v=sql.110) SQL Server, Locks Object https://docs.microsoft.com/en-us/sql/relational-databases/performance-monitor/sql-server-locks-object?view=sql-server-2017 How to resolve blocking problems that are caused by lock escalation in SQL Server https://support.microsoft.com/es-ve/help/323630/how-to-resolve-blocking-problems-that-are-caused-by-lock-escalation-in Main concept of SQL Server locking https://codingsight.com/main-concept-of-sql-server-locking/

相關文章
相關標籤/搜索