Data Concurrency andConsistency(數據的併發和並行)
這一章解釋了Oracle數據庫在多用戶環境中,怎樣維護數據一致性
這一張包含如下小節:
·Introduction to DataConcurrency and Consistency
·Overview of OracleDatabase Transaction Isolation Levels
·Overview of theOracle Database Locking Mechanism
·Overview ofAutomatic Locks
·Overview of ManualData Locks
·Overview ofUser-Defined Locks
Introduction to DataConcurrency and Consistency(介紹數據併發和數據一致性)
在單用戶數據庫,一個用戶可以數據的時候 可以不用擔心是否會有別的用戶在同一時間修改同一數據。
而在多用戶數據庫,多個同時發生的事務中的語句,可以更行同一個數據。事務同事執行時必須要產生有意義的,以及一致的返回結果。因此,一個多用戶數據庫必須提供以下功能:
·Data concurrency(數據併發),它保證了多用戶可以同時訪問同一數據。
·Data consistency(數據一致),它保證了每個用戶看到的數據都是一致的,包括用戶自己事務中顯式的修改,以及其他用戶所有提交的事務。
爲了說明在事務併發運行時的一致性行爲,數據庫科研人員定義了一個事務隔離型模型,叫做serializability(可串行化)。可串行化事務在一種使其看起來好像沒有其他用戶正在修改數據庫中的數據的環境中運作。
雖然這種程度的事務隔離通常是不錯的,但是如果多個應用都運行在可串行化模型下會嚴重影響應用的吞吐能力。併發完全隔離運行的事務可能意味着在一個事務查詢一個表時,另外一個事務無法對這個表進行插入操作。簡短截說,真實世界中通常要在完美的事務隔離和性能之間做個折中考慮。
Oracle數據庫使用multiversionconsistency model(多版本一致模型)和各種的鎖和事務來維護數據一致性。這種情況下,數據庫可以提供數據的顯示 給多個併發用戶。每個顯示,在某個時間點都是一致的。因爲在同一時間可以存在數據塊的好幾個版本,所以事務可以讀取在查詢開始時間點的已經提交數據的版本,然後在這個開始時間點一致的數據。
Multiversion Read Consistency(多版本讀一致性)
在Oracle數據庫,多版本指同時存在數據的多個版本。Oracle數據庫維護多版本讀一致性,它意味着數據庫查詢,需要有下面特性:
·Read-consistent queries (讀一致性查詢)
查詢返回的數據是已經提交的,在某一時間點一致的。
重要提醒:Oracle數據庫永遠不允許dirty reads(髒讀),
髒讀是意思是,一個事務查詢出了另一個事務還沒提交的數據。
解釋一下髒讀存在的問題,假設一個事務更新了一列的值,還沒提交。第二個事務讀到了這個更新後的髒值。這時第一個事務回滾了,現在列上的值還是原來的值,但是第二個事務繼續使用着剛纔更新後的值,這會損壞數據庫。髒讀危害數據完整性,破壞外鍵,以及忽略唯一約束。
·Nonblocking queries(查詢不阻塞)
數據的讀和寫之間,不會互相阻塞。
Statement-Level ReadConsistency(語句級讀一致性)
Oracle數據庫總是強制保證 語句級讀一致性,它保證了單個查詢返回的數據是在某一個時間點已經提交以及一致的。這個時間點取決於事務隔離級別以及查詢的性質:
·在 read committed 隔離級別,這個時間點是語句打開時的點,舉個例子,如果一個SELECT語句在SCN1000的時候打開,那麼這個語句獲取的數據就是在SCN 1000時一致的。
·在serializable或read-only 事務,這個點是 事務開始時的時間。舉個例子,如果一個事務在SCN 1000時開始,以及有多個SELECT發生在這個事務,那麼每個SELECT返回的數據
都是在SCN 1000時一致。
·在Flashback Query(閃回查詢)語句(SELECT .... AS OF),SELECT語句顯式指定了這個時間點,舉個例子,你可以查詢一個表在上一個週四的下午兩點時的顯示的數據。
Transaction-LevelRead Consistency(事務級讀一致性)
Oracle數據庫同樣可以對一個事務中的所有查詢都提供一個一致性,稱之爲transaction-level read consistency(事務級讀一致性),在這種情況下,事務中的每個語句,看到的數據都同一個時間點的一致性數據。這個點是這個事務開啓時的點。
serializable事務中的查詢只能看到自己本事務發出的修改。舉個例子,一個事務更新了employees,然後查詢employees,是可以看到修改。事務級讀一致性產生了可重複讀,且不產生幻影讀。
ReadConsistency and Undo Segments(讀一致性與回滾段)
爲了管理多版本多一致性模型,在一個表同時發生讀和更新時,數據庫必須創建這個數據的讀一致性的集。
Oracle數據庫通過UNDO data來完成這個目標。
任何時候用戶修改了數據,Oracle數據庫會創建undo 條目,它寫在undo segments(回滾段)裏。undo segments裏面包含着未提交的事務,或者剛剛提交事務 修改位置 之前的舊值。因此,同一個數據在不同時間點的不同版本,可以在數據庫中同時存在。數據庫可以使用數據在不同時間點的快照,來提供讀一致性 以及保證 nonblocking queries(查詢不阻塞)。
在單節點或ORACLE RAC環境 讀一致性都是保證的。Oracle RAC使用 cache-to-cache 塊傳送 稱之爲Cache Fusion的機制,來傳送數據塊的 讀一致性版本 從一個instance 到另外一個。
ReadConsistency:Example(讀一致性的案例)
下圖顯示了一個查詢使用undo數據提供語句級讀一致性(在read commited 隔離級別)
數據庫爲某個查詢檢索數據時,數據庫保證每個塊中的數據反映了該查詢開始時的內容。
如果有需要,數據庫會回滾塊中的改變去重構一個塊 它是查詢開始時 這個時間點的這個塊。
數據庫使用一個叫SCN的機制來保證事務的 排序(順序)。當一個SELECT進入的執行階段,數據庫確定查詢開始執行時所記錄的SCN。上圖,SCN是10023.這個查詢只看在SCN 10023時已經提交的數據
上圖中,塊的SCN號在10023之後的表示已經發生修改的塊,像上面顯示的,是兩個塊,SCN是10024. SELECT語句需要一個已經提交塊的一致性版本。數據庫複製一個當前數據塊,變成一個新的buffer(塊)然後引用undo數據去重構這個塊的之前的版本。這些被重構出來的塊叫做consistent read(CR)clones
在上圖中,數據庫創建了兩個CR clones:一個塊與SCN 10006時一致 另一個塊與SCN 10021時一致。對於這個查詢,數據庫返回重構後的數據。在這種情況下,Oracle數據庫預防了髒讀。
ReadConsistency and Transaction Tables(讀一致性和事務表)
數據庫使用一個 事務表,同樣也叫做interestedtransaction list(ITL)。用來確定當一個數據庫開始修改一個塊時,上面時候有事務未提交。每個段塊的塊頭都有一個事務表(ITL)。
事務表中的條目 描述了哪個事務有行鎖,以及塊中的哪些行包含提交和未提交的更改。事務表指向的是undo segment,它提供了當數據庫做這個修改時的時間信息。
在某種意義上來說,塊頭包含了一個對塊中任意行有過影響的 事務們的歷史。CREATE TABLE和ALTER TABLE語句的INITRANS參數控制了 事務歷史保存的數量。
Locking Mechanisms(鎖機制)
一般來說,多用戶數據使用一些形式的數據鎖定 來解決數據併發性,一致性,以及完整性的問題,Locks是一種機制 來預防多個事務訪問同一資源而導致的破壞性內部交互操作。
ANSI/ISOTransaction Isolation Levels(ANSI/ISO事務隔離級別)
在ANSI和ISO/IEC兩個標準都採納的SQL標準中,定義了 四個級別的transaction isolation(事務隔離)。這些級別 對於事務處理吞吐量有不同程度的影響。
這些隔離級別定義是爲了 預防兩個併發執行事務肯定會產生的一些現象。這些需要預防的現象有:
·Dirty reads(髒讀)
一個事務讀到了另外一個事務修改但還沒提交的數據
·Nonrepeatable(fuzzy) reads(不能重複,失真 讀)
一個事務重複讀了剛纔已經讀取過的數據,然後又發現了別的事務提交了的已經修改的數據。舉個例子,一個用戶查詢了一行,之後又查詢了一下相同的行,卻發現數據已經修改了
·Phantom reads(幻影讀)
一個事務重新執行了一下查詢,返回了的行集滿足了查詢條件,並查到了其他已經提交的事務中插入了一行 符合查詢條件的行。
舉個例子,一個事務查詢了僱員的數量,五分鐘以後它執行了相同的查詢,但是現在數量增長了1,因爲另外一個用戶插入了一行。更多的數據符合了之前的查詢標準,但是和模糊讀不一樣,之前的數據沒有發生變化。
根據運行在某個隔離級別所允許發生的現象,數據庫定義了四個隔離級別
Isolation Level |
Dirty Read |
Nonrepeatable Read |
Phantom Read |
Possible |
Possible |
Possible |
|
Read committed |
Not possible |
Possible |
Possible |
Repeatable read |
Not possible |
Not possible |
Possible |
Serializable |
Not possible |
Not possible |
Not possible |
Oracle數據庫提供了 read committed(默認)和serializable 兩種隔離級別。同樣,數據庫還提供了只讀模式
Overview of Oracle DatabaseTransaction Isolation Levels(Oracle數據庫事務隔離級別介紹)
上面的表總結了ANSI標準 中的事務隔離級別。該標準定義了各個隔離級別所允許或必須防止的現象。Oracle數據庫提供下面的事務隔離級別:
·Read Committed Isolation Level(已提交讀隔離級別)
·Serializable Isolation Level(串行化隔離級別)
·Read-Only Isolation Level(只讀隔離級別)
ReadCommitted Isolation Level(已提交讀隔離級別)
在read committed isolation level(默認),每個事務中執行的查詢看到的數據都是在查詢(注意,這裏是查詢,不是事務)之前已經提交的。這個隔離級別在一些事務可能會衝突的數據庫環境中是適用的。
一個在read committed事務中的查詢 避免了讀到當查詢已經發生後更提交的數據。舉個例子,如果一個查詢掃描一個百萬行的表,掃到了一半,而且如果另一個事務這時修改了第950000行,且提交了,那麼之前的查詢掃描到第950000行的時候 並不會顯示出這個修改。
然後,因爲數據庫並不阻止其他事務修改一個事務所讀取的數據,其他事務可能會在查詢執行期間修改。因此,一個事務中運行同樣的查詢兩次,可能會遇到模糊讀和幻影讀
ReadConsistency in the Read Committed Isolation Level(在已提交讀隔離級別中的讀一致性)
每個查詢都會提供一個一致性的結果集,不需要用戶做什麼操作,就能保證數據的一致性。
一個隱式查詢,比如說在UPDATE語句中WHERE條件隱含的查詢,也保證是個一致性的結果集。不管怎樣,在DML語句中隱式的查詢不會看到它自己做的修改,但是會看到修改之前的數據。
如果一個SELECT 包含一個PL/SQL函數,那麼PL/SQL函數之中的sql運行 也會引用語句級的讀一致性。而不是外面這個SELECT的讀一致性。舉個例子,一個函數可以訪問一個表中的已經修改的數據和其他用戶提交的數據。函數中每個SELECT的執行,都會建立一個新的讀一致性快照。
ConflictingWrites in Read Committed Transactions(已提交讀中的寫衝突)
在一個已提交讀事務,當事務要更改一行,而這行已經被另外一個未提交事務所修改(blocking transaction),這時會發生寫入衝突(conflicting write)。
這個已提交讀事務會等blocking transaction 結束然後釋放它的行鎖。有以下選項:
·如果blocking transaction回滾,那麼waiting transaction會修改之前被locked的行,彷彿其他事務從來沒出現過。
·如果blocking transaction提交然後釋放鎖,那麼waiting transaction會繼續進行它的打算,更新當前改變後的行。
下表顯示了transaction 1(它可以是serializable 或 read committed),與 read committed transaction2的交互。下表顯示的是典型的情況,叫做lost update.transaction1做的更新,沒有在表中 即使transaction 1提交了它。設計一個策略來解決lost update是一個應用開發的重要部分。
Session 1 |
Session 2 |
Explanation |
SQL> SELECT last_name, salary FROM employees WHERE last_name IN ('Banda','Greene','Hintz');
LAST_NAME SALARY ------------- ---------- Banda 6200 Greene 9500 |
session 1查詢了Banda,Greene以及Hintz的工資,沒有找到名叫Hintz的人。 |
|
SQL> UPDATE employees SET salary = 7000 WHERE last_name = 'Banda'; |
session 1通過更新Banda的工資來開啓了一個事務。事務1的默認隔離級別是READ COMITTED |
|
SQL> SET TRANSACTION ISOLATION LEVEL READ COMMITTED; |
Session2 開始一個事務2,顯式設置隔離級別爲READ COMMITTED |
|
SQL> SELECT last_name, salary FROM employees WHERE last_name IN ('Banda','Greene','Hintz');
LAST_NAME SALARY ------------- ---------- Banda 6200 Greene 9500 |
事務2 查詢Banda,Greene以及Hintz的工資,Oracle數據庫使用讀一致性顯示了Banda的工資還是爲事務1這個未提交事務之前的狀態。 |
|
SQL> UPDATE employees SET salary = 9900 WHERE last_name = 'Greene'; |
事務2可以成功更新Greene的工資,因爲事務1只鎖了Banda的一行 |
|
SQL> INSERT INTO employees (employee_id, last_name, email, hire_date, job_id) VALUES (210, 'Hintz', 'JHINTZ', SYSDATE, 'SH_CLERK'); |
事務1給僱員Hintz插入了一行,但沒有提交 |
|
SQL> SELECT last_name, salary FROM employees WHERE last_name IN ('Banda','Greene','Hintz');
LAST_NAME SALARY ------------- ---------- Banda 6200 Greene 9900 |
事務2查僱員Banda,Greene,Hintz的工資 |
|
SQL> UPDATE employees SET salary = 6300 WHERE last_name = 'Banda';
-- prompt does not return |
事務2企圖更新Banda的行,而這行當前已經被事務1鎖住了,產生了一個寫衝突,事務2要等待 直到事務1結束 |
|
SQL> COMMIT; |
事務1提交,結束了這個事務。 |
|
1 row updated.
SQL> |
Banda行的鎖被釋放掉了,所以事務2繼續處理它對Banda的工資的更新 |
|
SQL> SELECT last_name, salary FROM employees WHERE last_name IN ('Banda','Greene','Hintz');
LAST_NAME SALARY ------------- ---------- Banda 6300 Greene 9900 Hintz |
事務2查詢僱員Banda,Greene,以及Hintz的工資,在已經提交事務1中插入的Hintz,現在事務2也可以看到了,事務2還看到了它對Banda工資的更新。 |
|
COMMIT; |
事務2提交,結束了它的事務。 |
|
SQL> SELECT last_name, salary FROM employees WHERE last_name IN ('Banda','Greene','Hintz');
LAST_NAME SALARY ------------- ---------- Banda 6300 Greene 9900 Hintz |
Session 1查詢了Banda,Greene和Hintz的行,Banda的工資是6300,它是由事務2更新的。而由事務1更新的7000 現在"lost(丟)"了 |
Serializable Isolation Level(串行化隔離級別)
在串行化隔離級別中,一個事務可看到的在事務(不是語句)開始時已經提交的,以及事務自己做的修改。一個可串行化事務在一個環境中操作,使環境看起來好像沒有其他用戶在修改數據。
串行化隔離所適合的環境:
·超大的數據庫,很小的事務,每個事務只更新幾行。
·在兩個併發事務修改相同行的機率相對較低的環境
·有較長的事務,但主要是隻讀事務的時候
在串行化隔離中,讀一致性從通常的語句級拓展成整個事務級。事務中讀取的任何行,再次讀時都保證是相同的。在事務期間,任何查詢的值 都保證只有一份,這時其他事務做的修改,在這個事務是不體現的,和語句運行多長時間沒有任何關係。串行化事務不會遇到髒讀,模糊讀,或幻影讀。
Oracle數據庫允許串行化事務修改數據,不過如果有其他事務修改,那麼這個事務必須在串行化事務開始之前就提交。當一個串行化事務企圖修改一個行,而這個被別的事務修改,且在串行化事務開始之後才提交,這時數據會包一個錯
ORA-08177: Cannot serialize access for thistransaction
當一個串行化事務報錯ORA-08177,一個應用可以做一些動作。包括如下:
·提交事務
·執行其他不同的語句,比如回滾到之前建立的savepoint
·回滾整個事務
下標顯示一個串行化事務和其他事務是如何交互的。如果一個串行化事務不去嘗試修改其他事務在串行化事務開始後提交的數據,那麼serialized access 問題可以避免。
Session 1 |
Session 2 |
Explanation |
SQL> SELECT last_name, salary FROM employees WHERE last_name IN ('Banda','Greene','Hintz');
LAST_NAME SALARY ------------- ---------- Banda 6200 Greene 9500 |
事務1查詢Banda,Greene,以及Hintz的工資,沒有找到一個叫Hinz |
|
SQL> UPDATE employees SET salary = 7000 WHERE last_name = 'Banda'; |
session 1 通過更新Banda的工資開啓事務,默認是READ COMMITTED |
|
SQL> SET TRANSACTION ISOLATION LEVEL SERIALIZABLE; |
Session2開始事務2,設置它爲SERIALIZED隔離級別 |
|
SQL> SELECT last_name, salary FROM employees WHERE last_name IN ('Banda','Greene','Hintz');
LAST_NAME SALARY ------------- ---------- Banda 6200 Greene 9500 |
事務2查詢Banda,Greene,以及Hintz的工資,Oracle數據庫使用讀一致性顯示Banda的工資(事務1還沒提交) |
|
SQL> UPDATE employees SET salary = 9900 WHERE last_name = 'Greene'; |
事務2更新Greene的工資成功。因爲只有Banda的行是被鎖的。 |
|
SQL> INSERT INTO employees (employee_id, last_name, email, hire_date, job_id) VALUES (210, 'Hintz', 'JHINTZ', SYSDATE, 'SH_CLERK'); |
事務1插入一行(Hintz) |
|
SQL> COMMIT; |
事務1提交了它的操作,結束了事務。 |
|
SQL> SELECT last_name, salary FROM employees WHERE last_name IN ('Banda','Greene','Hintz');
LAST_NAME SALARY ------------- ---------- Banda 7000 Greene 9500 Hintz |
SQL> SELECT last_name, salary FROM employees WHERE last_name IN ('Banda','Greene','Hintz');
LAST_NAME SALARY ------------- ---------- Banda 6200 Greene 9900 |
session 1查詢僱員Banda,Greene和Hintz的工資。顯示了已經被事務1提交的值,沒顯示事務2 未提交的更新(Greene)
事務2查詢僱員Banda,Greene和Hintz的工資,Oracle數據庫讀一致性保證了事務1提交的更新和插入,在事務2中不顯示。事務2只看到它自己的修改。 |
COMMIT; |
事務2提交 |
|
SQL> SELECT last_name, salary FROM employees WHERE last_name IN ('Banda','Greene','Hintz');
LAST_NAME SALARY ------------- ---------- Banda 7000 Greene 9900 Hintz |
SQL> SELECT last_name, salary FROM employees WHERE last_name IN ('Banda','Greene','Hintz');
LAST_NAME SALARY ------------- ---------- Banda 7000 Greene 9900 Hintz |
兩個session都查詢Banda,Greene,以及Hintz的工資,每個session都能看到事務1和事務2提交的結果。 |
SQL> UPDATE employees SET salary = 7100 WHERE last_name = 'Hintz'; |
session1 通過更新Hintz的工資來開啓事務3,默認的隔離級別是READ COMMITED |
|
SQL> SET TRANSACTION ISOLATION LEVEL SERIALIZABLE; |
session2開啓事務4,設置SERIALIZABLE隔離級別 |
|
SQL> UPDATE employees SET salary = 7200 WHERE last_name = 'Hintz';
-- prompt does not return |
事務企圖去修改Hintz的更新,但是這行已經被事務3鎖了。事務4順序排在事務3的後面。 |
|
SQL> COMMIT; |
事務3提交了更新,結束了事務 |
|
UPDATE employees SET salary = 7200 WHERE last_name = 'Hintz' * ERROR at line 1: ORA-08177: can't serialize access for this transaction |
事務3的提交導致事務4的報錯(ORA-08177),這個問題出現是因爲事務3提交Hintz的修改,在事務4開始以後。 |
|
SQL> ROLLBACK; |
session2回滾事務4,結束了這個事務。 |
|
SQL> SET TRANSACTION ISOLATION LEVEL SERIALIZABLE; |
session 2 開始事務5 設置它爲SERIALIZABLE隔離級別 |
|
SQL> SELECT last_name, salary FROM employees WHERE last_name IN ('Banda','Greene','Hintz');
LAST_NAME SALARY ------------- ---------- Banda 7100 Greene 9500 Hintz 7100 |
事務5查詢Banda,Greene和Hintz的工資。Hintz的工資是事務3更新的結果。 |
|
SQL> UPDATE employees SET salary = 7200 WHERE last_name = 'Hintz';
1 row updated. |
事務5更新Hintz的工資爲其他值,因爲之前更新Hintz的事務在事務5開始之前就提交,那麼serialized access問題被避免
注意:如果一個不同的事務更新以及提交Hintz行在事務5開始以後,那麼serialized access問題依然會出現 |
|
SQL> COMMIT; |
session2提交更新。結束這個事務 |
Read-Only Isolation Level(只讀隔離級別)
只讀隔離級別和串行化隔離級別很像,但是隻讀事務中,不允許有修改操作,除非是SYS用戶。因此,只讀事務不會有ORA-08177 錯誤,只讀事務在產生一個報告時很有效(內容必須保證是事務開始時一致的)
Oracle數據庫完成通過使用UNDO segment 重構數據塊來完成讀一致性。因爲UNDO segment通過一種循環方式使用的,那麼數據庫可以覆蓋undo數據。這時長時間查詢的報表會有風險,就是需要完成讀一致性的undo數據被其他事務覆蓋了,這時會報snapshot too old錯誤,設置undo retention period(它是舊undo數據被覆蓋前 保留的時間),恰當的避免這個錯誤。
Overview of the OracleDatabase Locking Mechanism(Oracle數據庫鎖機制概述)
lock是一種機制,用來預防 事務之間訪問通共用的數據時 的破壞性交互動作(交互動作會錯誤的更新數據,或錯誤的更改底層數據結構)。locks在維護數據庫併發性和一致性的過程中 扮演了一個至關重要的角色。
Summaryof Locking Behavior(鎖定的行爲總結)
數據庫維護一些不同類型的鎖,取決於獲取鎖的操作。一般來說,數據庫使用兩種類型的鎖:exclusive locks 和share locks。一個資源上(如一行或一個表)只能同時存在一個exclusive鎖,但是一個資源上可以同時存在多個share locks
locks會影響讀取者和修改者的交互,讀取者是一個對某資源的查詢語句,而修改者是對某資源的修改語句。
下面幾條規則總結了Oracle數據庫中 讀取者和寫入着的鎖定行爲:
·只有一個修改者在修改這一行時,這一行纔會被鎖
當一個語句更新了一行,則事務至獲取了對這一行的鎖。通過在 行級別 鎖定表中數據,數據庫實現最小爭用。在正常情況下,數據庫不會將行鎖升級爲塊鎖,甚至表鎖。
·某行的修改者會阻塞併發的同一行的其他修改者
如果一個事務在修改某一行中,那麼行鎖會阻止其他事務在這一時間修改這一行。
·讀取者永遠不會堵塞修改者
因爲某行的讀取者不會對這行上鎖。一個修改者可以修改這行。有個例外,SELECT ... FOR UPDATE語句,它是一個特殊類型的SELECT語句,會在它讀的時候鎖定這一行。
·一個修改者永遠不會堵塞讀取者
當一個修改者正在修改一行時,數據庫通過使用undo數據來對讀取者提供讀一致性。
注意:在一些非常特殊的案例中,讀可能會等待同一個塊的寫
Useof locks(鎖的使用)
在一個單用數據庫,鎖是不需要的。因爲之後一個用戶在修改信息。然而,當多用戶訪問和修改數據是,數據庫必須提供一種方法來阻止對一個數據的併發修改。鎖完成了下列重要數據庫需求:
·Consistency 一致性
一個session正在查看或修改的數據不能被其他session修改,直到用戶修改完成。
·Integrity完整性
數據和結構必須按照正確的順序來反應對它們所做的所有修改。
Oracle數據庫通過鎖機制 提供了事務之間數據的併發性和一致性,以及完整性。
鎖是根據需要自動執行的 不需要用戶操作。
對某行的併發更新可以解釋爲什麼需要鎖。在下面案例中,一個簡單的 基於網頁的應用將僱員的emaill和電話號碼呈現給終端用戶。應用使用瞭如下的UPDATE語句:
UPDATE employees
SET email = ?, phone_number = ?
WHERE employee_id= ?
AND email = ?
AND phone_number = ?
在上面的更新語句中,在WHERE子句中的emaill和phone_number是原始的,未經修改的值。這個更新保證 應用修改的行在應用上次讀和寫時是沒修改過的。這種情況下,引用避免了lost update 數據庫問題,既一個用戶更新覆蓋了另外一個用戶的更新
下表顯示了當兩個session在差不多同一時間更新employees表中相同的行時
Time |
Session 1 |
Session 2 |
Explanation |
t0 |
SELECT employee_id, email, phone_number FROM hr.employees WHERE last_name = 'Himuro';
EMPLOYEE_ID EMAIL PHONE_NUMBER ----------- ------- ------------ 118 GHIMURO 515.127.4565 |
在session 1,hr1用戶查詢hr.employees中Himuro的條目,然後顯示了employee_id,email,以及電話號碼等屬性。 |
|
t1 |
SELECT employee_id, email, phone_number FROM hr.employees WHERE last_name = 'Himuro';
EMPLOYEE_ID EMAIL PHONE_NUMBER ----------- ------- ------------ 118 GHIMURO 515.127.4565 |
在session2,hr2用戶查詢了hr.employees的Himuro記錄,顯示了employee_id,電話號碼,email等屬性。 |
|
t2 |
UPDATE hr.employees SET phone_number='515.555.1234' WHERE employee_id=118 AND email='GHIMURO' AND phone_number='515.127.4565';
1 row updated. |
在session1,hr1用戶將這行的電話號碼更新爲515.555.1234,它獲取了這行的鎖。 |
|
t3 |
|
UPDATE hr.employees SET phone_number='515.555.1235' WHERE employee_id=118 AND email='GHIMURO' AND phone_number='515.127.4565';
-- SQL*Plus does not show -- a row updated message or -- return the prompt. |
在session2,hr2用戶企圖更新相同的行,但被阻塞了,因爲hr1正在處理這行。 hr2發出的更新基本上和hr1發出的更新是同時的 |
t4 |
COMMIT;
Commit complete. |
session1 提交 提交會對Himuro行造成永久的更改,然後將對session2的阻塞放開。 |
|
t5 |
0 rows updated. |
在session2,hr2用戶發現GHIMURO行已經被修改過了,而且和update的where(謂詞)不再匹配 因爲謂詞條件不再匹配,所以session2沒有更新任何記錄。 |
|
t6 |
UPDATE hr.employees SET phone_number='515.555.1235' WHERE employee_id=118 AND email='GHIMURO' AND phone_number='515.555.1234';
1 row updated. |
在session1,hr1用戶發現剛剛對GHIMURO行更新了一個錯誤的電話號碼。用戶開始了一個新的事務然後更新電話號碼爲515.555.1235,這個東西把GHIMURO這行加了鎖。 |
|
t7 |
SELECT employee_id, email, phone_number FROM hr.employees WHERE last_name = 'Himuro';
EMPLOYEE_ID EMAIL PHONE_NUMBER ----------- ------- ------------ 118 GHIMURO 515.555.1234 |
在session2,hr2用戶查詢hr.employees中的Himuro記錄,其中顯示的電話號碼是之前session1提交的那個修改.Oracle爲了保證一致性,並沒有顯示t6這個未提交事務所做的修改. |
|
t8 |
|
UPDATE hr.employees SET phone_number='515.555.1235' WHERE employee_id=118 AND email='GHIMURO' AND phone_number='515.555.1234';
-- SQL*Plus does not show -- a row updated message or -- return the prompt. |
在session2,hr2用戶企圖更新相同的行,但是被阻塞,因爲hr1當前正在處理這一行. |
t9 |
ROLLBACK;
Rollback complete. |
在session1 rollback掉事務. |
|
t10 |
1 row updated. |
在session2,更新電話號碼的操作成功了,因爲session 1 rollback了,那麼GHIMURO的行信息依然與update中的謂詞相匹配,所以更新成功 |
|
t11 |
COMMIT;
Commit complete. |
session2提交了修改. |
Oracle數據庫在執行sql的時候會自動獲取所需要的鎖.舉個例子,在數據庫允許session修改數據之前,session必須要先鎖住數據。鎖給了session獨佔控制這個數據的權利,這樣其他事務就不能修改這條被鎖的行,直到鎖被釋放
因爲Oracle數據庫的鎖定機制和事務控制緊密相連,應用設計的時候只需要定義恰當的事務類型,Oracle數據庫會自動管理鎖。用戶永遠不需要顯式鎖定任何資源,哪怕oracle數據庫是提供了手動鎖定數據的方法。
下面小節解釋了oracle數據庫是如何完成數據併發的重要底層原理
LockModes(鎖模式)
Oracle數據庫自動使用最低限制級別去提供最高程度的數據併發 也同樣提供了非常安全的數據完整性。限制級別越低,則會有更多的可用數據提供給其他用戶,相反,更高的限制級別,則其他事務獲取鎖的類型將更多受到限制
Oracle數據庫使用兩種類型的鎖:
·Exclusive lock mode(獨佔鎖模式)
這種類型防止相關的資源成爲共享的。一個事務獲取了一個獨佔鎖(修改數據),這個事務鎖定的期間只有這個事務可以修改這個資源。
·Share lock mode (共享鎖模式)
這種模式允許相關資源成爲共享的。多用戶讀數據時可以共享數據,獲取共享鎖是預防一個寫入者要獲取獨佔鎖。一些事務可以在同一個資源上都獲取都共享鎖。
假設一個事務使用SELECT ... FOR UPDATE語句來選擇一個表中的一行。這個事務獲取了一個獨佔行鎖以及一個共享表鎖。行鎖允許其他session修改除了這行以外的其他行,而表鎖阻其他session修改表結構
LockConversion and Escalation(鎖轉換和鎖升級)
Oracle數據庫在需要的時候會執行鎖轉換。在鎖轉換中,數據庫自動將一個低限制的表鎖轉換爲高限制的表鎖。
舉個例子,假設一個事務執行SELECT ... FOR UPDATE查出一行,之後對這行進行更新。在這個情況,數據庫自動轉換行共享表鎖到行獨佔表鎖。一個事務中所有的插入,更新或刪除行,本事務都會持有這個行鎖。因爲獲取的行鎖已經是最高限制級別了,所以是不需要鎖轉換的,也不執行鎖轉換。
鎖轉換和鎖升級是不一樣的,鎖升級發生在,當某個顆粒度級別持有很多鎖(比如行),數據庫將鎖升級到更高的顆粒度級別(比如表)。如果一個用戶 鎖了一個表中的很多行,那麼一些數據庫會自動把行鎖升級爲表鎖,這樣鎖的數量就減少了,但是限制程度增加了。
Oracle數據庫永遠不會升級鎖。鎖升級極大的增加了死鎖出現的可能性。假設一個系統在事務1執行的中間打算升級鎖,但是不能升級 ,因爲事務2獲取了一些相關的鎖。如果事務2這時同樣申請相同數據的鎖升級 纔可以繼續往下執行,那麼死鎖就會出現。
LockDuration(鎖持續時間)
當某些事件發生,導致事務不再需要這些資源時,Oracle數據庫自動釋放鎖。絕大部分情況,事務中的語句獲取並持有了鎖,持續到事務結束。這些鎖阻止了破壞性干涉,比如髒讀,更新丟失,以及併發事務發出的破壞性DDL語句
注意:因爲未索引外鍵而在子表上發生的表鎖,會在語句持續時間持有,而不是事務持續時間。同樣,像“Overview of User-Defined Locks”解釋的,DBMS_LOCK包使用戶自定義的鎖 可以隨意持有或釋放(根據你的意志),持有時間甚至可以超越事務的邊界。
事務中語句獲取的鎖,在事務結束時 Oracle會全部釋放(commit和rollback)。當事務中存了一個savepoint,在其後獲取了鎖,之後又回滾到savepoint,這個鎖也會被釋放掉。
但是,只有事務沒有等待之前被鎖資源的,可以獲取現在已經可以使用的鎖,而之前已經在等待的事務,叫繼續等待到原始事務完全提交或回滾。
Locksand Deadlocks(鎖和死鎖)
兩個事務或者多個事務 都在等待被對方所持有的資源,這種情況叫做死鎖。死鎖會阻止某些事務繼續工作。
Oracle數據庫會自動檢測死鎖,並通過回滾其中一個語句來解決死鎖。數據庫會對遭遇語句級回滾的事務返回一條相應的消息。回滾的語句是屬於檢測到死鎖這個事務的。通常來說,收到信號的事務應該要顯示回滾,不過它也可以在等待之後重試被 語句級回滾 的語句。
下表解釋了兩個事務中的死鎖。
Time |
Session 1 |
Session 2 |
Explanation |
t0 |
SQL> UPDATE employees SET salary = salary*1.1 WHERE employee_id = 100;
1 row updated. |
SQL> UPDATE employees SET salary = salary*1.1 WHERE employee_id = 200;
1 row updated. |
session 1 開始 事務1 然後更新employee 100的 工資,session2開始事務2更新employee 200的工資。現在沒有任何問題。 |
t1 |
SQL> UPDATE employees SET salary = salary*1.1 WHERE employee_id = 200;
-- prompt does not return |
SQL> UPDATE employees set salary = salary*1.1 WHERE employee_id = 100;
-- prompt does not return |
事務1現在企圖更新employee200的行,這行現在被事務2鎖着的。事務2企圖更新employee100的行,這行是被事務1鎖着的 現在有了一個死鎖,因爲兩個事務都不能獲取它需要的資源然後往後執行或者完成。不管每個事務等待多長時間,衝突依然存在。 |
t2 |
UPDATE employees * ERROR at line 1: ORA-00060: deadlock detected while waiting for resource
SQL> |
事務1發出死鎖信號,並且回滾t1所發的更新語句。但是,t0所發的更新語句並沒有回滾。session 1中收到了錯誤提示。 注意:在死鎖中,只有其中的一個session會有死鎖錯誤,但是任何一個事務都可能獲得這個錯誤。 |
|
t3 |
SQL> COMMIT;
Commit complete. |
session 1提交了在t0發出的更新操作,結束了事務 1.在t1發的更新沒有成功。 |
|
t4 |
1 row updated.
SQL> |
事務 2在t1時提交的更新(之前被事務1阻塞),現在執行成功,返回提示。 |
|
t5 |
SQL> COMMIT;
Commit complete. |
session 2提交了 t0和t1所做的更新。結束了事務2. |
當事務中顯示手動加鎖,覆蓋了Oracle數據庫原本的鎖時,最經常發生死鎖。因爲Oracle數據庫不會升級鎖,不會有讀鎖,使用的是行鎖(而不是頁鎖),所以很少出現死鎖。
Overview of Automatic Locks
Oracle數據庫會爲事務自動鎖定資源,防止其他事務對相同數據執行一些需要獨佔訪問的操作。數據庫根據執行的操作和對應的資源的不同限制程度來自動獲取不同的鎖。
注意:執行簡單的讀時,數據庫永遠不會對這些行上鎖。
Oracle數據庫的鎖 分爲以下幾個類別
Lock |
Description |
DML Locks |
保護數據,舉個例子,表鎖 鎖整個表,而行鎖 則鎖指定的行 |
DDL Locks |
保護schema objects的結構--舉個例子,表和視圖的定義 |
System Locks |
保護內部的數據庫結構,比如數據文件,latchs,mutexes以及內部鎖,這些都是完全自動的 |
DMLLocks(DML 鎖)
一個DML鎖,同樣也叫做data lock(數據鎖),保證數據被多用戶併發訪問時的完整性。舉個例子,一個DML鎖可以防止出現在網上書店中,兩個客戶同時購買了最後一本書的情況。DML鎖防止多個互相沖突DDL,DML操作對數據的破壞性
DML語句自動獲取兩種類型的鎖:
·Row Locks(TX)
·Table Locks(TM)
在下面小節中,每個鎖或鎖模式後面括號中的縮寫,就是在OracleEM的Locks Monitor中使用的縮略語。EM中可能用TM來任何表鎖,而不是顯示哪種表鎖(比如RS 或SRX)。
RowLocks(TX)行鎖
行鎖,同樣也被稱爲TX鎖,是在表中某行上面的鎖。一個事務中通過INSERT,UPDATE,DELETE,MERGE或SELECT....FOR UPDATE修改的任何行,上面都會加行鎖。行鎖會持續到事務結束(提交或回滾)
行鎖主要是提供一個排序機制來防止兩個事務同時修改一行。數據庫對修改過的行 加的都是獨佔模式的行鎖,這樣其他事務就沒辦法在同時對這行修改(直到持有鎖的事務提交或回滾)。行鎖提供了儘可能細粒度的鎖定,這樣數據庫就能儘可能提供更好的併發性和吞吐。
注意:如果一個事務因爲數據庫異常關閉,會先進行塊級恢復以使行可用,然後再進行整個事務的恢復。
如果一個事務獲取了一行的行鎖,那麼事務同樣獲取了包含這行的表 的表鎖。這表鎖將阻止衝突性DDL操作(它或許會覆蓋當前事務中的修改)。
下面圖解了一個表中一行的更新。Oracle數據庫自動防止一個獨佔鎖到被更新的行,以及一個子獨佔鎖到表。
RowLocks and Concurrency(行鎖和併發性)
下表解釋了Oracle數據庫通過行鎖實現併發性。三個session同時查詢相同的行,session1和2繼續更新了兩條不同的行(不提交),而session3沒有做任何更新。每個session都能看到它自己做的未提交更新,但是不能看到其他session的未提交更新。
Time |
Session 1 |
Session 2 |
Session 3 |
Explanation |
t0 |
SELECT employee_id, salary FROM employees WHERE employee_id IN ( 100, 101 );
EMPLOYEE_ID SALARY ----------- ------ 100 512 101 600 |
SELECT employee_id, salary FROM employees WHERE employee_id IN ( 100, 101 );
EMPLOYEE_ID SALARY ----------- ------ 100 512 101 600 |
SELECT employee_id, salary FROM employees WHERE employee_id IN ( 100, 101 );
EMPLOYEE_ID SALARY ----------- ------ 100 512 101 600 |
三個不同的session同事查詢employee100,101的ID和薪資。每個查詢返回的結果都是相同的 |
t1 |
UPDATE hr.employees SET salary=salary+100 WHERE employee_id=100; |
|
|
session1更新了僱員100的工資,但沒有提交。在更新的過程中,寫入者只需要獲取更行的行級別鎖,從而防止其他寫入者修改這一行。 |
t2 |
SELECT employee_id, salary FROM employees WHERE employee_id IN ( 100, 101 );
EMPLOYEE_ID SALARY ----------- ------ 100 612 101 600 |
SELECT employee_id, salary FROM employees WHERE employee_id IN ( 100, 101 );
EMPLOYEE_ID SALARY ----------- ------ 100 512 101 600 |
SELECT employee_id, salary FROM employees WHERE employee_id IN ( 100, 101 );
EMPLOYEE_ID SALARY ----------- ------ 100 512 101 600 |
每個session同事查之前的查詢。session1顯示的是t1更新的612.而session2和session3的讀取者立刻返回了行,沒有等到session1結束它的事務。數據庫使用多版本讀一致性來顯示session1 更新之前的工資(512)。 |
t3 |
|
UPDATE hr.employees SET salary=salary+100 WHERE employee_id=101; |
|
session2更新employee101的工資,但是沒有提交。在更新中,寫入者只獲取了更新行的行級鎖,防止其他寫入者修改這一行。 |
t4 |
SELECT employee_id, salary FROM employees WHERE employee_id IN ( 100, 101 );
EMPLOYEE_ID SALARY ----------- ------ 100 612 101 600 |
SELECT employee_id, salaryFROM employees WHERE employee_id IN ( 100, 101 );
EMPLOYEE_ID SALARY ----------- ------ 100 512 101 700 |
SELECT employee_id, salary FROM employees WHERE employee_id IN ( 100, 101 );
EMPLOYEE_ID SALARY ----------- ------ 100 512 101 600 |
每個session同事執行之前的查詢。session1顯示的是612(t1更新),但是沒顯示session2對employee101做的更新。session2的讀取者顯示了session2自己做的更新,但是沒顯示session1做的更新。session3的讀取者使用讀一致性來顯示session1和session2做修改之前的值。 |
Storageof Row Locks(行鎖的存儲)
有一些數據庫使用lock manager(鎖管理器)在內存中維護了一個(鎖的列表),和它們不同,Oracle數據庫將鎖信息存儲到包含了 被鎖行 的 data block(數據塊)中
數據庫使用排隊算法來獲取行鎖。如果一個事務需要一個行(未被加鎖)的鎖,那麼事務將放置一個鎖到數據塊中。這個事務修改的每行,都指向一個存儲在block header(塊頭)中的事務id副本。
當一個事務結束,事務id依然在block header。如果一個不同的事務想要修改這行,那麼它會使用事務ID去確認鎖時候在使用。如果鎖是active的,那麼session會要求鎖釋放時得到提醒。否則 ,這個事務獲取這個鎖。
TableLocks(TM)
表鎖,同樣也叫TM鎖,當事務通過INSERT,UPDATE,DELETE,MERGE,SELECT..FOR UPDATE修改表中數據,或者直接用LOCK TABLE語句,這時會獲取TM鎖。DML操作獲取表鎖,是保證DML對這個表的訪問,以及防止會發生衝突DDL語句。
表鎖可以以下幾種模式持有:
·Row Share(RS) (行共享表鎖)
這種鎖,同樣叫做subshare table lock(SS),表明 事務在這個表上鎖住了一行,並且打算更新它們。RS鎖是表鎖中最小約束性的了。提供了表級別的最高併發
·Row Exclusive Table Lock(RX)(行排他 表鎖)
這個鎖,又叫做 subexclusive table lock(SX),一般表明所在表中有事務更新了行,或者發佈了SELECT ... FOR UPDATE。一個SX鎖允許其他事務同時查詢,插入,更新,刪除,或者鎖行。因此多個事務可以同時獲取在同一個表上獲取SS和SX鎖。
·Share Table Lock(S)(共享表鎖)
一個事務持有了S鎖,允許其他事務查詢這個表(除了使用SELECT...FOR UPDATE),但更新操作只允許單個事務持有該鎖時才允許。因爲多個事務可能會併發持有這個鎖,那麼持有了這個鎖並不足以保證這個事務可以修改表
·Share Row Exclusive Table Lock(SRX)(共享 行排他 表鎖)
這個鎖,同樣爲叫share-subexclusive table lock(SSX),它比S鎖的限制更多。一次只能有一個事務獲取SSX鎖。一個事務持有SSX鎖,允許其他事務查詢表(除了for update)不允許更新表。
·Exclusive Table Lock(X)
這個鎖有最大的限制,禁止其他事務執行任何DML語句或放置任何類型的鎖 到這個表。
Locksand Foreign Keys(鎖和外鍵)
Oracle數據庫對外鍵所引用的父鍵提供最大的併發控制。鎖的行爲依賴於子表外鍵列是否有索引。如果外鍵沒有索引,那麼子表會更頻繁的被鎖,出現死鎖,以及減小併發。因爲這個原因,外鍵列最好都建索引,唯一的例外就是引用的父鍵永遠不會更新或刪除。
Locksand Unindexed Foreign Keys(鎖和未索引外鍵)
當下面兩個條件都符合,那麼數據庫將會在子表獲取全表鎖。
·子表的外鍵列沒有存在索引
·一個session修改父表中的 被引用建(父鍵)。對父表進行插入不會獲取子表的全表鎖。
假設hr.departments表 是hr.employees表的 父表,而hr.employees表的外鍵department_id沒加索引。
下面的圖顯示當一個session修改departments表的被引用鍵列中的值60時會發生什麼。
在上面的圖,數據庫在父表(departments)的被引用列發生修改期間,會持續持有employees表的全表鎖,這個鎖導致其他事務可以查詢,但是不能更新employees表。舉個例子,employees表的僱員電話號碼在此期間不能更新。當父表(departments)的被引用列修改完畢以後,employees表的全表鎖會立即釋放(不會一直鎖到事務結束)。如果departments表的多行經歷父鍵列被修改,那麼employees表的全表鎖 會在每行修改時獲取,每行修改完釋放,一直到多行修改完畢位置(每行獲取 釋放一次)
注意:在子表的DML不會獲取父表的全表鎖
Locksand indexed Foreign Keys(鎖和加了索引的外鍵)
當下面兩個條件符合,數據庫不會獲取子表的全表鎖:
·子表中的外鍵列加了索引
·session修改父表中的被引用列
在父表上的鎖 預防其他事務獲取父表的排外表鎖,但是不會阻止在父表上的DML語句,也不會在父表上DML執行期間阻止子表被修改。
最好要加索引。如果父表上的父鍵(被引用鍵)發生修改時,而子表也同時在發生更新
下圖顯示了子表employees以及加了索引的外鍵列 department_id。一個事務刪除了departments表的部門280行。這個刪除不會像外鍵爲加索引時 導致數據庫獲取employees表的全表鎖
如果子表指定了ON DELETE CASCADE,那麼父表的刪除會導致子表的刪除。舉個例子,刪除部門280可以導致employees中的和280部門相關的行被刪除。在這種情況,等待以及鎖的規則 和你先刪除子表中相關行,然後再刪除父表中的相關行 是一模一樣
DDLLOCKS(DDL鎖)
data dictionary lock 數據字典鎖 在一個DDL命令操作或者引用到某個對象時,保護它們的schema object 定義。只有被操作或引用的對象上會加DDL鎖。數據庫永遠不會鎖定整個data dictonary(數據字典).
當執行DDL事務 時需要DDL鎖時 Oracle數據庫會自動獲取它。用戶不可以顯式請求DDL鎖。舉個例子,如果用戶正在創建一個存儲過程,那麼Oracle數據庫會自動獲取這個存儲過程所有引用到的schema objects 上的DDL。這些DDL鎖是爲了預防 在存儲過程還沒編譯完成時 相關的對象就被刪除或者修改結構了。
ExclusiveDDL Locks(獨佔DDL鎖)
獨佔DDL鎖,阻止其他session獲取DDL或DML鎖。除了在“Share DDL Locks”中說明的,剩餘絕大部分DDL鎖都是獨佔鎖,防止其他DDL操作可能修改或應用這個資源而導致毀壞性影響。舉個例子,DROP TABLE命令是不允許當ALTER TABLE還在執行時 就刪除表,反之亦然。
獨佔DDL鎖在DDL語句執行期間和自動提交期間持續存在。在請求獨佔DDL鎖時,如果該對象的DDL鎖已經被另外的操作持有,那麼請求會等待,等待到舊的DDL鎖釋放,它纔會繼續。
ShareDDL Locks(共享DDL鎖)
共享DDL做用來阻止與之相沖突的DDL操作,但是允許相似的DDL操作併發執行。
舉個例子,當一個CREATE PROCEDURE語句執行,這個語句包含的事務會獲取所有引用的表的共享DDL鎖。其他事務也可以在相應的表上併發的CREATE PROCEDURE獲取共享DDL鎖,但是不可以在被引用的任何一個表上獲取獨佔DDL鎖
共享DDL鎖在DDL語句執行和自動提交期間 持續持有。因此一個事務持有共享DDL鎖保證被引用的對象在整個事務執行期間是 恆定的(結構沒發生變化)
BreakableParse Locks(可中斷解析鎖)
解析鎖由 SQL語句,或者PL/SQL程序單元 在其引用的相關對象上持有。如果獲取瞭解析鎖,那麼當引用的對象發生結構改變或者drop時,相關shared sql area 可以被標爲無效。
解析鎖 被稱之爲 可中斷解析鎖 是因爲它不會阻撓任何DDL操作,且可以被衝突的DDL操作打斷這個鎖。
當SQL語句執行到parse phase(解析階段),在shared pool(共享池)中獲取的。只要這個語句的shared sql are仍然停留在 shared pool,這個鎖就會一直持有。
SystemLocks(系統鎖)
Oracle 數據庫使用多種類型的系統鎖來保護 數據庫和內存的內部結構。由於用戶無法控制它們何時出現和持續多久,這些機制對於用戶來說幾乎是不可訪問的。
Latches(不翻了。就叫latch)
latch是簡單,低級別的串行化機制,它協調多個用戶訪問 這些 共享的數據結構、對象、文件。
當多個進程同時讀取一個共享內存資源時使用latch保護其不受損壞。
具體來說,latch在以下情況保護數據結構:
·多個session的併發修改
·讀取一個正在被別人修改的資源
·當訪問的時候,內存被釋放(換出)
基本來說,在SGA中,單個latch會保護多個對象。舉個例子,後臺進程,如DBWn和LGWR從shared pool分配了內存去創建數據結構。爲了分配這個內存,這些進程使用shared pool latch 串行訪問,防止兩個進程同時檢查或修改shared pool..內存分配完成以後,其他進程可能需要訪問 shared pool areas 比如library cache(解析時需要).這個情況下,進程會只把library cache latch住,而不latch 整個shared pool.
和行鎖之類enqueue latches的不同,latch是不允許session排隊.當一個latch變爲可用時,第一個請求這個latch的將獲取,並獨佔訪問它。Latch spinning發生在當一個進程重複循環請求一個latch時,而latch sleeping發生重新latch 請求之前,釋放CPU時。
基本來說,一個Oracle進程在操作或查看一個數據結構時 會極端短時間內獲取一個latch。舉個例子,僅僅爲某一名員工處理工資更新,數據庫就可能需要獲取並釋放成千上萬個latchs. . latch的實現是依賴操作系統的,特別是關於一個進程是否會等待latch和等待多長時間
latch的時間增長,意味着併發性的下降。舉個例子,過多的hard parse(硬解析)操作會對library cache latch 造成衝突。V$LATCH事務包含了每個latch的使用狀態的詳細信息,包括latch被請求和被等待的次數。
Mutexes(互斥)
mutual exclusionobject互相排斥的對象(mutex)是一個低級別的機制,防止內存中的對象被並行訪問時 被換出或被損壞。mutex和latch很相似,但是一個latch基本上都保護一組object。而mutex保護單個object。
mutexes有幾個優勢:
·mutex可以減少爭用的可能性
因爲一個latch保護多個objects,當很多進程要併發訪問被它保護的不同對象時,它可能會成爲瓶頸。通過 串行化訪問一個單一的對象 ,而不是一組,mutex增加了可用性
·mutex比latch消耗更少的內存
·當進入共享模式,一個mutex允許被多個session併發引用
InternalLocks(內部鎖)
內部鎖是高級別的鎖,比latch和mutexes更復雜的機制,並用於各種目的。數據庫使用以下類型的內部鎖:
·Dictionary cache locks (字典cache鎖)
當dictionary cache中的條目被修改或使用時會非常短時間的持有這個鎖。它們保證語句解析時不會看到不一致的對象定義。Dictionary cache locks可以是共享的,也可以是獨佔的。共享鎖會在解析完成後釋放,而獨佔鎖會在DDL語句執行完成後才釋放
·File and log managerment locks(文件以及日誌 管理鎖)
這些鎖保護各種文件。舉個例子,一個內部鎖保護control file(控制文件)這樣一次只能有一個進程修改它。另一個鎖協調online redo log文件的使用和歸檔。data files(數據文件)被鎖住 這樣可以保證多個instance(實例)通過共享模式來mount一個數據庫,或一個instance通過獨佔模式來mount一個數據庫。因爲文件和日誌鎖表示文件的狀態,這些鎖必要時會持有很長時間。
·Tablespace and undo segment locks(表空間和undo段鎖)
這些鎖保護表空間和undo段。舉個例子,訪問一個數據庫的所有實例 必須對一個表空間的狀態是否處於聯機還是離線必須保持一致。undo 段是被鎖定的 這樣只有一個實例可以寫入該段。
Overview of Manual Data Locks(手動數據鎖概述)
Oracle數據庫通過自動執行鎖定來保證數據併發性和數據完整性,以及語句級的讀一致性。但是你可以通過手動覆蓋Oracle數據庫默認的鎖機制。在下面情況中,手動覆蓋默認的鎖是有意義的:
·應用需要事務級讀一致性或可重複讀。
這個情況,一個事務內的查詢必須返回相同的數據,不反應其他事務的修改。完成這個事務級讀一致性可以通過使用 顯式鎖定,只讀事務,serializable事務,或覆蓋默認鎖定。
·應用需要一個應用對一個資源獨佔訪問,這樣這個事務就不需要等待其他事務的完成了
你可以在session級和事務級覆蓋Oracle數據庫的自動鎖定。在session級別,session可以通過使用ALTER SESSION語句設置需要的事務隔離級別。在事務級別,事務中包含如下語句時可以覆蓋Oracle數據庫的默認鎖定:
·SET TRANSACTION ISOLATION LEVEL語句
·LOCK TABLE語句(鎖定一張表或視圖的所有基表)
·SELECT ... FOR UPDATE語句
通過上面語句獲取的鎖 會在事務結束 或回滾到獲取鎖之前的savepoint 纔會釋放。
無論Oracle數據庫默認鎖定在什麼級別被覆蓋,DBA和開發都要保證覆蓋鎖定程序的操作是正確的。鎖定程序必須滿足下面標準:保證數據完整性,可以接受的數據併發性,死鎖不會發生(或偶爾發生 可以被合適的處理)
Overview of User-Defined Locks(用戶自定義鎖概述)
使用Oracle數據庫鎖管理服務,你可以爲指定的應用定義自己的鎖。舉個例子,你可以創建一個鎖去串行化訪問文件系統上的一條消息日誌。因爲保留的用戶鎖和oracle數據庫的鎖一樣,它有Oracle數據庫中鎖所有的功能(包括死鎖檢測),用戶的鎖和數據庫的鎖永遠不會衝突,因爲用UL前綴標識
Oracle數據庫鎖管理服務可以通過DBMS_LOCK包調用,你可以在PL/SQL 塊中包含如下語句:
·請求指定類型的鎖
·給鎖一個唯一的名字,用來和實例中的其他程序中的鎖相區別
·修改鎖的類型
·釋放鎖