Oracle 鎖機制探究


以前雖然在網上看到很多關於Oracle鎖機制的描述,但總感覺哪里有缺陷不適合自己,因此花了點時間參考官網以及Tom Tyke的《Oracle 9i/10g/11g編程藝術》一書整理了一下Oracle鎖相關的知識。

Ps:此博客經過多次編輯,以當前版為准。

官網網址參考:

11.2
10.2

一、Oracle數據庫的鎖類型:

根據保護的對象不同,Oracle數據庫鎖可以分為以下幾大類:    
1、DML鎖(data locks,數據鎖),用於保護數據的完整性;    
2、DDL鎖(dictionary locks,字典鎖),用於保護數據庫對象的結構,如表、索引等的結構定義;    
3、內部鎖和閂(internal locks and latches),保護數據庫的內部結構。 
 
二、接下來依次討論以上三種鎖結構:
1.DML鎖
DML鎖主要包括TM鎖和TX鎖,其中TM鎖稱為意向鎖或表級鎖,TX鎖稱為行級鎖或事務鎖。我們可以認為Oracle只有如下6種LMODE的鎖,只是根據鎖定的對象不同而有不同的名稱,如6號的X鎖,既可以是用於鎖表的TM鎖,也可以是TX鎖,也可以是DDL鎖。
1.1 TM鎖(也叫意向鎖/表級鎖)
TM鎖包含如下類型:

TM鎖的兼容性如下:(Y表示兼容,N表示沖突)

1.2 TX鎖

TX鎖只包含一種類型的鎖:
TX的本義是Transaction(事務),當一個事務第一次執行數據更改(Insert、Update、Delete)或使用SELECT… FOR UPDATE語句進行查詢時,它即獲得一個TX(事務)鎖,直至該事務結束(執行COMMIT或ROLLBACK操作)時,該鎖才被釋放。
在同一個事務中,無論是鎖定一行,還是一百萬行,對於Oracle來說鎖開銷是一樣的。這點可能與其他數據庫不一樣(例如SQL Server),原因是針對Oracle的每行數據都有一個標志位來表示該行數據是否被鎖定。這樣就極大的減小了行級鎖的維護開銷,也不可能出現鎖升級。數據行上的鎖標志一旦被置位,就表明該行數據被加X鎖。
注意TX鎖在v$lock的lmode也是6,但是這個6與TM鎖的6號X鎖只是因為鎖定的對象不同而被叫做了TX鎖。

1.3 舉例說明

當發出一個DML命令后會話獲取一個3號的TM鎖,和一個針對特定行的6號TX鎖。
行級只有X鎖,且鎖模式為6,再次重申這里的6並不是指TM的6號表鎖。
此外Oracle一個事務中無論鎖定多少行只會獲取一個TX鎖(沒有鎖升級),但有多少個表對象就會獲取多少個TM表級鎖。
驗證如下:

查詢鎖的語句為:

select sid,type,id1,lmode,request,block from v$lock l where sid in (select session_id from v$locked_object) and type in ('TM', 'TX') order by 1;

1.4 DML鎖的總結:

讀永遠不會阻止寫。但有唯一的一個例外,就是select ...for update。
寫永遠不會阻塞讀。當一行被修改后,Oracle通過回滾段提供給數據的一致性讀。
注意:
以上讀寫操作不會互相阻塞是指在DML鎖級別不會,但在內存中依然會發生內部閂鎖的爭用造成讀被阻塞。例如DDL鎖可能會造成嚴重的library cache lock等待,導致select語句被阻塞。再比如熱塊爭用也會造成寫阻塞讀,只不過表現是buffer busy waits而已。

2.DDL鎖

重點:DDL是保護表結構定義的。
DDL命令發出時,Oracle會自動在被處理的對象上添加DDL鎖定,從而防止對象被其他用戶所修改。當DDL命令結束以后,則釋放DDL鎖定。
DDL鎖定不能顯式的被請求,只有當對象結構被修改或者被引用時,才會在對象上添加DDL鎖定。
並不是所有DDL都會觸發DDL鎖,例如現在的創建索引語句,就只會獲取一個S模式的TM鎖,因此不會阻塞讀。而online模式創建索引的語句則只會獲取一個2號的TM鎖,因此連DML也不會被阻塞。
需要注意的是DDL總會提交,即便是執行不成功也是如此,因此如果在事務中執行了DDL語句會導致所有事物被提交。驗證很容易,在一個窗口執行一條delete然后執行DDL,你會發現記錄被不可逆轉的刪除了,RollBack無效。因此針對事務中的DDL請務必使用自治事務實現。
DDL鎖有3種:
2.1 排他DDL鎖  --即6號的TM鎖(Exclusive DDL Locks)
一般對表的DDL語句都會獲取一個X模式的TM鎖,這是為什么在表結構更改時只能查詢不能修改的原因。
 
