Oracle Enqueue Waits

eric0435發表於2019-04-12

佇列用來有序地鎖定關係與非關係型的Oracle結構。關係型結構可能是Oracle的資料字典表或應用程式表。例如,當Oracle更新sys.col$表或一個應用程式更新它的employee表,佇列將會被呼叫。如果一個伺服器程式被鎖定的表所阻止,不僅僅會post一個enqueue wait等待事件,還會在v$lock,dba_lock,v$enqueue_statistics與其它檢視中顯示鎖資訊。非關係型結構被鎖定是為了阻止不合適的更改比如library cache cursor。

顧名思義,佇列是非常有序的,並確保以非常確定的方式更改結構。程式的入佇列請求會被推送到適當的佇列上,當它需要處理時,它的入佇列條目會從佇列中彈出(也叫作dequeue)。這裡並沒有什麼令人興奮的地方,但是排隊不是為了冒險,而是為了確保以一種非常有序的、類似會計的方式更改Oracle結構。

Oracle維護了數量驚人的佇列。在Oracle 10gr2中有208種佇列,在Oracle 11gr1中有247種佇列。但不必驚慌,因為你可能只會遇到幾個排隊的人。另外,如果您是一位經驗豐富的DBA,您已經處理過使用enqueue的行級和表級鎖。

診斷Enqueue等待
當解決佇列問題時,首先判斷佇列型別,然後確定所涉及的SQL,最後根據您對應用程式和相關Oracle內部的知識開發解決方案。在深入研究最常見的排隊等待(事務(TX)排隊)之前,務必瞭解如何確定正在等待哪個排隊和oracle 10g之前和之後版本中的相關會話。

在Oracle 10g之前,所有佇列的等待事件都是enqueue。這確實很不幸,因為這要從v$lock或v$session_wait中取樣來確定佇列名稱。下面的SQL語句用來從v$session_wait中來確實enqueue名。會話4388已經鎖表,沒有等待鎖,因此沒有顯示。佇列中的第一個會話是4387,緊接著是會話4393。判斷正在執行的SQL與所涉及的表最簡單的方式就是從v$session中查詢會話的sql_address或sql_hash_values。對於TM佇列,表可以透過p2列(ID 1列)來識別。它包含object_id,可以使用它來從dba_objects中進行查詢。這使得確定爭用物件非常簡單。

SQL> col sid format 9999 heading "Sid"
SQL> col enq format a4 heading "Enq."
SQL> col edes format a30 heading "Enqueue Name"
SQL> col md format a10 heading "Lock Mode" trunc
SQL> col p2 format 9999999 heading "ID 1"
SQL> col p3 format 9999999 heading "ID 2"
SQL> select sid,
  2         chr(bitand(p1, -16777216) / 16777215) ||
  3         chr(bitand(p1, 16711680) / 65535) enq,
  4         decode(chr(bitand(p1, -16777216) / 16777215) ||
  5                chr(bitand(p1, 16711680) / 65535),
  6                'TX',
  7                'Row related lock (row lock or ITL)',
  8                'TM',
  9                'Table related lock',
 10                'TS',
 11                'Tablespace and Temp Seg related lock',
 12                'TT',
 13                'Temporary Table',
 14                'ST',
 15                'Space Mgt (e.g., uet$, fet$)',
 16                'UL',
 17                'User Defined',
 18                chr(bitand(p1, -16777216) / 16777215) ||
 19                chr(bitand(p1, 16711680) / 65535)) edes,
 20         decode(bitand(p1, 65535),
 21                1,
 22                'Null',
 23                2,
 24                'Sub-Share',
 25                3,
 26                'Sub-Exlusive',
 27                4,
 28                'Share',
 29                5,
 30                'Share/Sub-Exclusive',
 31                6,
 32                'Exclusive',
 33                'Other') md,
 34         p2,
 35         p3
 36    from v$session_wait
 37   where event = 'enqueue'
 38     and state = 'WAITING'
 39  /
