資料來自官方文件:
https://docs.oracle.com/database/121/CNCPT/consist.htm#CNCPT1333
https://docs.oracle.com/database/121/SQLRF/statements_9016.htm#SQLRF01605
latch主要內容來自:https://blog.csdn.net/yangshangwei/article/details/53725617
本章節主要介紹的oracle鎖總主要包含兩種型別的鎖:
1.enquences 佇列型別的鎖,通常跟業務相關的鎖(本章主要討論的就是這種鎖)
2.latches 系統鎖,比如記憶體使用,sql解析方面的問題。
實驗環境oracle版本都是19.3
1.為什麼會有鎖?
為了資料併發與一致性
在單使用者資料庫中,一個使用者可以修改資料而不必擔心其他使用者同時修改相同的資料。但是,在多使用者資料庫中,多個併發事務中的語句可能會更新相同的資料。同時執行的事務必須產生有意義且一致的結果。
多使用者資料庫必須提供以下內容:
為了在事務併發執行時描述一致的事務行為,資料庫研究人員定義了一個稱為可序列化的事務隔離模型。可序列化事務在一種環境中執行,使其看起來好像沒有其他使用者正在修改資料庫中的資料。
雖然事務之間的這種隔離程度通常是可取的,但在可序列化模式下執行許多應用程式會嚴重影響應用程式吞吐量。併發執行事務的完全隔離可能意味著一個事務不能執行插入到另一個事務正在查詢的表中。簡而言之,現實世界的考慮通常需要在完美的事務隔離和效能之間進行折衷。
Oracle 資料庫通過使用多版本一致性模型和各種型別的鎖和事務來維護資料一致性。通過這種方式,資料庫可以向多個併發使用者呈現資料檢視,每個檢視都與一個時間點保持一致。由於不同版本的資料塊可以同時存在,事務可以讀取查詢所需時間點提交的資料版本,並返回與單個時間點一致的結果。
2.Oracle 資料庫鎖定機制概述
鎖是一種防止破壞性互動的機制,破壞性互動是在訪問共享資料的事務之間錯誤地更新資料或錯誤地改變底層資料結構的互動。鎖在維護資料庫併發性和一致性方面起著至關重要的作用。
2.1鎖定行為總結
資料庫維護幾種不同型別的鎖,具體取決於獲取鎖的操作。
一般來說,資料庫使用兩種型別的鎖:排他鎖和共享鎖。一個資源(例如行或表)只能獲得一個排他鎖,但在單個資源上可以獲得多個共享鎖。
鎖會影響讀者和作者的互動。以下規則總結了 Oracle 資料庫對讀寫器的鎖定行為:
一行只有在被修改時才被鎖定。
當語句更新一行時,事務只為這一行獲取鎖。通過在行級別鎖定表資料,資料庫最大限度地減少了對相同資料的爭用。在正常情況下,資料庫不會將行鎖升級到塊級或表級。
一行的寫入阻塞同一行的併發寫入者。
如果一個事務正在修改一行,那麼行鎖可以防止不同的事務同時修改同一行。
讀取永遠不會阻塞寫。
因為一行的讀取不會鎖定它,所以寫入可以修改該行。唯一的例外是
SELECT ... FOR UPDATE
語句,它是一種特殊型別的SELECT
語句,它會鎖定正在讀取的行。寫入永遠不會阻塞讀。
當寫入更改一行時,資料庫使用undo資料為讀提供該行的一致檢視(即利用回滾段一致性讀)。
值得注意的是:分散式事務的非常特殊的情況下,資料的讀取可能不得不等待相同資料塊的寫入。
2.2鎖的使用
在單使用者資料庫中,不需要鎖定,因為只有一個使用者在修改資訊。但是,當多個使用者訪問和修改資料時,資料庫必須提供一種防止對同一資料進行併發修改的方法。
鎖實現了以下重要的資料庫要求:
一致性
在使用者完成提交之前,會話正在檢視或更改的資料不得被其他會話更改。
完整性
資料和結構必須以正確的順序反映對它們所做的所有更改。
Oracle 資料庫通過其鎖定機制在事務之間提供資料併發性、一致性和完整性。鎖定自動發生,無需使用者操作。
請感受下面這個案例:
sid為197的建立一張表t,且插入一條記錄,不提交這條記錄。
sid為205的插入同樣的一條記錄。
- set lin 200 pages 200
- select distinct sid from v$mystat;
- select distinct sid,id1,id2,addr,type,lmode,block from v$lock where type in ('TX','TM') ORDER BY 1,2;
select sid,seq#,event FROM v$session_wait where sid in (197,205);
常見的TM表級鎖,ID1表示被鎖定的物件的OBJECT_ID,ID2此時為0;對於 TX 鎖,這兩個欄位構成了事務在回滾段中的位置。
當鎖型別為TX事務鎖時ID1和ID2的含義如下:
ID1對應檢視V$TRANSACTION中的XIDUSN欄位和XIDSLOT欄位。其中ID1的高16位為XIDUSN,低16位為XIDSLOT。
ID2對應檢視V$TRANSACTION中的XIDSQN欄位。
select OBJECT_ID,owner,OBJECT_NAME,OBJECT_TYPE from dba_objects where OBJECT_ID in (107036);
可以檢視的是sid197,205的首先加了一個表級鎖,sid為205會話被sid197的阻塞,TX行級鎖;後面會詳細介紹。
例如檢視TX,AE,TM鎖型別:
- select distinct type,name,DESCRIPTION from v$lock_type a where a.TYPE in ('TX','AE','TM');
Oracle 資料庫在執行 SQL 語句時會自動獲取必要的鎖。例如,在資料庫允許會話修改資料之前,會話必須先鎖定資料。鎖賦予會話對資料的獨佔控制權,因此在釋放鎖之前沒有其他事務可以修改鎖定的資料。
LMODE的數字0-6代表的是:
0
- none 沒有鎖1
- null (NULL) 空2
- row-S (SS)行共享 (RS)3
- row-X (SX)也叫行級排他鎖 (RX)4
- share (S)共享表鎖 (S)5
- S/Row-X也叫共享行級排他鎖SRX)6
- exclusive (X)表級排他鎖 (X) alter table, drop table, drop index, truncate table操作等
2.3鎖定模式
Oracle 資料庫自動使用最低的適用的限制級別以提供最高程度的資料併發性,同時還提供故障安全資料完整性。級別限制越少,其他使用者訪問的資料就越多。相反,級別越嚴格,其他事務在它們可以獲取的鎖型別方面就越有限。
Oracle 資料庫在多使用者資料庫中使用兩種鎖定模式:
獨佔鎖定模式
此模式可防止共享關聯的資源。事務在修改資料時獲得排他鎖。第一個獨佔鎖定資源的事務是唯一可以更改資源的事務,直到獨佔鎖被釋放。
共享鎖定模式
此模式允許共享關聯的資源,具體取決於所涉及的操作。多個讀取資料的使用者可以共享資料,每個使用者都持有一個共享鎖,以防止需要排他鎖的寫入者進行併發訪問。多個事務可以在同一資源上獲取共享鎖。
假設事務使用SELECT ...
FOR
UPDATE
語句來選擇單個錶行。事務獲取排他行鎖和行共享表鎖。行鎖允許其他會話修改鎖定行以外的任何行,而表鎖防止會話更改表的結構。因此,資料庫允許執行儘可能多的語句。
2.3鎖轉換和升級
Oracle 資料庫執行鎖轉換有必要的。在鎖轉換中,資料庫會自動將限制性較低的表鎖轉換為限制性較高的表鎖。
例如,假設一個事務SELECT ...
FOR
UPDATE
,然後更新了鎖定的行。在這種情況下,資料庫會自動將行共享表鎖轉換為行排他表鎖。事務為事務中插入、更新或刪除的所有行持有排它行鎖。因為行鎖是在最高級別的限制下獲取的,所以不需要或執行鎖轉換。
鎖轉換與鎖升級不同,鎖升級是在一個粒度級別(例如,行)持有大量鎖並且資料庫將鎖提升到更高粒度級別(例如,表)時發生的。如果使用者鎖定一個表中的許多行,那麼某些資料庫會自動將行鎖定升級到單個表。鎖的數量減少了,但鎖定的限制增加了。
Oracle 資料庫從不升級鎖。鎖升級大大增加了死鎖的可能性。假設系統試圖代表事務 1 升級鎖,但由於事務 2 持有鎖而無法升級。如果事務 2 還需要對相同資料進行鎖升級才能繼續進行,則會建立死鎖。
select distinct sid,id1,id2,addr,type,lmode,block from v$lock where type in ('TX','TM') ORDER BY 1,2;
select OBJECT_ID,owner,OBJECT_NAME,OBJECT_TYPE from dba_objects where OBJECT_ID in (107036);
可以看到的是,sid為35首先發出SELECT ...
FOR
UPDATE
語句,資料庫會自動將共享表鎖轉換為表級排他鎖 LMOD為6和行排他表鎖LMOD為3;sid的會話一直在等待資源釋放。
2.4鎖定持續時間
當某些事件發生時,Oracle 資料庫會自動釋放鎖,以便事務不再需要該資源。通常,資料庫在事務期間持有事務內語句獲取的鎖。這些鎖可防止併發事務中的破壞性干擾,例如髒讀、更新丟失和破壞性DDL。
由於未使用索引的外來鍵而對子表採取的表鎖定在語句期間保持,而不是在事務期間保持。此外,如“使用者定義鎖概述”中所述,該DBMS_LOCK
包使使用者定義的鎖能夠隨意釋放和分配,甚至可以跨越事務邊界。
Oracle 資料庫在提交或回滾時釋放事務中的語句獲取的所有鎖。當回滾到savepoint時,Oracle 資料庫還會釋放在savepoint之後獲取的鎖。但是,只有不等待先前鎖定資源的事務才能獲取對現在可用資源的鎖定。等待事務繼續等待,直到原始事務完全提交或回滾。
2.5死鎖
死鎖是其中兩個或更多使用者正在等待由相互鎖定的資料的情況。死鎖會阻止某些事務繼續工作。
Oracle 資料庫自動檢測死鎖並通過回滾死鎖中涉及的一條語句來解決死鎖,釋放一組衝突的行鎖。資料庫向進行語句級回滾的事務返回相應的訊息。回滾的語句屬於檢測到死鎖的事務。通常,發出訊號的事務應該顯式回滾,但它可以在等待後重試回滾語句。
3.鎖的分類
Oracle 資料庫代表事務自動鎖定資源,以防止其他事務執行需要對同一資源進行獨佔訪問的操作。資料庫根據資源和正在執行的操作自動獲取不同級別限制的不同型別的鎖。
執行簡單讀取時,資料庫從不鎖定行。
官方分為以下三類,沒有找到官方有樂觀,悲觀的分類(有的關,可以留言提示我),雖然看到網路上有很多這種分類的,官方有提到顯示,當然對立的就是隱示了。
自動鎖
手動鎖
使用者自定義鎖
3.1 自動鎖
一個DML鎖,也稱為資料鎖定,保證多個使用者同時訪問資料的完整性。例如,DML 鎖可防止兩個客戶購買線上書商提供的最後一本圖書。DML 鎖可防止同時發生衝突的 DML 或 DDL 操作的破壞性干擾。
DML 語句自動獲取以下型別的鎖:
行鎖 (TX)
表鎖 (TM)
3.1.1.1行鎖 (TX)
一個行鎖,也稱為TX鎖,上表中的單個行的鎖。事務獲取用於通過修改的每個行的行鎖INSERT
,UPDATE
,DELETE
,MERGE
,或SELECT
... FOR
UPDATE
語句。行鎖一直存在,直到事務提交或回滾。
行鎖主要用作一種排隊機制,以防止兩個事務修改同一行。資料庫始終以獨佔模式鎖定已修改的行,以便其他事務在持有鎖的事務提交或回滾之前無法修改該行。行鎖提供了最好的粒度鎖,因此提供了最好的併發性和吞吐量。
如果事務因資料庫例項故障而終止,則塊級恢復會在整個事務恢復之前使一行可用。
如果一個事務獲得了某行的鎖,那麼該事務也會為包含該行的表獲得一個鎖。表鎖可防止衝突的 DDL 操作覆蓋當前事務中的資料更改。下圖說明了表中第三行的更新。Oracle 資料庫自動在更新的行上放置一個排他鎖,並在表上放置一個子排他鎖。
修改employees這張表的employee_id 為100的這行, 會有一個表鎖和行鎖,這就是為啥SELECT ...
FOR
UPDATE
語句,據庫會自動將共享表鎖轉換為獨佔表鎖 LMOD為6和行排他表鎖LMOD為3;一直等提交或者回滾資源才釋放。
行鎖和併發
這個場景說明了 Oracle 資料庫如何使用行鎖來實現併發。每個會話看到自己的未提交更新,但看不到任何其他會話的未提交更新。
行鎖的儲存
與某些使用鎖管理器在記憶體中維護鎖列表的資料庫不同,Oracle 資料庫將鎖資訊儲存在包含鎖定行的資料塊
資料庫使用佇列機制來獲取行鎖。如果事務需要鎖定未鎖定的行,則事務會在資料塊中放置一個鎖。此事務修改的每一行都指向儲存在塊頭。
當交易結束時,交易 ID 保留在區塊頭中。如果不同的事務想要修改一行,則它使用事務 ID 來確定鎖是否處於活動狀態。如果鎖處於活動狀態,則會話要求在釋放鎖時得到通知。否則,事務獲取鎖。
3.1.1.2表鎖 (TM)
表鎖,也稱作TM鎖,通過一個事務被獲得當一個表INSERT
,UPDATE
,DELETE
,MERGE
,SELECT
與FOR
UPDATE
子句或LOCK
TABLE
語句。DML 操作需要表鎖來代表事務保留對錶的 DML 訪問,並防止與事務衝突的 DDL 操作。
表鎖包括以下型別:
2-行共享 (RS) --行級共享鎖
此鎖,也稱為子共享表鎖 (SS),表示持有該表鎖的事務已鎖定表中的行並打算更新它們。行共享鎖是限制最少的表鎖模式,可為表提供最高程度的併發性。SS,行級共享鎖,其他物件只能查詢這些資料行,sql操作有select for update、lock for update、lock row share;
3-行獨佔表鎖 (RX)--行級排他鎖
此鎖,也稱為子排他表鎖 (SX),通常表示持有該鎖的事務已更新錶行或已發出
SELECT ... FOR UPDATE
。SX 鎖允許其他事務在同一表中同時查詢、插入、更新、刪除或鎖定行。因此,SX 鎖允許多個事務同時獲取同一表的 SX 和子共享表鎖。行級排他鎖,commit前不允許DML操作,sql操作有insert、update、delete、lock row share;4-共享表鎖 (S)--共享鎖
SELECT ... FOR UPDATE
事務持有的共享表鎖允許其他事務查詢該表,但僅當單個事務持有共享表鎖時才允許更新。因為多個事務可能同時持有一個共享表鎖,持有這個鎖不足以保證一個事務可以修改表,sql操作有建立索引,鎖共享。5-共享行獨佔表鎖 (SRX)--共享行級排他鎖
此鎖也稱為共享子排他表鎖 (SSX),比共享表鎖限制更多。一次只有一個事務可以獲取給定表上的 SSX 鎖。事務持有的 SSX 鎖允許其他事務查詢表(除了
SELECT ...
FOR UPDATE
)但不允許更新表;sql操作有鎖共享行排他。6-獨佔表鎖--表級排他鎖 (X)
這個鎖是最嚴格的,禁止其他事務執行任何型別的 DML 語句或在表上放置任何型別的鎖,alter table, drop table, drop index, truncate table,等DDL操作。
v$lock對應的LMODE列的數字0-6代表的是:
0
- none 沒有鎖1
- null (NULL) 空2
- row-S (SS)行共享 (RS)3
- row-X (SX)也叫行級排他鎖 (RX)4
- share (S)共享表鎖 (S)5
- S/Row-X也叫共享行級排他鎖 (SRX)6
- exclusive (X)表級排他鎖 (X)
鎖和外來鍵(RI)
Oracle 資料庫最大限度地提高了父鍵相對於從屬外來鍵的併發控制。
鎖定行為取決於外來鍵列是否有索引。如果外來鍵沒有索引,那麼子表可能會更頻繁地被鎖定,會發生死鎖,併發度會降低。出於這個原因,外來鍵應該有索引。唯一的例外是匹配的唯一鍵或主鍵從未更新或刪除。
鎖和無索引的外來鍵
當子表的外來鍵列上不存在索引時,資料庫在子表上獲取全表鎖,並且會話修改父表中的主鍵(例如刪除一行或修改主鍵屬性)或將行合併到父表中。
當以下兩個條件都為真時,資料庫在子表上獲取全表鎖:
子表的外來鍵列上不存在索引。
會話修改父表中的主鍵(例如,刪除一行或修改主鍵屬性)或將行合併到父表中。
插入到父表中不會獲取阻止子表 DML 的阻塞表鎖。在插入的情況下,資料庫獲取子表上的鎖,以防止結構更改,但不會修改現有或新新增的行。
官方給的圖如下:
資料庫employees
在部門 60 的主鍵修改期間獲得了一個全表鎖。這個鎖使其他會話可以查詢但不能更新employees
表。例如,會話無法更新員工電話號碼。表上employees
的主鍵修改departments
完成後立即釋放表鎖。如果 中的多行departments
進行主鍵修改,employees
則為 中修改的每一行獲取並釋放一次表鎖departments
。
子表上的 DML 不會獲取父表上的表鎖。
請看測試:
- select distinct sid from v$mystat;
- create table ph_1(id int primary key);
- create table st_1(id int, constraint fk_st_1 foreign key (id) references ph_1(id));
insert into ph_1 select rownum from dual connect by rownum <=10;
commit;
update ph_1 set id=11 where id=1; --不提交
select distinct sid from v$mystat;
select distinct sid,id1,id2,addr,type,lmode,block from v$lock where type in ('TX','TM') ORDER BY 1,2;
insert into st_1 values(2);
update st_1 set id=3 where id=2; --不提交
select distinct sid from v$mystat;
select distinct sid from v$mystat;
select distinct sid,id1,id2,addr,type,lmode,block from v$lock where type in ('TX','TM') ORDER BY 1,2;
select OBJECT_ID,owner,OBJECT_NAME,OBJECT_TYPE from dba_objects where OBJECT_ID in (107257,107259);
以上操作可以看出,當子表外來鍵沒有索引的時候,主表update操作,子表無表鎖;但是當子表進行update操作時,主表有表鎖。
鎖和有索引的外來鍵(RI)
如果子表指定ON DELETE CASCADE
,則從父表中刪除會導致從子表中刪除。例如,部門 280 的刪除可能導致employees
刪除部門中員工的記錄的刪除。在這種情況下,等待和鎖定規則與從父表中刪除行後從子表中刪除行一樣。
官方如下圖:
create index st_1_idx on st_1(id);
update ph_1 set id=222 where id=10; --不提交
select distinct sid from v$mystat;
select distinct sid,id1,id2,addr,type,lmode,block from v$lock where type in ('TX','TM') ORDER BY 1,2;
提交後,子表進行操作;
以上操作可以看出,當子表外來鍵有索引的時候,主表update操作,子表有表鎖;但是當子表進行update操作時,主表也有表鎖。
3.1.2 DDL鎖
資料字典(DDL)鎖保護模式物件的定義,同時正在進行的DDL操作作用於或指物件。在 DDL 操作期間,只有被修改或引用的單個架構物件會被鎖定。資料庫從不鎖定整個資料字典。
Oracle 資料庫任何需要它的 DDL 事務自動獲取 DDL 鎖。使用者不能顯式請求 DDL 鎖。例如,如果使用者建立了一個儲存過程,那麼 Oracle 資料庫會自動為過程定義中引用的所有模式物件獲取 DDL 鎖。DDL 鎖可防止在過程編譯完成之前更改或刪除這些物件。
3.1.2.1獨佔 DDL 鎖
排他 DDL 鎖可防止其他會話獲得 DDL 或 DML 鎖。除了“共享 DDL 鎖”中描述的那些操作外,大多數 DDL 操作都需要資源的排他 DDL 鎖,以防止破壞性干擾其他可能修改或引用相同架構物件的 DDL 操作。例如,DROP TABLE
在向表ALTER TABLE
中新增列時不允許刪除表,反之亦然。
獨佔 DDL 鎖在 DDL 語句執行和自動提交期間持續。在獲取排它 DDL 鎖期間,如果另一個 DDL 鎖被另一個操作持有在架構物件上,則獲取將等待,直到較舊的 DDL 鎖被釋放,然後繼續進行。
3.1.2.2共享 DDL 鎖
資源的共享 DDL 鎖可防止對衝突 DDL 操作的破壞性干擾,但允許類似 DDL 操作的資料併發。
例如,當一個CREATE
PROCEDURE
語句執行時,包含的事務為所有引用的表獲取共享 DDL 鎖。其他事務可以併發地建立引用相同表的過程並獲取相同表上的併發共享 DDL 鎖,但沒有事務可以在任何引用的表上獲取排他 DDL 鎖。
共享 DDL 鎖在 DDL 語句執行和自動提交期間持續。因此,持有共享 DDL 鎖的事務可以保證所引用模式物件的定義在事務期間保持不變。
3.1.2.3可破壞的解析鎖
一個解析鎖是通過SQL語句或PL / SQL程式單元對每個方案物件,它引用舉行。獲取解析鎖,以便關聯如果引用的物件被更改或刪除,共享 SQL 區域可能會失效。解析鎖被稱為可破壞解析鎖,因為它不禁止任何 DDL 操作,並且可以被破壞以允許發生衝突的 DDL 操作。
解析鎖是在 SQL 語句執行解析階段的共享池。只要該語句的共享 SQL 區域保留在共享池中,就會保持鎖定。
3.1.3 系統鎖(閂)latch
一般也無層面是看不到的,資料庫使用各種型別的系統鎖來保護內部資料庫和記憶體結構。使用者無法訪問這些機制,因為使用者無法控制其發生或持續時間。
鎖存器
閂鎖是一個簡單的,保證序列化的機制,對共享資料結構,物件和檔案的多使用者訪問。
當多個程序訪問時,鎖存器保護共享記憶體資源不被破壞。具體來說,閂鎖保護資料結構免受以下情況的影響:
多個會話併發修改
被一個會話讀取同時被另一個會話修改
訪問時釋放(老化)記憶體
通常,單個閂鎖保護 SGA 中的多個物件。例如,DBW 和 LGWR 等後臺程序從共享池中分配記憶體以建立資料結構。為了分配這些記憶體,這些程序使用共享池鎖存器來序列化訪問以防止兩個程序同時嘗試檢查或修改共享池。分配記憶體後,其他程序可能需要訪問共享池區域,例如庫快取,這是解析所需的。在這種情況下,程序只鎖存庫快取,而不是整個共享池。
與行鎖等入隊閂鎖不同,閂鎖不允許會話排隊。當閂鎖可用時,請求閂鎖的第一個會話獲得對它的獨佔訪問。鎖存自旋現象發生在一個程序在迴圈中重複請求鎖存時,而鎖存休眠發生在一個程序在更新鎖存請求之前釋放 CPU 時。
通常,Oracle 程序在操作或檢視資料結構時會在極短的時間內獲取鎖存器。例如,在處理單個員工的工資更新時,資料庫可能會獲取並釋放數千個鎖存器。鎖存器的實現依賴於作業系統,尤其是在程序是否等待鎖存器以及等待多長時間方面。
鎖存的增加意味著併發性的減少。例如,過多的硬解析操作會產生對庫快取鎖存器的爭用。該V$LATCH
檢視包含每個閂鎖的詳細閂鎖使用統計資訊,包括請求和等待每個閂鎖的次數。
Latch的目的
保證對資源的序列訪問:
– 保護 SGA 資源訪問
– 保護記憶體分配
保證執行的序列化:
– 保護關鍵資源的序列執行
– 防止記憶體結構損壞
Latch 是保護資料庫本身的記憶體結構,而不是保護在業務層面。
latch 和enquence lock 對比:
latch一般發生在共享池(sql解析,sql重用),buffer cache(資料的修改,訪問,段擴充套件,資料讀入記憶體,資料塊的修改等)
檢視latch的檢視:
V$LATCH
V$LATCHHOLDER
V$LATCHNAME
3.1.3.1 latch原理
3.1.3.2 latch的獲取方式
以等待方式分為兩種:
3.1.3.3 latch的資源爭用
1.shared pool的latch 爭用,繫結變數
下面測試案例:
drop table t ;
create table t as select * from dba_objects;
update t set object_id=rownum;
commit;
--沒有做繫結變數
create or replace procedure ps1 as
l_cnt number;
begin
for i in 1 .. 20000 loop
execute immediate ' select count(*) from t where object_id = ' || i into l_cnt;
end loop;
end;
/
exec ps1;
select SID,EVENT from v$session_wait where wait_class <> 'Idle';
生產上遇到這種,進行sql語句的改寫,將複雜的SQL繫結變數語句拆解成多個簡單的使用繫結變數的sql語句;或者SQL加入進行繫結變數,當然olap環境就不需要加了。
2.buffer cache latch熱塊爭用
檢視造成 LATCH BUFFER CACHE CHAINS 等待事件的熱塊
X$bh 表是資料庫頭,oracle資料庫執行的基礎,在資料庫啟動時由Oracle應用程式動態建立,是不允許sysdba之外的使用者直接訪問的,顯示授權不被允許。
一下是都去訪問同一個資料塊,引起熱快(記憶體不夠大)。
訪問頻率非常高的資料塊被稱為熱塊( Hot Block),當很多使用者一起去訪問某幾個資料塊時,就會導致一些 Latch 爭用
最常見的 latch 爭用有:
- buffer busy waits
- cache buffer chain
Buffer busy waits 產生原因
當一個會話需要訪問一個數據塊,而這個資料塊正在被另一個使用者從磁碟讀取到記憶體中或者這個資料塊正在被另一個會話修改時,當前的會話就需要等待,就會產生一個 buffer busy waits 等待。產生這些 Latch 爭用的直接原因是太多的會話去訪問相同的資料塊導致熱塊問題, 造成熱塊的原因可能是資料庫設定導致或者重複執行的 SQL 頻繁訪問一些相同的資料塊導致。
Cache buffer chian 產生原因
當一個會話需要去訪問一個記憶體塊時,它首先要去一個像連結串列一樣的結構中去搜索這個資料塊是否在記憶體中,當會話訪問這個連結串列的時候需要獲得一個Latch,如果獲取失敗,將會產生 Latch cache buffer chain 等待,導致這個等待的原因是訪問相同的資料塊的會話太多或者這個列表太長(如果讀到記憶體中的資料太多,需要管理資料塊的 hash 列表就會很長,這樣會話掃描列表的時間就會增加,持有 chache buffer chain latch 的時間就會變長,其他會話獲得這個 Latch 的機會就會降低,等待就會增加)。
( 1) 表資料塊
( 2) 索引資料塊
( 3) 索引根資料塊
( 4) 段頭資料塊
熱塊產生的原因
熱塊產生的原因不盡相同,按照資料塊的型別,可以分成一下幾種型別,不同熱塊型別處理的方式都是不同的。
表資料塊
比如在 OLTP 系統中,對於一些小表,會給出某些資料塊被頻繁查詢或者修改的操作,這時候,這些被頻繁訪問的資料塊就會變成熱塊,導致記憶體中 Latch爭用。
處理方式:
如果出現這樣熱塊的情況,並且表不太大,一個方法是可以考慮將表資料分佈在更多的資料塊上,減少資料塊被多數會話同時訪問的頻率。
可以通過一下命令將每個資料塊存放記錄的數量減少到最少:
Alter table tableName minimize records_per_block;
功能:當前表所有block中容納的最大行數,並會把這個數字記錄到資料字典,以後任何導致block行數超過這個數字的插入都會被拒絕
缺點:
我們把資料分佈到更多的資料塊上,大大降低了一個數據塊被重複讀取的概率。 但是這種方法的缺點很明顯,就是降低了資料的效能,在這種情況下,訪問相同的資料意味著需要讀取更多的資料塊,效能會有所降低。
索引資料塊
這樣的情況通常發生在一個 RAC 架構裡,某個表的索引鍵值出現典型的“右傾”現象,
比如一個表的主鍵使用一個序列來生成鍵值,那麼在這個主鍵在索引資料塊上的鍵值就是以一種順序遞增的方式排列的,比如: 1, 2, 3, 4,5….,由於這些鍵值分佈得非常接近,當許多使用者在 RAC 的不同例項來向表中插入主鍵時,就會出現相同的索引資料塊在不同例項的記憶體中被呼叫,形成一種資料塊的爭用。
對於這種情況,使用反向索引可以緩解這種爭用。
反向索引是將從前的索引鍵值按照反向的方式排列,在正常的主鍵 B-Tree 引中,鍵值會按照大小的順序排列,比如: 1234,反向索引,鍵值就變成 4321.
原理:這樣,本來是放在相同的索引資料塊上的鍵值,現在分佈在不同的資料塊上,這樣使用者在 RAC 不同的例項上插入的主鍵值因為分佈在不同的資料塊上,所以不會導致熱塊的產生,這基本是反向索引被使用的唯一情況。
反向索引使用場合之所以如此受限制,是因為它丟棄了 B-Tree 索引的一個最重要的功能:
Index range scan
索引訪問方式中,這個方式最常見,但是反向索引卻不能使用這個功能,因為反向索引已經把鍵值的排列順序打亂,當按照鍵值順序查詢一個範圍時,在反向索引中,由於鍵值被反向儲存,這些值已經不是連續存放的了。 所以 Index range scan 的方式在反向索引中沒有任何意義。 在反向索引中只能通過全表掃描或者全索引掃描的方式來實現。 這就是反向索引的一個非常嚴重的缺陷。
索引根資料塊
熱塊也可能發生在索引的根資料塊上。
在 B-Tree 索引裡,當 Oracle 訪問一個索引鍵值時,首先訪問索引的根,然後是索引的分支,最後才是索引的葉塊。
索引的鍵值就是儲存在葉塊上面。
當索引的根,枝資料都集中在幾個資料塊上時,比如 D, G 所在的枝資料塊,
當用戶訪問的範圍從 A-F,都會訪問這個資料塊,如果很多使用者頻繁的訪問這個範圍的索引鍵值,有可能導致這個枝資料塊變成熱塊。
當出現這種現象時,可以考慮對索引做分割槽,以便於使用這些根,枝資料塊分佈到不同的資料段(分割槽)上,減少資料塊的並行訪問的密度,從而避免由於索引根,枝資料塊太集中導致熱塊產生。
段頭資料塊(修改)
從 Oracle 9i 開始,引入了一個自動段管理的技術 ASSM( Automatic SegmentSpace Management: ASSM),它讓 Oracle 自動管理“ Free List”。 實際上在 ASSM裡,已經沒有 Free List 這樣的結構, Oracle 使用點陣圖方式來標記資料塊是否可用,這種資料塊的空間管理方式比用一個列表來管理效率更高。
對於 OLTP 系統,表的 DML 操作非常密集,對於這些表,使用 ASSM 方式來管理會比人工管理更加方便和準確,能有效的避免段頭變成熱塊。
對於 OLAP 系統,這個引數並沒有太大的實際意義,因為在這樣的資料庫中,很少有表發生頻繁的修改, OLAP 系統主要的工作是報表和海量資料的批量載入。
請看下面一個測試:
- create table latch_table1 as select * from dba_objects;
- create or replace procedure p1 as
- l number;
- begin
- for i in 1 .. 20000 loop
- select count(*) into l from latch_table1 where object_type = 'TABLE';
- end loop;
- dbms_output.put_line('successfully');
- end;
- /
- select distinct sid from v$mystat;
- exec p1;
- select distinct sid from v$mystat;
- exec p1;
- select distinct sid from v$mystat;
- select * from v$latchholder;
- select SID,EVENT from V$SESSION_WAIT where wait_class <> 'Idle';
由於我的版本是19.3,所以是cache buffer chain,生產上我遇到過類似的latch等待事件,此種等待事件最好從業務規避,進行序列化,避免業務引起的資源爭用。
3.1.3.4 latch 優化
3.2 手動鎖
官方的文件翻譯如下:
Oracle 資料庫自動執行鎖定以確保資料併發性、資料完整性和語句級讀取一致性。但是,可以手動覆蓋 Oracle 資料庫預設鎖定機制。在以下情況下,覆蓋預設鎖定很有用:
應用程式需要事務級讀取一致性或可重複讀取。
在這種情況下,查詢必須在事務期間生成一致的資料,而不是反映其他事務的更改。可以通過使用顯式鎖定、只讀事務、可序列化事務或覆蓋預設鎖定來實現事務級讀取一致性。
應用程式要求事務具有對資源的獨佔訪問許可權,以便事務不必等待其他事務完成。可以覆蓋 Oracle 資料庫在會話或事務級別自動鎖定。在會話級別,會話可以使用
ALTER
SESSION
語句設定所需的事務隔離級別。在事務級別,包含以下 SQL 語句的事務會覆蓋 Oracle 資料庫預設鎖定:
SET
TRANSACTION
ISOLATION
LEVEL
宣告LOCK
TABLE
(其鎖定或者一個表或者,享有使用時,基表)語句SELECT
...
FOR
UPDATE
宣告
前面語句獲取的鎖在事務結束或回滾到儲存點後釋放。
如果在任何級別覆蓋 Oracle 資料庫預設鎖定,則資料庫管理員或應用程式開發人員應確保覆蓋鎖定過程正確執行。鎖定過程必須滿足以下條件:保證資料完整性,可接受資料併發,不可能發生死鎖或已適當處理死鎖。
而實際的測試如下:
3.2.1 row share mode為2的測試
實驗如下:
create table t as select * from dba_objects;
update t set object_id=rownum;
commit;
lock table t in row share mode;
select distinct sid from v$mystat;
select distinct sid from v$mystat;
select distinct sid,id1,id2,addr,type,lmode,block from v$lock where type in ('TX','TM') ORDER BY 1,2;
select OBJECT_ID,owner,OBJECT_NAME,OBJECT_TYPE from dba_objects where OBJECT_ID in (107264);
update t set object_id=3 where object_id=2;
insert into t select * from t where object_id=1;
select distinct sid,id1,id2,addr,type,lmode,block from v$lock where type in ('TX','TM') ORDER BY 1,2
delete from t where object_id=3;
commit;
在row share mode模式下,可以進行增刪改查,以及for update 操作。能不能進行DDL操作呢?
答案是不可以的。必須等待前面鎖釋放了才能。排斥的是獨佔表鎖 (X),6模式,這個鎖是最嚴格的,禁止其他事務執行任何型別的 DML 語句或在表上放置任何型別的鎖,即有了6就不會在有其他的鎖,有了其他的鎖就不會有6這個鎖。
3.2.2 row exclusive mode為3的測試
跟在row share mode模式下一樣,row exclusive mode可以進行增刪改查,以及for update 操作。
3.2.3 share mode為4 測試
經測試,即mode為4的share 模式,只能進行select 操作。
3.2.4 share row exclusive mode為5的模式
可以看到,進行非select 操作,都會等待鎖釋放。只有select(非for update) 操作才能進行。
3.2.5 exclusive mode為6的模式
可以看到,非select 操作,都會等待鎖釋放。只有select(非for update) 操作才能進行。
3.2.6總結:
從2-6是級別越來越嚴格的操作,所有模式都排斥6,因為6模式,這個鎖是最嚴格的,禁止其他事務執行任何型別的 DML語句或在表上放置任何型別的鎖,即有了6就不能再有其他的鎖,有了其他的鎖就不能再有6這個鎖。
3.3 使用者自定義鎖
使用 Oracle 資料庫鎖管理服務,可以為特定應用程式定義自己的鎖。例如,您可以建立一個鎖來序列化對檔案系統上的訊息日誌的訪問。由於保留使用者鎖與 Oracle 資料庫鎖相同,因此它具有所有 Oracle 資料庫鎖功能,包括死鎖檢測。使用者鎖永遠不會與 Oracle 資料庫鎖衝突,因為它們用字首UL
。
Oracle 資料庫鎖管理服務可通過DBMS_LOCK
包中的過程獲得。可以在 PL/SQL 塊中包含以下語句:
請求特定型別的鎖
給鎖一個唯一的名字,這個名字可以在同一個或另一個例項的另一個過程中識別
更改鎖型別
釋放鎖
4.鎖的檢視
4.1 select 鎖檢視
select 是沒有鎖的。
以最常見的TM表級鎖定為例,ID1表示被鎖定的物件的OBJECT_ID,ID2此時為“0”。
當所型別為TX事務鎖時ID1和ID2的含義如下:
ID1對應檢視V$TRANSACTION中的XIDUSN欄位和XIDSLOT欄位。其中ID1的高16位為XIDUSN,低16位為XIDSLOT。
ID2對應檢視V$TRANSACTION中的XIDSQN欄位。
select * from t where object_id=100 for update; --不提交
select distinct sid,id1,id2,addr,type,lmode,block from v$lock where type in ('TX','TM') ORDER BY 1,2
select OBJECT_ID,owner,OBJECT_NAME,OBJECT_TYPE from dba_objects where OBJECT_ID in (107264);
select XIDUSN,XIDSLOT,XIDSQN from V$TRANSACTION where XIDSQN=2452;
2*2^16+28= 13100 即正好通過XIDUSN*2^16+XIDSLOT=ID1(TX)
select …for update 會有一個TM錶行級排他鎖,TX的獨佔鎖。
4.2 delete 鎖檢視
同樣的也有一個TM錶行級排他鎖,TX的獨佔鎖。
4.3 update鎖檢視
同樣的也有一個TM錶行級排他鎖,TX的獨佔鎖。
4.2 insert 鎖檢視
可以發現的是,除了單純的select沒有鎖,其餘的包括for update DML操作,都是驚人的TM錶行級排他鎖,TX的獨佔鎖。