2.2 共享DDL鎖(Share DDL Locks)
共享DDL鎖的常見情形為創建存儲過程時,會嘗試為所有涉及到的表添加共享DDL鎖,這會允許類似的DDL操作並發,但會阻止所有想要獲取排他DDL鎖的會話(即更改表結構的會話)。
--我覺的可以認為這就是4號的TM表鎖。
 
2.3 可中斷解析鎖(Breakable Parse Locks)
會話解析一條SQL或PLSQL代碼塊時,對於該語句引用的每一個對象都會施加解析鎖,目的在於:如果以某種方式刪除或修改了引用對象,可以將共享池中已經解析的緩存語句無效刷出。
之所以叫做可中斷解析鎖,是因為這種鎖優先級很低可能會被其他互斥DDL操作打斷。
每個SQL在解析時都會獲取一個可中斷解析鎖,只要SQL語句還在shared pool中,這個鎖就一直存在。
Library cache lock就屬於可中斷解析鎖,可以認為Library cache lock是針對父游標句柄和子游標句柄的保護體,pin是針對父子游標的保護體。前者有三種模式:1 null,2 S,3 X 后者只有2 S,3 X兩種模式。在進行解析時一般會獲取共享模式的Library cache lock以防止內存對象被修改,之后轉為NULL模式。
 
3.內部閂鎖機制
Latches:
可譯作閂或者栓,這是一種非常低級別的序列化結構,用於協調並發會話對於共享數據結構、文件等的訪問。
閂可以防止並發會話破壞共享的內存資源,幾個非常典型的場景是:
  • 多個會話同時修改數據
  • 讀取的數據正在被其他會話修改
  • 重新分配內存
可能在這里會和事務鎖混淆,但事務鎖是鎖定邏輯結構的,如表和行,但是閂卻是鎖定物理結構的,如內存中的數據塊、執行計划鏈表等等。多個會話對於同一個對象的訪問就需要閂來保護了,例如對於同一個數據塊中不同行的訪問就需要latch來保護,而閂的存在時間是極短的,因此可以實現高並發(其實Oracle高並發並非是真正的並發而是latch釋放速度極快的串行訪問)。
通常在SGA中一個latch可以保護多個對象,例如DBWn和LGWR進程從SGA中分配內存以便創建新的數據結構,為了防止沖突進程會使用一種叫做shared pool latch的閂來序列化分配操作,在內存分配完畢后其它進程可能會需要訪問shared pool中的其他區域如library cache,此時閂只會鎖住library cache而非整個shared pool。
通常情況下oracle進程獲取閂的時間極短,一個簡單的查詢都會有成千上萬次的閂的獲取和釋放,是完全不能由用戶控制的。
閂的數目的增多,意味着並發量增大,可以通過v$latch視圖來查看閂的使用情況,包括每種閂被請求和等待的次數。
Mutex:(Mutual exclusion object)
可以譯作互斥體,這種結構很類似於閂,區別在於一種mutex只保護一種對象,而一種閂通常會保護多種對象。
mutex的優勢在於:
  • mutex可以減少latch爭用
  • mutex比latch占用更少的內存
  • share mode模式的mutex允許多個會話並發使用
其他內部鎖:
這些是比mutex和latch更復雜的結構,oracle數據庫使用以下幾種內部鎖:
1.Dictionary cache locks
2.File and log management locks
3.Tablespace and undo segment locks
這幾種鎖的解釋有興趣可以到官網看,平時基本觀察不到,后兩種基本都在多實例時出現。
 
