oracle-數據庫的各種-鎖-詳解
數據庫是一個多用戶使用的共享資源。當多個用戶並發地存取數據時,在數據庫中就會產生多個事務同時存取同一數據的情況。若對並發操作不加控制就可能會讀取和存儲不正確的數據,破壞數據庫的一致性。
如果是單用戶的系統,那完全沒有必要這個鎖,就是因為有多用戶並發操作,我們為了確保資源的安全性(也就是Oracle的數據完整性和一致性)才引申出這個鎖出來。Oracle 利用其鎖機制來實現事務間的數據並發訪問及數據一致性。
加鎖是實現數據庫並發控制的一個非常重要的技術。當事務在對某個數據對象進行操作前,先向系統發出請求,對其加鎖。加鎖后事務就對該數據對象有了一定的控制,在該事務釋放鎖之前,其他的事務不能對此數據對象進行更新操作。
Oracle的鎖機制是一種輕量級的鎖定機制,不是通過構建鎖列表來進行數據的鎖定管理,而是直接將鎖作為數據塊的屬性,存儲在數據塊首部。
在 Oracle 數據庫中,它並不是對某個表加上鎖或者某幾行加上鎖, 鎖是以數據塊的一個屬性存在的。 也就是說, 每個數據塊本身就存儲着自己數據塊中數據的信息,這個地方叫 ITL( Interested Transaction List), 凡是在這個數據塊上有活動的事務,它的信息就會記錄在這里面供后續的操作查詢,一保證事務的一致性。
在oracle數據庫中,不存在真正意義上屬於某個對象或數據的鎖。oracle鎖的信息是數據塊的一個物理屬性,而不是邏輯上屬於某個表或某個行。
分類
按用戶和系統分可以分為自動鎖和顯示鎖
自動鎖( Automatic Locks)
當進行一項數據庫操作時,缺省情況下,系統自動為此數據庫操作獲得所有有必要的鎖。
自動鎖分為三種:
- DML 鎖
- DDL 鎖
- systemlocks。
顯示鎖( Manual Data Locks)
某些情況下,需要用戶顯示的鎖定數據庫操作要用到的數據,才能使數據庫操作執行得更好,顯示鎖是用戶為數據庫對象設定的。
按鎖級別分可以分為排它鎖和共享鎖
排他鎖(exclusive lock,即X鎖)和共享鎖(share lock,即S鎖)
排他鎖(exclusive lock,即X鎖)
事務設置排它鎖后,該事務單獨獲得此資源,另一事務不能在此事務提交之前獲得相同對象的共享鎖或排它鎖。
共享鎖(share lock,即S鎖)
共享鎖使一個事務對特定數據庫資源進行共享訪問——另一事務也可對此資源進行訪問或獲得相同共享鎖。
共享鎖為事務提供高並發性,但如拙劣的事務設計+共享鎖容易造成死鎖或數據更新丟失。
按操作分可以分為DML鎖、DLL鎖和System Locks
DML鎖
DML 鎖用於控制並發事務中的數據操縱,保證數據的一致性和完整性。
DML鎖主要用於保護並發情況下的數據完整性。
DML 語句能夠自動地獲得所需的表級鎖(TM)與行級(事務)鎖(TX)。
它又分為:
( 1) TM 鎖(表級鎖)
( 2) TX 鎖( 事務鎖或行級鎖)
當 Oracle 執行 DML 語句時,系統自動在所要操作的表上申請 TM 類型的鎖。當 TM 鎖獲得后,系統再自動申請 TX 類型的鎖,並將實際鎖定的數據行的鎖標志位進行置位。
這樣在事務加鎖前檢查 TX鎖相容性時就不用再逐行檢查鎖標志,而只需檢查 TM 鎖模式的相容性即可,大大提高了系統的效率。
在數據行上只有 X 鎖(排他鎖)。
在 Oracle 數據庫中,當一個事務首次發起一個 DML 語句時就獲得一個 TX 鎖,該鎖保持到事務被提交或回滾。當兩個或多個會話在表的同一條記錄上執行 DML 語句時,第一個會話在該條記錄上加鎖,其他的會話處於等待狀態。當第一個會話提交后, TX 鎖被釋放,其他會話才可以加鎖。
當 Oracle 數據庫發生 TX 鎖等待時,如果不及時處理常常會引起 Oracle 數據庫掛起,或導致死鎖的發生,產生ORA-600 的錯誤。這些現象都會對實際應用產生極大的危害,如長時間未響應,大量事務失敗等。
TM 鎖(表級鎖)
TM 鎖用於確保在修改表的內容時,表的結構不會改變,例如防止在 DML 語句執行期間相關的表被移除。當用戶對表執行 DDL 或 DML 操作時,將獲取一個此表的表級鎖。
當事務獲得行鎖后,此事務也將自動獲得該行的表鎖(共享鎖),以防止其它事務進行 DDL 語句影響記錄行的更新。
事務也可以在進行過程中獲得共享鎖或排它鎖,只有當事務顯示使用 LOCK TABLE 語 句顯示的定義一個排它鎖時,事務才會獲得表上的排它鎖,也可使用 LOCK TABLE 顯示的定義一個表級的共享鎖。
TM 鎖包括了 SS、 SX、 S、 X 等多種模式,在數據庫中用 0-6 來表示。不同的 SQL 操作產生不同類型的 TM 鎖.
TM 鎖類型表
TX 鎖( 事務鎖或行級鎖)
當事務執行數據庫插入、更新、刪除操作時,該事務自動獲得操作表中操作行的排它鎖。
事務發起第一個修改時會得到TX 鎖(事務鎖),而且會一直持有這個鎖,直至事務執行提交(COMMIT)或回滾(ROLLBACK)。
對用戶的數據操縱, Oracle 可以自動為操縱的數據進行加鎖,但如果有操縱授權,則為滿足並發操縱的需要另外實施加鎖。
DML 鎖可由一個用戶進程以顯式的方式加鎖,也可通過某些 SQL 語句隱含方式實現。 這部分屬於 Manual Data Locks。
原理:一個事務要修改塊中的數據,必須獲得該塊中的一個itl,通過itl和undo segment header中的transaction table,可以知道事務是否處於活動階段。事務在修改塊時(其實就是在修改行)會檢查行中row header中的標志位,如果該標志位為0(該行沒有被活動的事務鎖住),就把該標志位修改為事務在該塊獲得的itl的序號,這樣當前事務就獲得了對記錄的鎖定,然后就可以修改行數據了,這也就是oracle行鎖實現的原理。
DML 鎖有如下三種加鎖方式:
- 共享鎖方式( SHARE)
- 獨占鎖方式( EXCLUSIVE)
- 共享更新鎖( SHARE UPDATE)
其中:
SHARE, EXCLUSIVE 用於 TM 鎖(表級鎖)
SHARE UPDATE 用於 TX 鎖( 行級鎖)
共享方式的表級鎖( Share)
共享方式的表級鎖是對表中的所有數據進行加鎖,該鎖用於保護查詢數據的一致性,防止其它用戶對已加鎖的表進行更新。
其它用戶只能對該表再施加共享方式的鎖,而不能再對該表施加獨占方式的鎖,共享更新鎖可以再施加,但不允許持有共享更新封鎖的進程做更新。
共享該表的所有用戶只能查詢表中的數據,但不能更新。
共享方式的表級鎖只能由用戶用 SQL 語句來設置.
語句格式如下:
LOCK TABLE <表名>[,<表名>]... IN SHARE MODE [NOWAIT]
執行該語句,對一個或多個表施加共享方式的表封鎖。
當指定了選擇項NOWAIT,若該鎖暫時不能施加成功,則返回並由用戶決定是進行等待,還是先去執行別的語句。
持有共享鎖的事務,在出現如下之一的條件時,便釋放其共享鎖:
- A、執行 COMMIT 或 ROLLBACK 語句。
- B、退出數據庫( LOG OFF)。
- C、程序停止運行。
共享方式表級鎖常用於一致性查詢過程,即在查詢數據期間表中的數據不發生改變。
獨占方式表級鎖( Exclusive)
獨占方式表級鎖是用於加鎖表中的所有數據,擁有該獨占方式表封鎖的用戶,即可以查詢該表,又可以更新該表,其它的用戶不能再對該表施加任何加鎖(包括共享、獨占或共享更新封鎖)。
其它用戶雖然不能更新該表,但可以查詢該表。
獨占方式的表封鎖可通過如下的 SQL 語句來顯示地獲得:
LOCK TABLE <表名>[,<表名>].... IN EXCLUSIVE MODE [NOWAIT]
獨占方式的表級鎖也可以在用戶執行 DML 語句 INSERT、UPDATE、DELETE時隱含獲得。
擁有獨占方式表封鎖的事務,在出現如下條件之一時,便釋放該封鎖:
- ( 1)、執行 COMMIT 或 ROLLBACK 語句。
- ( 2)、退出數據庫( LOG OFF)
- ( 3)、程序停止運行。
獨占方式封鎖通常用於更新數據,當某個更新事務涉及多個表時,可減少發生死鎖.
共享更新加鎖方式( Share Update)
共享更新加鎖是對一個表的一行或多行進行加鎖,因而也稱作行級加鎖。表級加鎖雖然保證了數據的一致性,但卻減弱了操作數據的並行性。
行級加鎖確保在用戶取得被更新的行到該行進行更新這段時間內不被其它用戶所修改。
因而行級鎖即可保證數據的一致性又能提高數據操作的迸發性。
可通過如下的兩種方式來獲得行級封鎖:
( 1)、執行如下的 SQL 封鎖語句,以顯示的方式獲得:
LOCK TABLE < 表 名 >[,< 表 名 >].... IN SHARE UPDATE MODE [NOWAIT]
( 2)、用如下的 SELECT …FOR UPDATE 語句獲得:
SELECT <列名 >[,<列名 >]...FROM <表名 > WHERE <條件 > FOR UPDATE OF <列名>[,<列名>].....[NOWAIT]
一旦用戶對某個行施加了行級加鎖,則該用戶可以查詢也可以更新被加鎖的數據行,其它用戶只能查詢但不能更新被加鎖的數據行.
如果其它用戶想更新該表中的數據行,則也必須對該表施加行級鎖.即使多個用戶對一個表均使用了共享更新,但也不允許兩個事務同時對一個表進行更新,真正對表進行更新時,是以獨占方式鎖表,一直到提交或復原該事務為止。
行鎖永遠是獨占方式鎖。
當出現如下之一的條件,便釋放共享更新鎖:
- ( 1)、執行提交( COMMIT)語句;
- ( 2)、退出數據庫( LOG OFF)
- ( 3)、程序停止運行。
執行 ROLLBACK 操作不能釋放行鎖。
DLL鎖( dictionary locks)
DDL 鎖用於保護數據庫對象的結構,如表、索引等的結構定義。
DDL 鎖又可以分為:
- 排它 DDL 鎖、
- 共享 DDL 鎖、
- 分析鎖
排它 DDL 鎖
創建、修改、刪除一個數據庫對象的 DDL 語句獲得操作對象的 排它鎖。
如使用 alter table 語句時,為了維護數據的完成性、一致性、合法性,該事務獲得一排它 DDL 鎖
共享 DDL 鎖
需在數據庫對象之間建立相互依賴關系的 DDL 語句通常需共享獲得 DDL鎖。
如創建一個包,該包中的過程與函數引用了不同的數據庫表,當編譯此包時該事務就獲得了引用表的共享 DDL 鎖。
分析鎖
ORACLE 使用共享池存儲分析與優化過的 SQL 語句及 PL/SQL 程序,使運行相同語句的應用速度更快。
一個在共享池中緩存的對象獲得它所引用數據庫對象的分析鎖。
分析鎖是一種獨特的 DDL 鎖類型, ORACLE 使用它追蹤共享池對象及它所引用數據庫對象之間的依賴關系。
當一個事務修改或刪除了共享池持有分析鎖的數據庫對象時, ORACLE 使共享池中的對象作廢,下次在引用這條SQL/PLSQL 語 句時, ORACLE 重新分析編譯此語句。
DDL 級加鎖也是由 ORACLE RDBMS 來控制,它用於保護數據字典和數據定義改變時的一致性和完整性。 它是系統在對 SQL 定義語句作語法分析時自動地加鎖,無需用戶干予。
字典/語法分析加鎖共分三類:
-
( 1)字典操作鎖:
用於對字典操作時,鎖住數據字典,此封鎖是獨占的,從而保護任何一個時刻僅能對一個字典操作。 -
( 2) 字典定義鎖:
用於防止在進行字典操作時又進行語法分析,這樣可以避免在查詢字典的同時改動某個表的結構。 -
( 3)表定義鎖:
用於一個 SQL 語句正當訪問某個表時,防止字典中與該表有關的項目被修改。
悲觀封鎖和樂觀封鎖
悲觀封鎖
鎖在用戶修改之前就發揮作用:
Select ..for update [nowait] Select * from tab1 for update
用戶發出這條命令之后,oracle將會對返回集中的數據建立行級封鎖,以防止其他用戶的修改。
如果此時其他用戶對上面返回結果集的數據進行dml或ddl操作都會返回一個錯誤信息或發生阻塞。
- 1:對返回結果集進行update或delete操作會發生阻塞。左下角的時間執行了很久。
- 2:對該表進行ddl操作將會報:Ora-00054:resource busy and acquire with nowait specified.
原因分析 :
此時Oracle已經對返回的結果集上加了排它的行級鎖,所有其他對這些數據進行的修改或刪除操作都必須等待這個鎖的釋放,產生的外在現象就是其他的操作將發生阻塞,這個這個操作commit或rollback.
同樣這個查詢的事務將會對該表加表級鎖,不允許對該表的任何ddl操作,否則將會報出ora-00054錯誤::resource busy and acquire with nowait specified.
悲觀的缺陷是,加鎖的時間可能會很長,這樣可能會長時間的限制其他用戶的訪問,也就是說悲觀鎖的並 發訪問性不好.
栗子
會話A:
在這里新開一個plsql窗口模擬會話A
--建表 create table xgj (name varchar2(20)); --新增數據 insert into xgj values('xiaogongjiang'); --提交數據 commit ; --使用for update方式獲取排他行級鎖 select * from xgj where name='xiaogongjiang' for update ;
會話B:
在這里是在plsql中另外新開了一個窗口模擬會話B,不能在同一個會話窗口,否則測試不出來。
alter table xgj add(salary number(5));
注意看左下角的時間,會看到已經執行時間了很長時間,如果會話A不提交則會一直等待,A提交后,馬上執行成功。
樂觀封鎖
樂觀的認為數據在select出來到update數據並提交的這段時間數據不會被更改。樂觀鎖多個會話可以同時操作數據。這里面有一種潛在的危險就是由於被選出的結果集並沒有被鎖定,是存在一種可能被其他用戶更改的可能。因此Oracle仍然建議是用悲觀封鎖,因為這樣會更安全。
比較常見的方式使用版本列來,每次更新時都和舊版本的數據比較。
System Locks
oracle使用不同類型的系統鎖來保護內部數據庫和內存結構.
這些機制是用戶無法訪問的。
死鎖
當兩個用戶希望持有對方的資源時就會發生死鎖.
即兩個用戶互相等待對方釋放資源時,oracle認定為產生了死鎖,在這種情況下,將以犧牲一個用戶作為代價,另一個用戶繼續執行,犧牲的用戶的事務將回滾。
場景
1:用戶 1 對 A 表進行 Update,沒有提交。
2:用戶 2 對 B 表進行 Update,沒有提交。
此時雙反不存在資源共享的問題。
3:如果用戶 2 此時對 A 表作 update,則會發生阻塞,需要等到用戶一的事物結束。
4:如果此時用戶 1 又對 B 表作 update,則產生死鎖。此時 Oracle 會選擇其中一個用戶進行會滾,使另一個用戶繼續執行操作。
起因分析
Oracle 的死鎖問題實際上很少見,如果發生,基本上都是不正確的程序設計造成的,經過調整后,基本上都會避免死鎖的發生。
在 Oracle 系統中能自動發現死鎖,並選擇代價最小的,即完成工作量最少的事務予以撤消,釋放該事務所擁有的全部鎖,記其它的事務繼續工作下去。
從系統性能上考慮,應該盡可能減少資源競爭,增大吞吐量,因此用戶在給並發操作加鎖時,應注意以下幾點:
- 1、 對於 UPDATE 和 DELETE 操作,應只鎖要做改動的行,在完成修改后立即提交。
- 2、 當多個事務正利用共享更新的方式進行更新,則不要使用共享封鎖,而應采用共享更新鎖,這樣其它用戶就能使用行級鎖,以增加並行性。
- 3、 盡可能將對一個表的操作的並發事務施加共享更新鎖,從而可提高並行性。
- 4、 在應用負荷較高的期間,不宜對基礎數據結構(表、索引、簇和視圖)進行修改
死鎖后的解決辦法
如果死鎖不能自動釋放,就需要我們手工的 kill session
生成Kill Session語句
- 查看有無死鎖對象,如有 kill session
SELECT 'alter system kill session ''' || sid || ',' || serial# || ''';' "Deadlock" FROM v$session WHERE sid IN (SELECT sid FROM v$lock WHERE block = 1);
如果有,會返回類似與如下的信息:
kill session:
執行
alter system kill session '646,3953';
注意: 應當注意對於 sid 在 100 以下的應當謹慎,可能該進程對應某個application,如對應某個事務,可以 kill
查看導致死鎖的 SQL
SELECT s.sid, q.sql_text FROM v$sqltext q, v$session s WHERE q.address = s.sql_address AND s.sid = &sid -- 這個&sid 是第一步查詢出來的 ORDER BY piece;
執行后,輸入對應的sid即可查看對應的sql.
如果輸入的sid找不到對應的sql,可以先執行查看誰鎖了誰(2)的sql, 查到另外一個sid, 根據另外一個sid,會查到對應的sql .
查看誰鎖了誰
SELECT s1.username || '@' || s1.machine || ' ( SID=' || s1.sid || ' ) is blocking ' || s2.username || '@' || s2.machine || ' ( SID=' || s2.sid || ' ) ' AS blocking_status FROM v$lock l1, v$session s1, v$lock l2, v$session s2 WHERE s1.sid = l1.sid AND s2.sid = l2.sid AND l1.BLOCK = 1 AND l2.request > 0 AND l1.id1 = l2.id1 AND l2.id2 = l2.id2;
或者
SELECT
LPAD (' ', DECODE (l.xidusn, 0, 3, 0)) || l.oracle_username User_name, o.owner, o.object_name, o.object_type, s.sid, s.serial# FROM v$locked_object l, dba_objects o, v$session s WHERE l.object_id = o.object_id AND l.session_id = s.sid ORDER BY o.object_id, xidusn DESC;
鎖和阻塞
概念
通常來講,系統如果平時運行正常,突然會停止不動,多半是被阻塞( Blocked)住了。 我們可以通過 v$lock 這張視圖,看查看阻塞的信息。
SQL> desc v$lock
Name Type Nullable Default Comments
------- ----------- -------- ------- --------
ADDR RAW(8) Y KADDR RAW(8) Y SID NUMBER Y TYPE VARCHAR2(2) Y ID1 NUMBER Y ID2 NUMBER Y LMODE NUMBER Y REQUEST NUMBER Y CTIME NUMBER Y BLOCK NUMBER Y SQL>
我們關注的比較多的是 request 和 block 字段。
如果某個 request 列是一個非 0 值,那么它就是在等待一個鎖。 如果 block 列是1,這個 SID 就持有了一個鎖,並且阻塞別人獲得這個鎖。
這個鎖的類型由 TYPE字段定義。鎖的模式有 LMODE 字段定義, ID1 和 ID2 字段定義了這個鎖的相關信息。
ID1 相同,就代表指向同一個資源。 這樣就有可能有加鎖者和等待者。
LMODE 的 6 中模式參考上面的 TM 鎖類型表。
可以結合 v$lock
和 v$session
視圖來查詢相關的信息:
SELECT sn.username, m.SID, sn.SERIAL#, m.TYPE, DECODE(m.lmode, 0, 'None', 1, 'Null', 2, 'Row Share', 3, 'Row Excl.', 4, 'Share', 5, 'S/Row Excl.', 6, 'Exclusive', lmode, LTRIM(TO_CHAR(lmode, '990'))) lmode, DECODE(m.request, 0, 'None', 1, 'Null', 2, 'Row Share', 3, 'Row Excl.', 4, 'Share', 5, 'S/Row Excl.', 6, 'Exclusive', request, LTRIM(TO_CHAR(m.request, '990'))) request, m.id1, m.id2 FROM v$session sn, v$lock m WHERE (sn.SID = m.SID AND m.request != 0) --存在鎖請求,即被阻塞 OR (sn.SID = m.SID --不存在鎖請求,但是鎖定的對象被其他會話請求鎖定 AND m.request = 0 AND lmode != 4 AND (id1, id2) IN (SELECT s.id1, s.id2 FROM v$lock s WHERE request != 0 AND s.id1 = m.id1 AND s.id2 = m.id2)) ORDER BY id1, id2, m.request;
或者
SELECT /*+ rule */ s.username, DECODE(l.TYPE, 'TM', 'TABLE LOCK', 'TX', 'ROW LOCK', NULL) lock_level, o.owner, o.object_name, o.object_type, s.sid, s.serial#, s.terminal, s.machine, s.program, s.osuser FROM v$session s, v$lock l, dba_objects o WHERE l.sid = s.sid AND l.id1 = o.object_id(+) AND s.username IS NOT NULL;
引起阻塞的幾種常見情況
( 1) DML 語句引起阻塞
( 2)外鍵沒有創建索引
1.DML 語句引起阻塞
當一個會話保持另一個會話正在請求的資源上的鎖定時,就會發生阻塞。被阻塞的會話將一直掛起,直到持有鎖的會話放棄鎖定的資源為止。
4 個常見的 dml 語句會產生阻塞:
- ( 1) INSERT
- ( 2) UPDATE
- ( 3) DELETE
- ( 4) SELECT…FOR UPDATE
INSERT
Insert 發生阻塞的唯一情況就是用戶擁有一個建有主鍵約束的表。
當 2 個會話同時試圖向表中插入相同的數據時,其中的一個會話將被阻塞,直到另外一個會話提交或會滾。一個會話提交時,另一個會話將收到主鍵重復的錯誤。回滾時,被阻塞的會話將繼續執行。
Update 和 Delete
UPDATE 和 DELETE 當執行 Update 和 delete 操作的數據行已經被另外的會話鎖定時,將會發生阻塞,直到另一個會話提交或會滾。
Select …for update
當一個用戶執行 select..for update 對返回的結果集進行修改時,如
果結果集已經被另一個會話鎖定,此時 Oracle 已經對返回的結果集上加了排它的行級鎖, 所有其他對這些數據進行的修改或刪除操作都必須等待這個鎖的釋放(操作 commit 或 rollback.),產生的外在現象就是其他的操作將發生阻塞.
同樣這個查詢的事務將會對該表加表級鎖,不允許對該表的任何 ddl 操作,否則將會報出 Ora-00054:resource busy and acquire with nowait specified.
可以通過發出 select ... for update nowait
的語句來避免發生阻塞,如果資源已經被另一個會話鎖定,則會返回以下錯誤:Ora-00054:resource busy and acquire with nowait specified.
2.外鍵沒有創建索引
如果系統中有主,外鍵引用關系,並且滿足一下三個條件中的任意一個,那么就應該考慮給外鍵字段創建索引,否則系統的性能可能會下降甚至阻塞。
- ( 1) 主表上有頻繁的刪除操作
- ( 2) 主鍵上有頻繁的修改操作。
- ( 3) 業務上經常會出現主表和從表做關聯查詢的情況。
第一和第二個條件操作的時候,主表會在從表上創建一個鎖定,以保證主表主鍵的修改不會導致從表的數據在引用上出現問題,這是一個數據引用完整性的要求。
如果主表上經常出現這樣的刪除或者是對主鍵列進行修改的操作,或者每次操作的記錄數很多,都將會造成從表長時間被鎖定,而影響其他用戶的正常操作。
比如主表每次刪除 1000 行數據,它就需要掃描從表 1000 次,以確定每一行記錄的改變都不會造成從表數據在引用上的不完整。
特別是在 OLAP 系統中,從表經常會是非常巨大的表,在這種情況下,如果從表沒有索引,那么查詢幾乎是不可想象的。
Latch
latch概述
Latch屬於 System Lock, 用於保護 SGA區中共享數據結構的一種串行化鎖定機制。
Latch 的實現是與操作系統相關的,尤其和一個進程是否需要等待一個latch、需要等待多長時間有關.
Latch 是 Oracle 提供的輕量級鎖資源, 是一種能夠極快地被獲取和釋放的鎖,能快速,短時間的鎖定資源,
Latch用於防止多個並發進程同時修改訪問某個共享資源, 它只工作在 SGA 中, 通常用於保護描述 buffer cache 中 block 的數據結構。
比如 SGA 中,各種數據被反復從磁盤讀取到內存,又被重新寫回到磁盤上,如果有並發的用戶做相同的事情, Oracle 必須使用一種機制,來保證數據在讀取的時候,只能由一個會話來完成,這種保護機制就是 Latch。
-
並發( concurrency): 是說有超過兩個以上的用戶對同樣的數據做修改(可能包括插入,刪除和修改)。
-
並行( parallel): 是說將一件事情分成很多小部分,讓每一部分同時執行,最后將執行結果匯總成最終結果。
與每個 latch 相聯系的還有一個清除過程,當持有 latch 的進程成為死進程時,該清除過程就會被調用。
Latch 還具有相關級別,用於防止死鎖,一旦一個進程在某個級別上得到一個 latch,它就不可能再獲得等同或低於該級別的 latch。
Latch 不會造成阻塞,只會導致等待。 阻塞是一種系統設計上的問題,等待是一種系統資源爭用的問題。
spin概述
比如數據緩存中的某個塊要被讀取,我們會獲得這個塊的 latch, 這個過程叫做 spin,另外一個進程恰好要修改這個塊,他也要 spin 這個塊,此時他必須等待,當前一個進程釋放 latch 后才能 spin 住,然后修改, 如果多個進程同時請求的話,他們之間將出現競爭,沒有一個入隊機制,一旦前面進程釋放所定,后面的進程就蜂擁而上,沒有先來后到的概念, 並且這一切都發生的非常快,因為Latch 的特點是快而短暫。
SPIN 與休眠( sleep)
Oracle 選擇了 spin,讓進程繼續占有 CPU,運行一些空指令,之后繼續請求,繼續 spin,直到達到_spin_count 值,這時會放棄 CPU,進行短暫的休眠,再繼續剛才的動作。
進程休眠的時間也是存在算法的.休眠的閥值限制由隱含參數_max_exponential_sleep控制, 默認是 2 秒.
如果當前進程已經占用了別的 Latch,則他的休眠時間不會太長(過長會引起別的進程的 Latch 等待),此時的休眠最大時間有隱含參數_max_sleep_holding_latch 決定, 默認是 4 厘秒.
總之,Latch 獲取的流程: 請求-SPIN-休眠-請求-SPIN-休眠 … … 占用。
Latch 和 Lock
從某種意義上說, Latch 是內存中的資源鎖,數據庫對象(表,索引等)的鎖叫Lock。
Latch 和 Lock 的區別:
-
( 1) . Latch 是對內存數據結構提供互斥訪問的一種機制,而 Lock 是以不同的模式來套取共享資源對象,各個模式間存在着兼容或排斥,從這點看出, Latch的訪問,包括查詢也是互斥的,任何時候,只能有一個進程能 spin 住內存的某一塊,幸好這個過程是相當的短暫,否則系統性能將沒的保障,從 9I 開始,允許多個進程同時查詢相同的內存塊。
-
( 2) . Latch 只作用於內存中,他只能被當前實例訪問,而 Lock 作用於數據庫對象,在 RAC 體系中實例間允許 Lock 檢測與訪問
-
( 3) . Latch 是瞬間的占用,釋放, Lock 的釋放需要等到事務正確的結束,他占用的時間長短由事務大小決定
-
( 4) . Latch 是非入隊的,而 Lock 是入隊的
-
( 5) . Latch 不存在死鎖,而 Lock 中存在。
Latch 爭用
如果發現系統中經常由於 Lock 導致用戶等待
這時需要考慮系統在邏輯設計上是否有問題,比如多用戶對主鍵的刪除或者修改,是否有用戶使用 select … for update 這樣的語法,外鍵是否創建索引的因素。 這些因素是需要結合系統的業務邏輯性來進行數據庫對象設計的。
如果發現系統慢是因為很多的 Latch 爭用
就要考慮系統及數據庫自身設計上是否存在問題,比如是否使用綁定變量,是否存在熱快,數據存儲參數設計是否合理等因素。
導致 Latch 爭用而等待的原因非常多,內存中很多資源都可能存在爭用。
最常見的兩類 latch 爭用如下:
( 1) 共享池中的 Latch 爭用。
( 2)數據緩沖池中的 latch 爭用。
共享池中的 Latch 爭用
共享池中如果存在大量的 SQL 被反復分析,就會造成很大的 Latch 爭用和長時間的等待, 最常見的現象就是沒有綁定變量。
最常見的集中共享池里的 Latch 是 library cache。
可以通過一下 SQL 來查詢:
select * from v$latchname where name like 'library cache%';
在分析系統性能時,如果看到有 library cache 這樣的 Latch 爭用,就可以斷定是共享池中出現了問題,這種問題基本是由 SQL 語句導致的,比如沒有綁定變量 或者一些存儲過程被反復分析。
資源的爭用可以通過如下 SQL 來查看
select event,count(*) from v$session_wait group by event;
數據緩沖池中的 latch 爭用
訪問頻率非常高的數據塊被稱為熱快( Hot Block),當很多用戶一起去訪問某幾個數據塊時,就會導致一些 Latch 爭用.
最常見的 latch 爭用有:
- ( 1) buffer busy waits
- ( 2) cache buffer chain
這兩個 Latch 的爭用分別發生在訪問數據塊的不同時刻。
產生這些 Latch 爭用的直接原因是太多的會話去訪問相同的數據塊導致熱快問題, 造成熱快的原因可能是數據庫設置導致或者重復執行的 SQL 頻繁訪問一些相同的數據塊導致。