SQL>
Sid   Enq. Enqueue Name                   Lock Mode  ID 1     ID 2
----- ---- ------------------------------ ---------- -------- --------
4387  TM   Table related lock             Exclusive     49911        0
4393  TM   Table related lock             Sub-Exlusi    49911        0
SQL> @swswp enq%
Database: prod16 31-MAR-10 04:32pm
Report: swswp.sql OSM by OraPub, Inc. Page 1
Session Wait Real Time w/Parameters
Sess
ID    Wait Event                   P1           P2        P3
----- ---------------------------- ------------ --------- -----
4383  enq: TM – contention         1414332422   49911     0
4388  enq: TM – contention         1414332422   49911     0
2 rows selected.
SQL> l
1 select sid, event,
2 p1, p2, p3
3 from v$session_wait
4 where event like '&input%'
5 and state = 'WAITING'
6* order by event,sid,p1,p2

與latch等待事件一樣,從Oracle 10g開始,每一種佇列都有它自己的等待事件。這節省了診斷步驟,因為我們可以透過一個簡單的查詢確定所涉及的會話和佇列型別。會話4393已經持有表鎖並且沒有等待所以沒有顯示,會話4383和4388正等待鎖表因此post一個TM佇列等待。透過使用P2列(49911)來與dba_objects檢視的object_id關聯進行查詢來獲得被呼叫的表。

TX Enqueue等待
TX佇列等待是最常見的佇列等待事件。這也是最迷人的。想深入研究這個等待事件,因為它將使您更深入地瞭解Oracle如何管理事務併發性,這與塊克隆、undo、讀取一致性和相關事務列表有關。

TX佇列也叫作行級鎖佇列,實際上出現TX佇列有三個原因,並且只有一個實際上是行級鎖。每一個Oracle資料塊可以被抽象為三個區域:
.行資料包含真實的Oracle行記錄並且是每個資料塊最重要的一個部分。

.可變資料包含事務後設資料

.可用空間數量可以透過行資料增長與可變資料增長而減小

相關事務列表(ITLs)
內建在每個Oracle資料塊的可以資料區域的結構叫作相關事務列表(ITLs)。這些結構最主要是用來負責Oracle的行級鎖與讀一致性。從高度抽象的角度來看,可以認為ITLs就像檢查框,每個檢查框與一個特定的事務相關。如果想要更新行記錄,但被鎖定的行已經與其它事務的ITL關聯,你將會收到一個TX佇列等待,這確實是行級鎖。

每個Oracle資料塊都建立了特定數量的ITLs。ITLs的初始值是由表的initrans空間引數所控制的並且可以透過dba_tables檢視的ini_trans列來檢視。從Oracle 9i開始,預設的ini_trans值為1,然而透過簡單的塊dump可以清楚的看到建立了兩個ITL。使用兩個ITLs,單個資料塊可以同時併發地執行兩個事務。

假設第三個事務想要修改塊中沒有被鎖定的行而只在兩個ITL存在時,第三個事務的伺服器程式將嘗試動態建立一個額外的ITL。然而伺服器程式必須首先確保ITL的最大數(max_trans)不會被超過並且在資料塊中要有可用空間。如果伺服器程式不能建立額外的ITL,它將發出一個TX佇列等待事件,並且這個程式將耐心等待。為了減小這種情況的出現,單個塊的ITLs的預設值與最大值都可以設定為255。當不超過這個值時可以執行alter table命令來修改。

一旦在資料塊中建立了一個ITL後,唯一能獲得空間的方式是重新建立整個表。修改空間引數將不會影響已經建立的ITL。這就是為什麼預設的ITLs為1(實際上建立了兩個ITL)並且最大值設定為255的原因。如果資料塊的併發請求更多的ITLs,Oracle寧願消耗空間也不願意發出TX佇列等待事件而讓事務等待。

初看,ITL的最大數是255可能看上去非常有限,但請考慮這種情況:想想在最高併發應用程式中,在最高併發的資料庫中的最高並發表。也許有一個表可能有250個併發程式正在更新,刪除與插入記錄。現在真正有多少程式將會併發更新,刪除或插入記錄到一個資料塊中,而不是整個表或區,是單個塊。即使使用最高併發性的應用程式,在一個塊中啟用超過255個併發事務也是極不可能的。所以ITL的最大數255並沒有太大的限制。然而如果確實出現了問題,可以透過增加表的pct_free引數來減小資料塊的併發性或者為了減少儲存在塊中的行記錄可以增加固定長度的列。

