oracle的鎖與併發機制

鎖是併發訪問的時候用於保護不共享資源不被同時併發修改的機制。
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: 0x10396  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
0x01   0×0008.00b.0000029b  0x00c05271.006a.3c  —-    3  fsc 0×0000.00000000
0x02   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
0x01   0×0008.00b.0000029b  0x00c05271.006a.3c  C—    0  scn 0×0000.000d6470
0x02   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編程藝術

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章