鎖是併發訪問的時候用於保護不共享資源不被同時併發修改的機制。
oracle鎖分為DML鎖,DDL鎖,內部鎖和latch
DML鎖確保一次只能只有一個人修改某一行(TX鎖),而且正在處理一個表時別人不能刪除(TM鎖)。
DDL鎖,在DDL操作是系統會自動為物件加上DDL鎖,保護這些物件不被其他會話鎖修改。
latch是輕量級的序列化裝置,用於協調對共享資料結構、物件、檔案的多使用者訪問,一般都是保護共享記憶體結構使用的鎖,在此不做討論。
一般的鎖管理器工作過程:
1.找到想要鎖定的那一行地址
2.在鎖管理器排隊
3.鎖定列表
4.搜尋列表,檢視別人是否鎖定這一行
5.在列表中建立一個新的條目,表明已經鎖定這一行
6.對列表解鎖
接下里修改,之後提交修改後,繼續過程:
7.再次排隊
8.鎖住鎖的列表
9.在這個列表中鎖定,並釋放所有的鎖
10.對列表解鎖
oracle鎖管理方式:
找到需要鎖定的那行地址
到達那一行
鎖定這一行
通常lock有三個元件:Resource Structure(資源)、Lock Structure(鎖)和Enqueue(排隊機制)
Resource和lock是資料結構,而Enqueue是演算法。
Resource Structure每一個需要併發控制的資源都有用這個資料結構來描述,先關的成員為:owner、waiter和converter,這是三個指標,分別指向3個由Lock Structure組成的連結串列。
Lock Structure
每當程式需要訪問共享資源時,必須先“鎖定”該資源,這個動作實際上是從記憶體中申請一個Lock Structure,
,在其中記錄“鎖模式、程式ID”等重要資訊。然後看是否立即能夠獲得資源的訪問權,如果不能的話將這個Lock structure掛到Resource Structure的Waiter連結串列中,如果能夠獲得,則把Lock Structure的owner連結串列中。
最常用的鎖模式
Share 擁有這對資源進行只讀訪問,允許其他使用者併發只讀訪問
Exclusive 擁有者對資源進行修改訪問,不允許其他使用者併發訪問
Enqueue 演算法
Lock使用的是Enqueue演算法,可以理解為“先入先出佇列”,如果程式的鎖定請求不能滿足,該程式的Lock
Structure就被加到Waiter連結串列的末端。當佔用程式釋放鎖時,會檢查Waiter和Converter佇列,把鎖分配給先入對的請求者。
converter和waiter兩個等待佇列,演算法的有些區別:如果某個操作先後需要2中不同模式的鎖,比如先是share
mode然後是exclusive mode,則程式會先請求share mode 後獲得lock
structure會掛在owner佇列上,當需要exclusive mode鎖時,程式先釋放share
mode的鎖,然後再次申請exclusive
mode的鎖,但是可能無法立即獲得,這時請求會掛在converter佇列下,converter佇列會被優先於waiter佇列處理。
oracle行級鎖機制
首先明白三個概念:
ITL:每個資料塊的頭部有一個叫做ITL的資料結構,用於記錄那些事務修改了這個資料塊的內容。
記錄頭ITL索引:每條記錄的記錄頭部有一個欄位,用於記錄ITL表項號,可以看做指向ITL表的指標
TX鎖,事務鎖
TM鎖:保護表或檢視定義不被修改的鎖
當一個事務開始時,必須申請一個TX鎖,這種鎖保護資源是回滾段、回滾段資料塊,因此這個這個申請意味著:使用者程式必須先申請到回滾段資源後才能開始一個事務,才能執行DML語句修改資料。
申請到回滾段資源後,使用者事務就可以開始修改資料了,事務資訊可在v$transaction中查到,在修改資料表的記錄時,需要遵守如下操作順序:
首先獲得這個表的TM鎖,這個鎖用於保護事務執行過程中其他使用者不能修改表結構;
事務修改某個資料塊記錄時,首先需要在改資料塊塊頭的ITL表中申請一個空閒表項,並在其中記錄事務號,實際就是在記錄這個事物要使用的回滾段地址;
事務修改該資料塊的某條記錄時,會設定該記錄頭部的ITL索引指向上一步申請到的表項,然後再修改記錄內容,修改前先在回滾段對記錄修改前的狀態做一個拷
貝,然後才能修改資料記錄,這個拷貝用於以後的回滾、恢復和一致性讀。當其他使用者併發修改這條記錄時,會根據記錄頭的ITL索引讀取ITL表項內容,檢視
這個事務是否已經提交,如果沒有提交,則這個使用者的TX鎖會等待前一個使用者的TX鎖的釋放。
例如如下式轉儲的一個資料塊的ITL資訊:
Block header dump:  0×00411819
Object id on Block? Y
seg/obj: 0×10396  csc: 0×00.d62e7  itc: 2  flg: O  typ: 1 – DATA
fsl: 0  fnx: 0×0 ver: 0×01
Itl           Xid                  Uba         Flag  Lck        Scn/Fsc
0×01   0×0008.00b.0000029b  0x00c05271.006a.3c  —-    3  fsc 0×0000.00000000
0×02   0×0000.000.00000000  0×00000000.0000.00  —-    0  fsc 0×0000.00000000
seg/obj:seg/obj id
csc:clean scn
itc:itl slots的個數,此時多少個事務在對本data block進行操作
flg: 0=on the freelist
typ:資料塊型別
fsl: itl tx freelist slot
fnx: dba of next block on freelist
Itl:interested transaction list index
Xid:transaction id
Uba:undo address
Flag:事務狀態標誌
Lck:事物所影響行的數量
oracle
在對資料行鎖定時,行指向事務ID的一個副本,事務ID儲存在包含資料的塊中,釋放鎖時,事務ID會儲存下來,這個事務ID時事務特有的,表示了回滾段
號、槽和序列號,事務ID留在包含資料行的塊中,可以告訴其他會話:一個會話擁有這個資料行。另一個會話會看到鎖ID,由於鎖ID表示一個事務,所以可以
很快的檢視持有這個鎖的事務是否活動的。如果鎖不活動的,則允許會話訪問這個資料,如果鎖還是活動的,會話會要求一旦釋放鎖就得到通知。所以這需要一個排
隊機制:請求鎖的會話會排隊,等待目前擁有這個鎖的事務執行,然後的到這個資料。可以根據v$lock檢視的lmode和request
mode判斷誰是owner、waiter和converter
owner:lomode>0,request=0
waiter:lmode=0,request>0
converter:lmode>0,request>0
例如下試驗可以清楚看到這些資訊:
系統已更改。
SQL> create table t1 ( x int );
表已建立。
SQL> create table t2 ( x int );
表已建立。
SQL> insert into t1 values ( 1 );
已建立 1 行。
SQL> insert into t2 values ( 1 );
已建立 1 行。
SQL> select (select username
2                 from v$session
3                 where sid = v$lock.sid) username,
4         sid,
5         id1,
6         id2,
7         lmode,
8         request, block, v$lock.type
9    from v$lock
10   where sid = (select sid
11                  from v$mystat
12                 where rownum=1)
13  /
USERNAME  SID   ID1   ID2   LMODE  REQUEST    BLOCK    TYPE
——– —–  —-   —-  —–   ——-   —–    —-
SYS       13    66455   0     3        0         0      TM
SYS       13    66456   0     3        0         0      TM
SYS       13    589840  662   6        0         0      TX
SQL> select object_name, object_id
2    from user_objects
3   where object_name in (‘T1′,’T2′)
4  /
OBJECT_NAME    OBJECT_ID
————  ———
T1            66455
T2            66456
每個事務只能有一個TX鎖,但是TM鎖依照修改的物件個數而定,TM對應的ID1列就是DML鎖定物件ID.
SQL> select username,
2         v$lock.sid,
3         trunc(id1/power(2,16)) rbs,
4         bitand(id1,to_number(‘ffff’,`xxxx’))+0 slot,
5         id2 seq,
6         lmode,
7         request
8  from v$lock, v$session
9  where v$lock.type = ‘TX’
10    and v$lock.sid = v$session.sid
11    and v$session.username = USER;
USERNAME  SID    RBS    SLOT   SEQ   LMODE  REQUEST
——– —– ——  —–  —— —— ———
SYS       13     9      16      662       6     0
SQL> select XIDUSN, XIDSLOT, XIDSQN  from v$transaction;
XIDUSN    XIDSLOT     XIDSQN
—— ———- ———-
9         16        662
oracle事務不同於其他資料庫之處,不需要專門語句顯示開始事務,事務會在修改資料的第一條語句處開始,但是一定要用commit或rollback事務。
oracle的commit做了如下操作:
為事務生成一個SCN
LGWR將所有餘下的快取重做日誌條目寫至磁碟,並把SCN記錄到線上重做日誌檔案中
v$lock中記錄著會話持有的鎖,這些鎖將被釋放,而排隊等待這些鎖的每一個佇列都會被喚醒
如果事務處理的某些塊還在快取中,則會快速的模式訪問並清除
Itl           Xid                  Uba         Flag  Lck        Scn/Fsc
0×01   0×0008.00b.0000029b  0x00c05271.006a.3c  C—    0  scn 0×0000.000d6470
0×02   0×0005.007.00000372  0x00c0dbca.006e.2f  –U-    1  fsc 0×0000.000d6584
如上的flag列,第一條ITL資訊顯示資料塊當前事務資訊已經被清除,第二個還未清除事務ITL資訊標誌為U;
oracle併發支援,實現了一種多版本體系,能夠同時物化多個版本的資料,能夠提供讀一致性機制,資料讀取器絕不會被寫入器所阻塞,也就是寫不會阻塞讀。一種情況例外,那就是在分散式事務處理(2PC)期間。
另外,記住大多數DDL都帶排它鎖,有些DDL沒有DDL鎖,如create index idx on t(x)
online;online關鍵字會改變建立索引的方法。oracle只會得到表上的TM鎖,防止其他DDL發生,但是執行DML執行。oracle發生
死鎖的原因外來鍵未加索引、點陣圖索引發生更新,外來鍵未加索引更新或刪除父表都會對整個子表加鎖
會話1:
create table p ( x int primary key );
create table c ( x references p );
insert into p values ( 1 );
insert into p values ( 2 );
commit;
insert into c values ( 2 );
會話2:
delete from p where x = 1;;
這個時候機會發生阻塞:
SQL> select
2        (select username from v$session where sid=a.sid) blocker,
3         a.sid,
4        ‘ is blocking ‘,
5         (select username from v$session where sid=b.sid) blockee,
6             b.sid
7    from v$lock a, v$lock b
8   where a.block = 1
9     and b.request > 0
10     and a.id1 = b.id1
11     and a.id2 = b.id2;
BLOCKER    SID ‘ISBLOCKING’  BLOCKEE     SID
——-  —–  ———   ——— ——-
SYS       142  is blocking  SYS          13
不需要對外來鍵加索引的情況:
1、沒有從父表刪除行
2、沒有更新父表的唯一鍵/主鍵值
3、沒有從父表聯結子表
參考:
TOM oracle 9i&10g程式設計藝術
摘自我的blog:ww.manotes.net