Unod段的事務表
每個undo段在它的頭塊中包含一個結構叫事務表。Oracle開發人員將事務表中的行稱作slots(插槽)。每一個已經佔用的slot都與正在或已經在undo段中儲存undo資訊的事務相關。如果一個事務已經提交或者回滾,它確實是一個非活動事務,否則它就是一個活動事務。除了包含slot號與事務狀態,每個slot也包含一個序列號。為了區分不同的事務使用相同的slot並能讓slot重用,序列號可以增長。UBA是undo塊地址,提供到事務的undo的直接連結。SCN是當相關事務開始時事務的系統改變號。

事務表與效能分析人員相關因為它們提供了事務號。每個事務有一個相關的事務號,並且事務號是基於事務的事務表條目生成的。事務號由三組數字組成。第一部分是事務表號,第二部分是slot號,最後是相關序列號。例如,一個事務號為00100.000.00007。ITLs與事務表之間的聯絡是每個ITL條目關聯到一個特定的事務並且在ITL條目中包含事務號,比如00100.000.00007。

深入瞭解相關事務列表(ITL)
已經瞭解了ITL與undo段事務表,現在是將它們作為單個工作單元組合在一起的時候了,並展示在事務活動期間ITLs是如何變化的。深入瞭解相關事務列表可以讓你深入理解Oracle如何管理事務併發性,如何建立讀一致性塊以及為什麼要小心“snapshot too old”錯誤。

下面透過執行命令alter system dump datafile 1 block 75847來dump資料塊。在執行塊dump時,這個塊(1,75847)包含了許多行記錄並且有三個活動事務更新四行不同的記錄。第一個與第三個事務顯示正在更新一行記錄,第二個事務正在更新二行記錄。

$ cat prod5_ora_21741.trc
...
Block header dump: 0x00412847
Object id on Block? Y
seg/obj: 0xff6b csc: 0x00.50fcb6 itc: 3 flg: O typ: 1 - DATA
fsl: 0 fnx: 0x412848 ver: 0x01
Itl         Xid                 Uba         Flag  Lck      Scn/Fsc
0x01 0x0003.00d.00000318 0x00c3e3d0.0593.0c ----   1    fsc 0x0000.00000000
0x02 0x0008.01b.00000340 0x00c41bce.0481.24 ----   2    fsc 0x0000.00000000
0x03 0x0001.000.00000320 0x00c45fa0.0599.0b ----   1    fsc 0x0000.00000000
...

ITL條目包含以下內容:
itl:這是事務的ITL號

xid:這是事務ID,它由事務表ID(0003),事務表slot號(00d)與序列號(00000318)組成。事務ID是很重要的,因為它用於確保看起來相關的undo資訊是真正相關的。

uba:這是undo塊地址。這直接指向事務的最新更改undo,對於回滾事務和讀取一致性(克隆緩衝區構造)都是必要的。

flag:事務的狀態它可以有許多值,以下是常見值
----- 意味著事務是活動的,DML在執行事務沒有提交或回滾

--U-- 意味著事務已經提交,因此任何行資料都可以引用在活動事務中沒有被使用的ITL並且它們沒有被鎖定。事務的行資料可能沒有被合併。例如,如果一個列被更新,在更改之前與之前的值可能保留在行資料中。

--C-- 意味著事務已經提交,行資料已經合併,並且行資料中的ITL條目已經被刪除。任何塊touch可能觸發對這個flag的改變,包括select語句。我知道這很難相信。這種看似延遲的更改通常稱為延遲塊清除,或者簡單地稱為塊清除。

Lck:這是事務在某個時刻鎖定在這個塊中的行數。大於0的值不能夠說時行被鎖定。如果這個值為2,就像第二個事務一樣,這個事務關聯兩行記錄。鎖會保持到flag改變為C----為止。這意味著在一個事務提交後且不再被認為是活動(--U--)狀態時,Lck值可能大於0

Scn/Fsc:SCN是系統改變號並用來判斷事務是何時結束的(提交或回滾)。上面的例子中SCN沒有被指泒,但在事務提交後,SCN被設定瞭如下所示。當建立一個buffer的讀一致性版本判斷是否需要檢索undo時SCN是很重要的。FSC引用可用空間信用。它用於未提交的事務當一個更新或刪除操作造成行記錄長度收縮使用。Oracle將保護這個空閒空間,以防事務回滾和需要重新填充空間。如果空閒空間用於其他用途,然後事務回滾,則可能需要遷移行!。在下面的dump結果中,前兩個事務(ITLs x01與x02)已經提交標記它們的事務為非活動狀態。第三個事務,ITL x03,還沒有提交。在前兩個事務提交後,相同的塊dump命令,alter system dump datafile 1 block 75847。注意flag已經改變了,一個SCN已經指泒給事務了。