拓展說明:
上邊談到了Library cache lock和Library cache pin,這里稍微解釋下:
一、首先我們以Oracle 10.2之前版本作為基准來解釋:主要來自於《Oracle8i Internal Services》一書的第四章
****************************************
Library cache的包含很多cache部分,它包含PLSQL代碼塊的、解析樹、SQL執行計划等,它還包含一個由SQL引用的DIANA的數據庫對象,此對象被用於進行PLSQL塊的編譯以及SQL解析和執行,盡管數據字典中也包含此類信息。此外library cache也包含例如同義詞轉換、依賴追蹤等信息,當然還有library cache lock/pin等內存閂鎖。
Library cache lock屬於可中斷解析鎖,有NULL、S、X三種模式,而Library cache pin有S和X兩種模式,在解析SQL時會話獲取S模式的Library cache lock,解析完畢后轉為NULL模式。如果有DDL操作更改了語句涉及的數據庫對象,那么依賴於此數據庫對象的所有library cache objects都會失效,失效過程就是失去NULL模式的library cache lock的過程。
****************************************
接下來先放一張圖:
這是library cache中主鏈表的簡易結構圖,每個hash bucket對應一個父游標句柄的鏈表,LCO表示library cache objects,是父游標或者子游標。父子游標是一對一或者一對多的關系。
在了解了以上基本常識的情況下,再來看涉及的內存栓鎖:
首次進行硬解析的會話需要獲取library cache latch(只有獨占模式,無隊列)來生成父游標句柄,待添加X模式library cache lock后釋放library cache latch進行父游標句柄構造,構造父游標句柄完畢后獲取X(獨占)模式的library cache pin來構造父游標,接下來獲取X模式的library cache lock生成子游標句柄,最后獲取X模式library cache pin構造子游標完成硬解析。
如果是在已經存在的父游標上進行硬解析那么就會添加S模式的父游標句柄library cache lock來獲取X模式的父游標library cache pin,之后獲取X模式的library cache lock/pin完成硬解析。
軟解析時則先獲取S模式的父游標句柄library cache lock,然后獲取S模式父游標library cache pin、S模式子游標句柄library cache lock和S模式子游標library cache pin。
二、接下來討論10.2之后的的變化:
library cache pin結構被Cursor:xxx類型的mutex取代,而在11.1之后library cache latch結構也被library cache:mutex xxx類型的mutex取代了。
那么全新的SQL硬解析時首先會在library cache:mutex X的保護下獲取X模式的library cache lock,然后釋放mutex並構造父游標句柄,之后獲取Cursor:pin X構造父游標,然后獲取子游標句柄library cache lock構造子游標句柄並釋放父游標的Cursor:pin X,最后獲取Cursor:pin X完成子游標的構造從而完成硬解析。
如果是已經存在的父游標進行硬解析,那么首先在library cache:mutex X的保護下獲取S模式的library cache lock,之后獲取Cursor:pin S來pin住父游標,然后獲取X模式的library cache lock構造子游標句柄並釋放父游標mutex,最后獲取Cursor:pin X構造子游標完成硬解析。
如果是軟解析那么首先在library cache:mutex X的保護下獲取S模式的library cache lock,之后獲取Cursor:pin S來pin住父游標,最后依次找到子游標句柄和子游標。
如果發生了Cursor:pin S wait on X,那么意味着有硬解析在發生,並且可能存在較高的version count,導致此父游標句柄下的所有游標無法拿到Cursor:pin S。還有一種情況是游標涉及的LCO發生了DDL導致所有句柄失效,此時需要進行重新編譯獲取X模式的子游標mutex,因此其他會話發生Cursor:pin S wait on X等待。
同樣的library cache lock等待也意味着存在硬解析,只有硬解析才會獲取X模式的library cache lock,由於library cache lock的獲取需要mutex的保護,相應的mutex等待也會伴生,可能是library cache:mutex X或者Cursor:xxx類型的mutex,這取決於library cache lock的等待是在父游標句柄還是子游標句柄上,子游標句柄較為常見。
 
最后重要說明:
以上關於內存鎖的解釋絕大部分都是基於《Oracle內核技術揭秘》的關於shared pool的章節以及《Oracle Internals: An Introduction》一書,如有困惑可以翻書或在文章下方留言交流。
 
總結一下自己的觀點:
DML和除可中斷解析鎖之外的DDL鎖就是保護邏輯結構的結構體,而library cahce lock/pin、latch和mutex則是保護內存中物理結構的結構體,拋開DML鎖,一般來說在內存中獲取內存鎖都需要latch和mutex的保護,常見的場景就是library cache中Library cache lock的獲取需要library cache:mutex X的保護,以及Buffer pool中的buffer pin 需要cbc latch保護住hash bucket中涉及到本SQL的鏈表頭。
從名字上就可以看出latch和mutex的保護對象,cursor: xxx這種mutex保護的是游標, cbc latch保護的則是cache buffer chain鏈表,library cahce:mutex(以前叫library cache latch)則是用於保護library cache的,具體說來也是hash bucket的父游標句柄鏈表,以此來獲取parent cursor handler上的library cache lock。
 
Oracle官網已經沒有太多更詳細的關於內部閂鎖的資料,更詳細的關於內部latch和mutex的運作機制和種類,參考呂海波《Oracle內核技術揭秘》和Steve Adams的《Oracle 8i Internal Services》一書。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM