多用戶環境下的數據併發訪問及數據一致性簡介
數據庫
在只有單一用戶的數據庫中,用戶能夠任意修改數據,而無需考慮同時有其餘用戶正在修改相同的數據。但在一個多用戶數據庫中,多個併發事務中包含的語句可能 會修改相同的數據。數據庫中併發執行的事務最終應產生有意義且具有一致性的結果。所以在多用戶數據庫中,對數據併發訪問(data concurrency)及數據一致性(data consistency)進行控制是兩項極爲重要的工做。 緩存
一、數據併發訪問指多用戶同時訪問相同的數據。 網絡
二、數據一致性指系統中每一個用戶都可以取得具有一致性的數據,同時還可以看到本身或其餘用戶所提交的事務對數據的修改。 併發
爲了描述同時執行的多個事務如何實現數據一致性,數據庫研究人員定義了被稱爲串行化處理(serializability)的事務隔離模型(transaction isolation model)。當全部事務都採起串行化的模式執行時,咱們能夠認爲同一時間只有一個事務在運行(串行的),而非併發的。 函數
以串行化模式對事務進行隔離的效果很好,但在此種模式下應用程序的效率將大大下降。將並行執行的事務徹底隔離意味着即使當前只存在一個對錶進行查詢(query)的事務,其餘事務也不能再對此表進行插入(insert)操做了。總之,爲了知足實際要求,咱們須要在事務的隔離程度與應用的性能之間找出一個平衡點。 性能
Oracle 支持兩種事務隔離級別(isolation level),使應用程序開發者在對事務進行控制時,既能保證數據的一致性,又能得到良好的性能。 spa
須要防止的現象和事務隔離級別 設計
ANSI/ISO SQL 標準(SQL92)定義了四種事務隔離級別(transaction isolation level),這四種隔離級別所能提供的事務處理能力各不相同。這些事務隔離級別是針對三種現象定義的,在併發事務執行時,須要阻止這三種現象 中的一種或多種發生。 事務
三種須要阻止的現象(preventable phenomena)是: 內存
一、髒讀取(dirty read):一個事務讀取了被其餘事務寫入但還未提交的數據。
二、不可重複讀取(nonrepeatable read):一個事務再次讀取其以前曾經讀取過的數據時,發現數據已被其餘已提交的事務修改或刪除。
三、不存在讀取(phantom read):事務按照以前的條件從新查詢時,返回的結果集中包含其餘已提交事務插入的知足條件的新數據。
SQL92 標準中定義了四個隔離級別,在各隔離級別中,容許發生上述三種須要阻止的現象中的一種或多種。詳情見下表
現象 | 髒讀取 | 不可重複讀取 | 不存在讀取 |
隔離級別 | |||
|
|||
未提交讀取(read uncommitted) |
容許 | 容許 | 容許 |
已提交讀取(read committed) |
不容許 |
容許 | 容許 |
可重複讀取(repeatable read) |
不容許 |
不容許 | 容許 |
串行化(rerializable) |
不容許 |
不容許 | 不容許 |
Oracle 支持三種事務隔離級別:已提交讀取,串行化,以及 SQL92 中沒有包含的只讀模式(read-only mode)。已提交讀取是 Oracle 默認使用的事務隔離級別。
Oracle 如何管理數據併發訪問及數據一致性
Oracle 利用多版本一致性模型(multiversion consistency model),各類類型的鎖及事務來管理多用戶系統中的數據一致性(data consistency)。
多版本併發訪問控制
Oracle 可以自動地實現一個查詢的讀一致性,即一個查詢所得到的數據來自同一時間點(single point in time)(這也被稱爲語句級讀一致性(statement-level read consistency))。Oracle 還能令一個事務內的全部查詢都具有讀一致性(即事務級讀一致性(transaction-level read consistency))。
Oracle 利用回滾段中的信息生成一個能保證一致性的數據視圖。回滾段內保存了未提交或最近提交的事務中所修改數據的原值。下圖展現了 Oracle 如何利用回滾段實現語句級的讀一致性。
在查詢開始執行時,將記錄當前的系統變化編號(system change number,SCN)。在 圖 中,記錄的系統變化編號爲 10023。當查詢進行掃描時,只會使用有效的(observed)數據塊。若是某個數據塊內的數據被修改過(即數據塊的 SCN 晚於查詢開始執行時記錄的 SCN),Oracle 將使用回滾段中的信息重建此數據塊,並以重建的數據塊替代被修改的數據塊供查詢使用。所以,查詢的結果集只包含查詢開始執行時就已經提交的數據。在查詢執行時,其餘事務修改的數據對此查詢來講是無效的,這保證了每一個查詢都能獲得知足一致性的數據。
語句級讀一致性
Oracle 強制實現語句級讀一致性(statement-level read consistency)。這保證了單一查詢的結果集來自一個時間點——即查詢開始執行的時間。所以,一個查詢的結果集永遠不會包含髒數據及此查詢執行時 其餘事務提交的數據。在一個查詢執行期間,只有在查詢執行前提交的數據對此查詢纔是可見的。查詢沒法看到其開始執行後提交的數據。
任何一個查詢都能獲得知足一致性的結果集,這保證了用戶無需額外操做就能確保數據一致性。 SELECT ,使用子查詢的INSERT ,及包含顯式或隱式查詢的 UPDATE 或 DELETE 語句,都可以保證數據一致性。上述語句經過一個查詢(query)來獲得她們所需的知足一致性的結果集(分別使用 SELECT,INSERT,UPDATE 或 DELETE 語句)。
SELECT 語句是一個顯式地查詢,且其中能夠包含嵌套查詢(nested query)或鏈接操做(join operation)。 INSERT 語句中也可以使用嵌套查詢。 UPDATE 及 DELETE 語句可以利用 WHERE 子句或子查詢進行限制,只操做數據表內的部分數據行。
INSERT , UPDATE ,及 DELETE 語句中包含的查詢可以得到一致性的結果集。這些查詢沒法看到其所在 DML 語句對數據的修改。換句話說,這些查詢只能看到其所在 DML 語句開始以前的數據。
TIPS:若是 SELECT 列表中存在 PL/SQL 函數,那麼函數中包含的 SQL 語句將聽從其自身的語句級讀一致性,而非其所在 SQL 的讀一致性。例如, SELECT 語句中的某個函數訪問的表可能會在語句執行時被其餘事務修改並提交。此函數每次執行時都將創建一個新的一致性視圖(snapshot)。
事務一致性讀
Oracle 還可以實現事務級讀一致性(transaction-level read consistency)。當一個事務運行在串行化模式(serializable mode)下時,則事務內全部數據訪問均反映的是事務開始時的數據狀態。即事務內的全部查詢對某個時間點來講具有一致性,可是運行在串行化模式下的事務可以看到事務自身對數據所做的修改。事務級的讀一致性可以保證可重複讀取並可阻止出現不存在讀取。
RAC環境下的讀一致性
RAC 系統採用緩存對緩存(cache-to-cache)的數據塊傳輸機制(此技術被稱爲 Cache Fusion)在實例間傳輸知足讀一致性(read-consistent)的數據塊鏡像。RAC 系統經過高速度低延遲的內部鏈接(interconnect)實現上述數據傳輸,從而知足實例之間對數據塊的請求。
Oracle 事務隔離級別
Oracle 支持如下三種事務隔離級別(transaction isolation level)。
隔離級別 | 描述 |
|
|
已提交讀取 | Oracle 默認使用的事務隔離級別。事務內執行的查詢只能看到查詢執行前(而非事務開始前)就已經提交的數據。Oracle 的查詢永遠不會讀取髒數據(未提交的數據)。 Oracle 不會阻止一個事務修改另外一事務中的查詢正在訪問的數據,所以在一個事務內的兩個查詢的執行間歇期間,數據有可能被其餘事務修改。舉例來講,若是一個事務內同一查詢執行兩次,可能會遇到不可重複讀取或不存在讀取的現象。 |
串行化 | 串行化隔離的事務只能看到事務執行前就已經提交的數據,以及事務內 INSERT ,UPDATE ,及 DELETE 語句對數據的修改。串行化隔離的事務不會出現不可重複讀取或不存在讀取的現象。 |
只讀模式 | 只讀事務只能看到事務執行前就已經提交的數據,且事務中不能執行 INSERT ,UPDATE ,及 DELETE 語句。 |
應用程序的設計開發者及數據庫管理員能夠依據應用程序的需求及系統負載(workload)而爲不一樣的事務選擇不一樣的隔離級別(isolation level)。用戶能夠在事務開始時使用如下語句設定事務的隔離級別:
已提交讀模式:SET TRANSACTION ISOLATION LEVEL=READ COMMITTED;
串行模式:SET TRANSACTION ISOLATION LEVEL= SERIALIZABLE;
只讀模式:SET TRANSACTION= READ ONLY;
(經筆者實驗以上命令在非sysdba用戶下能夠成功執行,sysdba下不能執行,待驗證正確性)
若是在每一個事務開始時都使用 SET TRANSACTION 語句,將加劇網絡及處理器的負擔。用戶可使用 ALTER SESSION語句 改變一個會話全部內事務的默認隔離級別:
一、ALTER SESSION SET ISOLATION_LEVEL SERIALIZABLE;
二、ALTER SESSION SET ISOLATION_LEVEL READ COMMITTED;
(沒法設置會話級別的只讀隔離模式)
已提交讀取隔離
Oracle 默認使用的隔離級別(isolation level)是已提交讀取(read committed)隔離。這種程度的隔離適合在事務發生衝突的可能性較小的系統中使用。在這種隔離級別下,Oracle 可以保證事務內每一個查詢在執行期間都擁有一個惟一的數據視圖,所以事務可能出現不可重複讀取或不存在讀取的現象,但此時系統的數據處理能力較高。
串行化隔離
符合如下特性的系統適合採用串行化隔離(serializable isolation):
一、數據量大,但事務短小,只會更新較少數據行的數據庫
二、兩個併發事務修改相同數據的機率較小
三、運行時間相對較長的事務只執行只讀操做
在串行化隔離下,併發事務對數據庫進行修改時只能順序執行。具體來講,在串行化隔離下,Oracle 在容許一個採用串行化隔離的事務修改某些數據行時,須要判斷在此事務開始執行以前,其餘全部事務對這些數據行的修改已經被提交。
爲了實現上述判斷,Oracle 在數據塊內存儲了相關的控制信息,用於記錄此塊內數據行中所包含的數據是已提交或未提交的。即數據塊內記錄了近期對本數據塊內數據行進行了修改的全部事務及事務的狀態。在一個數據塊內可以保留多少這樣的記錄是由 CREATE TABLE 或 ALTER TABLE 語句中的 INITRANS 參數設定的。
有些狀況下,Oracle 沒法得到足夠的歷史信息來判斷某個數據行是否被一個事務修改過。當大量事務在短期內併發地修改同一數據塊就會出現以上狀況。用戶能夠爲可能被多個事務同時更新相同數據塊的表設置較大的 INITRANS 值,以便避免上述狀況。設置了較大的 INITRANS 值後,Oracle 就能爲每一個數據塊分配足夠的空間來記錄訪問此數據塊的事務的信息。
當一個串行化事務試圖更新或刪除數據,而這些數據在此事務開始後被其餘事務修改並進行了提交,Oracle 將報錯:
ORA-08177: 沒法進行串行化訪問
當一個串行化事務由於 沒法進行串行化訪問 (Cannot serialize access)錯誤而失敗時,應用程序能夠 選擇如下幾種處理方式:
一、將錯誤發生以前的操做提交
二、執行其餘操做(執行前能夠回滾到事務內的某個保存點)
三、撤銷整個事務
下圖顯示了一個事務遇到 沒法進行串行化訪問 後, 程序進行回滾並嘗試從新執行此事務的例子:
圖中顯示了一個串行化事務,其中首先執行了兩個相同的 SELECT 語句,接着執行了一個 UPDATE 語句。即使在兩個 SELECT 執行之間有其餘事務修改了相關數據,這兩個 SELECT 也可以返回相同的結果。當 UPDATE 語句更新數據時,所更新的數據在此事務開始後被其餘事務修改並提交過,Oracle 將報錯 Cannot Serialize Access。這個錯誤將致使事務回滾並嘗試從新執行。
已提交讀取隔離與串行化隔離的區別
Oracle 爲應用程序開發者提供了兩種特性相異的事務隔離級別。已提交讀取隔離和串行化隔離都能實現高度的數據一致性及併發訪問能力。這兩種隔離級別都可以利用 Oracle 的讀一致性多版本併發訪問控制模型及獨有的行級鎖(row-level locking)技術,從而減小併發事務間的競爭。應用程序開發者可使用這兩種隔離級別開發符合現實要求的應用系統。
事務集數據一致性
咱們能夠參考如下場景來研究 Oracle 中的兩種隔離級別:假設現有一組數據庫表(或稱爲一組數據集),一系列讀取表數據的查詢,以及一組在任意時間提交的事務。若是一個數據庫操做(一個查詢或一個事務)中全部讀取返回的數據是由同一組已提交事務寫入的,咱們就稱此操做知足事務集數據一致性(transaction set consistent)。相反,當一個數據庫操做內的不一樣讀取反映了不一樣事務集對數據的修改,此操做就不知足事務集數據一致性。換句話說,一個不知足事務集數據一致性的操做所看到的數據庫的狀態是由不一樣的已提交事務集決定的。
在已提交讀取隔離模式下,Oracle 能保證每一個語句的事務集數據一致性。而在串行化隔離模式下,Oracle 能保證每一個事務的事務集數據一致性。
下表總結了 Oracle 中已提交讀取事務和串行化事務的關鍵區別。
已提交讀取 |
串行化 |
|
|
||
髒寫入(dirty write) |
不可能 |
不可能 |
髒讀取(dirty read) |
不可能 |
不可能 |
不可重複讀取(nonrepeatable read) |
可能 |
不可能 |
不存在讀取(phantom) |
可能 |
不可能 |
與 ANSI/ISO SQL 92 標準兼容 |
是 |
是 |
惟一的數據視圖的使用範圍 |
語句 |
事務 |
事務集數據一致性 |
語句級 |
事務級 |
行級鎖 |
是 |
是 |
讀操做(reader)阻塞寫操做(writer) |
否 |
否 |
寫操做阻塞讀操做 |
否 |
否 |
針對不一樣數據行的寫操做 是否相互阻塞 |
否 |
否 |
針對相同數據行的寫操做 是否相互阻塞 |
是 |
是 |
等待致使阻塞的事務(blocking transaction) |
是 |
是 |
會出現 沒法進行串行化訪問 (Cannot serialize access)錯誤 |
否 |
是 |
在致使阻塞的事務結束後 將發生錯誤 |
否 |
否 |
在致使阻塞的事務提交後 將發生錯誤 |
否 |
是 |
選擇隔離級別
已提交讀取隔離
對於大多數應用來講,已提交讀取隔離是最適合的事務隔離級別。已提交讀取隔離可以最大限度地保證數據併發性,但在某些事務中可能會出現不可重複讀取或不存在讀取,所以略微增長了出現數據不一致性的風險。
在對性能要求較高的系統中,爲了應對較高的事務到來率(transaction arrival rate),系統須要提供更大的事務吞吐量和更快的響應速度,此時採用串行化隔離可能難以實現。還有一類系統,其事務到來率較低,出現不可重複讀取或不存在讀取的風險也較低。以上兩種系統均適合採用已提交讀取隔離 。
Oracle 的已提交讀取隔離可以確保全部查詢的事務集數據一致性。即查詢得到的數據是處於一致性狀態下的。所以在 Oracle 中已提交讀取隔離可以知足大多數應用的要求。而在沒有多版本併發訪問控制的數據庫管理系統中,開發者可能須要採用更高程度的隔離方式。
在已提交讀取隔離模式下,開發者不須要在應用邏輯中捕獲 沒法進行串行化訪問 錯誤,也無需回滾並從新執行事務。在大多數應用程序中,幾乎不會有在一個事務中執行同一查詢屢次的狀況,所以在這些應用程序中,爲防止出現不可重複讀取或不存在讀取而採起的保護措施並不重要。 若是開發者選擇已提交讀取隔離,就可以省略在每一個事務中加入錯誤檢查及事務重作的代碼。
Oracle 的串行化隔離適合於具有如下特色的系統:出現修改相同數據的事務的概率較小,且長時間執行的事務以只讀操做爲主。最適合採用串行化隔離的系統是大型數據庫,且其中主要運行更新少許數據的短小事務。
串行化隔離可以提供更好的數據一致性,她能阻止不可重複讀取或不存在讀取的現象。當一個讀或寫事務中須要運行同一查詢屢次時,串行化隔離的做用更加明顯。
某些數據庫管理系統在實現串行化隔離時,不管讀寫操做都要對整個數據塊加鎖。而 Oracle 則採用了無阻塞查詢及低粒度的行級鎖技術,減小了讀寫操做間的競爭。對於存在較多讀寫競爭的應用,Oracle 的串行化隔離與其餘數據庫管理系統相比可以大大地提升事務處理能力。所以,某些應用在 Oracle 中能夠採用串行化隔離,而在其餘數據庫管理系統則未必可行。
運行在串行化隔離模式下的事務中的全部查詢所得到的數據都來自同一時間點,因此這種隔離級別適合於須要執行多個知足一致性的查詢的事務。例如,彙總數據並將結果寫入數據庫的報表應用能夠採用串行化隔離,由於串行化事務所提供的數據一致性與 READ ONLY 事務相同,但其中還能夠執行 INSERT , UPDATE ,和 DELETE 操做。