$ cat prod5_ora_21741.trc
...
Block header dump: 0x00412847
Object id on Block? Y
seg/obj: 0xff6b csc: 0x00.50fcb6 itc: 3 flg: O typ: 1 - DATA
fsl: 0 fnx: 0x412848 ver: 0x01
Itl          Xid                Uba         Flag Lck      Scn/Fsc
0x01 0x0003.00d.00000318 0x00c3e3d0.0593.0c --U- 1   fsc 0x0000.0050fd6f
0x02 0x0008.01b.00000340 0x00c41bce.0481.24 --U- 2   fsc 0x0000.0050fd6b
0x03 0x0001.000.00000320 0x00c45fa0.0599.0b ---- 1   fsc 0x0000.00000000

兩個flags ----與--U-是必需的,因為活動事或者過去的活動事務中涉及的行可以在其行資料中具有有效的ITL條目。因為簡單引用行資料與檢視ITL條目不能說明行當前被活動事務呼叫與鎖定。為了檢查行是否被鎖定,一個伺服器程式必須從行資料中得到ITL引用然後檢查資料塊的可變ITL區域中的flag。如果flag為 ----,那麼伺服器程式知道行確實被一個活動事務所呼叫且被鎖定。然而如果falg為--U,伺服器程式知道行沒有被鎖定。

塊清除程式的部分工作將刪除非活動事務行資料ITL條目,將它們各自的ITL條目在資料塊的可變部分的flag的狀態修改為C---,併合並行資料。

這是一種聰明的策略,因為Oracle可以快速使用最小的改變來記錄資料塊中的改變,但仍然在行級別維護併發控制。最終需要對塊進行最後的更改,但這可能發生在工作負載較低的時期,比如基準測試完成之後。

執行查詢語句來touch塊1,75847後,再執行dump命令的結果如下,資料塊(1,75847)在執行查詢語句touch資料塊後事務flags從--U-變為了C---,指示塊清除已經發生了。

$ cat prod5_ora_21741.trc
...
Block header dump: 0x00412847
Object id on Block? Y
seg/obj: 0xff6b csc: 0x00.510047 itc: 3 flg: O typ: 1 - DATA
fsl: 0 fnx: 0x412848 ver: 0x01
Itl          Xid                Uba         Flag Lck     Scn/Fsc
0x01 0x0003.00d.00000318 0x00c3e3d0.0593.0c C--- 0     scn 0x0000.0050fd6f
0x02 0x0008.01b.00000340 0x00c41bce.0481.24 C--- 0     scn 0x0000.0050fd6b
0x03 0x0001.000.00000320 0x00c45fa0.0599.0b ---- 1     fsc 0x0000.00000000
...

現在我意識到這很有趣,但是我也理解一些讀者可能認為這個block dump和ITL的東西並沒有那麼強的關聯。但我不敢苟同。您不僅對TX排隊有了更全面的瞭解,而且還清楚地瞭解瞭如何排隊Oracle實現了它的專利行級鎖方案。

深入瞭解Buffer克隆
介紹塊克隆是因為它與CBC latch競爭。現在將深入學習Oracle如何使用ITLs,undo塊,SCNs與其它有趣的Oracle技術。當一個伺服器程式要定位一個請求的buffer並且發現請求的行在查詢開始後發生改變了,它必須為buffer建立一個時光倒流的映象。這就叫作當前(CU)buffer的一致性讀(CR)buffer。一旦buffer被複製,合適的undo被應用後,使被複製的buffer回退直到CR buffer被成功克隆好為止。

假設我們的查詢執行時間是SCN 12330,查詢最終得到要訪問的buffer 7,678。然而,我們注意到存在一些ITL活動事務 7.3.8當前是活動狀態並且buffer可能在我們查詢開始後發生了改變。事務5.2.6是非活動狀態(flag為C,並指派了SCN,並且Lck為0),但是改變的提交時間在我們查詢開始之後並且影響這個當前(CU)buffer。這些塊改變意味著在CU buffer在我們查詢在SCN 12330時間點開始後已經發生改變了並且不能用於我們的查詢。我們需要一個一致性讀副本,它可以時光倒流回到SCN 12330時間點。因此CU buffer 7,678必須被克隆並應用undo,來建立一個SCN12330時間點的CR buffer。

在執行buffer克隆之前,必須找到一個不被頻繁訪問的free buffer然後使用7,678的CU buffer來替換它。伺服器程式將獲得LRU chain latch與相關的LRU chain,然後從LRU chain的LRU端開始掃描,查詢不被頻繁訪問的free buffer。最終將找到一個不被頻繁訪問的free buffer並使用CU buffer 7,678的副本來替換它。當然CBC結構也將被更新來對映克隆buffer在buffer cache中的位置。

從與第一個ITL相關的活動事務7.3.8開始。伺服器程式需要檢索在我們查詢開始時間scn 12330之後所有生成的undo記錄。事務7.3.8的最近生成的undo可以透過它的ITL的undo塊地址(UBA)所連結到的undo塊2,45中找到。伺服器程式然後必須訪問undo塊2,45。這需要請求CBC活動並且也可能請求LRU活動來執行IO呼叫。一旦訪問到undo buffer 2,45,將會透過比較事務號來檢查確保我們使用正確的事務在工作。資料塊與undo塊事務號需要匹配(7.3.8),因為事務是活動的,所以undo資訊應該沒有鋪覆蓋。

undo塊2,45的SCN是12348,這意味著undo塊代表的塊改變出現在我們查詢開始時間scn 12330之後,因此,我們需要對克隆的CR buffer應用undo資料,讓它回退到過去一點點。

undo塊2,45也連結到了另一個undo塊2,90。這是一種undo鏈並且可能持續一段時間,消耗大量的計算資源。伺服器程式現在必須訪問undo塊2,90(請求CBC活動並且也可能請求LRU活動來執行IO呼叫)並且再次比較事務號來確保它們是否匹配。它們匹配,現在檢查SCN。undo塊2,90的SCN是12320,它在我們的查詢開始時間SCN 12330之前,因此我們不需要應用undo。如果不應用undo,我們的CR buffer將代表的是塊7,678在SCN 12320時間點的版本,這比我們要查詢的時間SCN 12330早了。

現在檢視第二個ITL,它與事務5,2.6關聯。這個事務在SCN 12350時間點已經提交了,在我們的查詢開始時間之後,因此我們需要應用它的undo。從ITL條目來看,我們將得到ndo塊地址2,70並且訪問這個undo塊。現在比較事務號,因為事務已經提交,undo資訊將不再受保護。增加undo保留期可以讓udno資訊保留更長的境,但也不受保護。

假設另一個伺服器程式覆蓋了undo塊2,70中的相關事務undo資訊。如果出現這種情況,伺服器程式的事務號將被記錄並且這裡將記錄為5.2.6。透過事務號比較,我們注意到差異並且立即知道undo塊2,70中的undo不能應用於我們的CR buffer。在這時,伺服器程式將會發出快照太舊的錯誤資訊並停止我們的查詢。很明顯,undo塊快照太舊因為被其它程式覆蓋了。

幸運地是,事務號是匹配的。undo塊2,70中的undo是在SCN 12340時間點發生的改變,它在我們的查詢開始之後,因此我們應用這個undo到我們的CR buffer。下一個undo連結是空的,因此沒有其它undo需要應用了。

現在返回到ITL條目,這裡沒有更多的ITL需要考慮,因此我們完成的資料塊的克隆。任何一個伺服器程式現在都可以訪問CR buffer 7,678它包含了SCN 12330時間所代表的內容。

現在應該很清楚為什麼ITLs如此重要了,而且Oracle的讀取一致性模型雖然非常強大、必要且高效,但仍然相對昂貴,因為它可能會消耗大量CPU和IO,從而減慢應用程式的響應時間。Oracle非常清楚這一點,並且從Oracle 10gr2開始使用記憶體最佳化結構來臨時儲存undo資訊。這些物件不是段型別並且不受與段相關的CBC和LRU chain活動的影響。在記憶體中,undo被儲存在shared pool中。


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/26015009/viewspace-2641209/,如需轉載,請註明出處,否則將追究法律責任。

相關文章