程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 數據庫知識 >> DB2數據庫 >> DB2教程 >> DB2 9 基礎(730 考試)認證指南,第 6 部分: 數據並發性

DB2 9 基礎(730 考試)認證指南,第 6 部分: 數據並發性

編輯:DB2教程

事務

理解數據一致性

什麼是數據一致性?回答這個問題的最佳方法是通過研究示例。假定您的公司擁有多家連鎖飯店,公司用一個數據庫來跟蹤每家飯店中的貨物存儲量。為了使貨物采購過程更方便,數據庫包含每個連鎖店的庫存表。每當一家飯店收到或用掉一部分貨物時,與該飯店相應的庫存表就會被修改以反映庫存變化。

現在,假定從一家店調配若干瓶番茄醬到另一家店。為了准確地表示這一次庫存調配,調出方飯店表中存儲的番茄醬瓶數必須減少,而接收方飯店表中存儲的番茄醬瓶數必須增加。如果用戶減少了調出方飯店庫存表中的番茄醬瓶數,但沒有增加接收方庫存表中的番茄醬瓶數,則數據就會變得不一致。此時所有連鎖店的番茄醬的總瓶數不再准確了。

如果用戶忘記了進行所有必要的更改(正如在前面的示例中一樣),或者如果在用戶進行更改的過程中系統崩潰了,又或者如果數據庫應用程序由於某種原因過早地停止了,數據庫中的數據都會變得不一致。當幾個用戶同時訪問相同的數據庫表時,也可能發生不一致。為了防止數據的不一致(尤其是在多用戶環境中),DB2 的設計中集成了下列數據一致性支持機制:

事務

隔離級別

事務和事務邊界

事務(也稱為工作單元)是一種將一個或多個 SQL 操作組合成一個單元的可恢復操作序列,通常位於應用程序進程中。事務的啟動和終止定義了數據庫一致性點;要麼將一個事務中執行的所有 SQL 操作的結果都應用於數據庫(提交),要麼完全取消並丟棄已執行的所有 SQL 操作的結果(回滾)。

使用從 Command Center、Script Center 或 Command Line Processor 運行的嵌入式 SQL 應用程序和腳本,在可執行 SQL 語句第一次執行時(在建立與數據庫的連接之後或在現有事務終止之後),事務就會自動啟動。在啟動事務之後,必須由啟動事務的用戶或應用程序顯式地終止它,除非使用了稱為自動提交(automatic commit) 的過程(在這種情況下,發出的每個單獨的 SQL 語句被看作單個事務,它一執行就隱式地提交了)。

在大多數情況下,通過執行 COMMIT 或 ROLLBACK 語句來終止事務。當執行 COMMIT 語句時,自從事務啟動以來對數據庫所做的一切更改就成為永久性的了 —— 即,它們被寫到磁盤。當執行 ROLLBACK 語句時,自從事務啟動以來對數據庫所做的一切更改都被撤消,而數據庫返回到事務開始之前所處的狀態。不管是哪種情況,數據庫在事務完成時都保證能回到一致狀態。

一定要注意一點:雖然事務通過確保對數據的更改僅在事務被成功提交之後才成為永久性的,從而提供了一般的數據庫一致性,但還是需要用戶或應用程序來確保每個事務中執行的 SQL 操作序列始終會導致一致的數據庫。

COMMIT 和 ROLLBACK 操作的效果

正如在前面提到的,通常通過執行 COMMIT 或 ROLLBACK SQL 語句來終止事務。為了理解這些語句如何工作,研究一個示例是有幫助的。

如果按所示的順序執行下列 SQL 語句:

清單 1. 由三個事務組成的簡單工作負載

CONNECT TO MY_DB
CREATE TABLE DEPARTMENT (DEPT_ID INTEGER NOT NULL, DEPT_NAME VARCHAR(20))
INSERT INTO DEPARTMENT VALUES(100, 'PAYROLL')
INSERT INTO DEPARTMENT VALUES(200, 'ACCOUNTING')
COMMIT  
    
INSERT INTO DEPARTMENT VALUES(300, 'SALES')
ROLLBACK 
    
INSERT INTO DEPARTMENT VALUES(500, 'MARKETING')
COMMIT

這將創建一個名為 DEPARTMENT 的表,它的結構如下所示:

DEPT_ID DEPT_NAME 100 PAYROLL 200 ACCOUNTING 500 MARKETING

這是因為當執行第一個 COMMIT 語句時,創建名為 DEPARTMENT 的表並向 DEPARTMENT 表中插入兩條記錄,這兩個操作都會變成永久性的。另一方面,當執行 ROLLBACK 語句時,刪除插入 DEPARTMENT 表中的第三條記錄,該表返回到執行插入操作之前所處的狀態。最後,當執行第二個 COMMIT 語句時,插入到 DEPARTMENT 中的第四條記錄成為永久性的,而數據庫再次返回到一致狀態。

正如可以從這個示例中看到的,提交或回滾操作只影響在這個操作所結束的事務內做出的更改。只要數據更改仍然未被提交,其他用戶和應用程序通常就無法看見它們(也有例外情況,稍後我們將進行討論),並可以通過執行回滾操作取消它們。但是,一旦數據更改被提交了,其他用戶和應用程序就可以訪問它們,並且再也不能通過回滾操作取消它們了。

不成功事務的效果

我們剛才已看到當通過 COMMIT 或 ROLLBACK 語句終止事務時會發生什麼。但是,如果在事務完成前出現系統故障,那會發生什麼情況呢?在這種情況下,DB2 數據庫管理程序會取消所有未提交的更改,從而恢復數據庫一致性(假定在事務啟動時存在這樣的一致性)。圖 1 對比了成功的事務和在成功終止之前失敗的事務的效果。

圖 1. 對比成功的和不成功的事務

DB2 9 基礎(730 考試)認證指南,第 6 部分: 數據並發性

並發性和隔離級別

當多個用戶訪問同一數據庫時會發生的現象

在單用戶環境中,每個事務都是順序執行的,而不會遇到與其他事務的沖突。但是,在多用戶環境下,多個事務可以(而且常常)同時執行。因此每個事務都有可能與其他正在運行的事務發生沖突。有可能與其他事務發生沖突的事務稱為交錯的 或並行的 事務,而相互隔離的事務稱為串行化 事務,這意味著同時運行它們的結果與一個接一個連續地運行它們的結果沒有區別。在多用戶環境下,在使用並行事務時,會發生四種現象:

丟失更新:這種情況發生在兩個事務讀取並嘗試更新同一數據時,其中一個更新會丟失。例如:事務 1 和事務 2 讀取同一行數據,並都根據所讀取的數據計算出該行的新值。如果事務 1 用它的新值更新該行以後,事務 2 又更新了同一行,則事務 1 所執行的更新操作就丟失了。由於設計 DB2 的方法,DB2 不允許發生此類現象。

髒讀:當事務讀取尚未提交的數據時,就會發生這種情況。例如:事務 1 更改了一行數據,而事務 2 在事務 1 提交更改之前讀取了已更改的行。如果事務 1 回滾該更改,則事務 2 就會讀取被認為是不曾存在的數據。

不可重復的讀:當一個事務兩次讀取同一行數據,但每次獲得不同的數據值時,就會發生這種情況。例如:事務 1 讀取了一行數據,而事務 2 在更改或刪除該行後提交了更改。當事務 1 嘗試再次讀取該行時,它會檢索到不同的數據值(如果該行已經被更新的話),或發現該行不復存在了(如果該行被刪除的話)。

幻像:當最初沒有看到某個與搜索條件匹配的數據行,而在稍後的讀操作中又看到該行時,就會發生這種情況。例如:事務 1 讀取滿足某個搜索條件的一組數據行,而事務 2 插入了與事務 1 的搜索條件匹配的新行。如果事務 1 再次執行產生原先行集的查詢,就會檢索到不同的行集。

維護數據庫的一致性和數據完整性,同時又允許多個應用程序同時訪問同一數據,這樣的特性稱為並發性。DB2 數據庫用來嘗試強制實施並發性的方法之一是通過使用隔離級別,它決定在第一個事務訪問數據時,如何對其他事務鎖定或隔離該事務所使用的數據。DB2 使用下列隔離級別來強制實施並發性:

可重復的讀(Repeatable Read)

讀穩定性(Read Stability)

游標穩定性(Cursor Stability)

未提交的讀(Uncommitted Read)

可重復的讀隔離級別可以防止所有現象,但是會大大降低並發性的程度(可以同時訪問同一資源的事務數量)。未提交的讀隔離級別提供了最大的並發性,但是後三種現象都可能出現。

可重復讀隔離級別

可重復讀隔離級別是最嚴格的隔離級別。在使用它時,一個事務的影響完全與其他並發事務隔離:髒讀、不可重復的讀、幻像都不會發生。當使用可重復的讀隔離級別時,在事務執行期間鎖定該事務以任何方式 引用的所有行。因此,如果在同一個事務中發出同一個 SELECT 語句兩次或更多次,那麼產生的結果數據集總是相同的。因此,使用可重復的讀隔離級別的事務可以多次檢索同一行集,並可以對它們執行任意操作,直到由提交或回滾操作終止事務。但是,在事務存在期間,不允許其他事務執行會影響這個事務正在訪問的任何行的插入、更新或刪除操作。為了確保這種行為,鎖定該事務所引用的每一行 —— 而不是僅鎖定被實際檢索或修改的那些行。因此,如果一個事務掃描了 1000 行,但只檢索 10 行,則所掃描的 1000 行(而不僅是被檢索的 10 行)都會被鎖定。

那麼在現實環境中可重復讀隔離級別是如何工作的呢?假定您使用 DB2 數據庫跟蹤旅館記錄,包括房間預訂和房價信息,還有一個基於 Web 的應用程序,它允許顧客按 “先到先服務” 的原則預訂房間。如果旅館預訂應用程序是在可重復讀隔離級別下運行的,當顧客掃描某個日期段內的可用房間列表時,您(旅館經理)將無法更改那些房間在指定日期范圍內的房價。同樣,其他顧客也無法進行或取消將會更改該列表的預訂(直到第一個顧客的事務終止為止)。但是,對於生成第一個顧客的列表時沒有讀取的任何房間記錄,您可以修改房價。同樣,其他顧客也可以進行或取消這些房間的預訂。圖 2 說明了這種行為。

圖 2. 可重復讀隔離級別的示例

DB2 9 基礎(730 考試)認證指南,第 6 部分: 數據並發性

讀穩定性隔離級別

讀穩定性隔離級別沒有可重復讀隔離級別那麼嚴格;因此,它沒有將事務與其他並發事務的效果完全隔離。讀穩定性隔離級別可以防止髒讀和不可重復的讀,但是可能出現幻像。在使用這個隔離級別時,只鎖定事務實際檢索和修改的行。因此,如果一個事務掃描了 1000 行,但只檢索 10 行,則只有被檢索的 10 行(而不是所掃描的 1000 行)被鎖定。因此,如果在同一個事務中發出同一個 SELECT 語句兩次或更多次,那麼每次產生的結果數據集可能不同。

與可重復讀隔離級別一樣,在讀穩定性隔離級別下運行的事務可以檢索一個行集,並可以對它們執行任意操作,直到事務終止。在這個事務存在期間,其他事務不能執行那些會影響這個事務檢索到的行集的更新或刪除操作;但是其他事務可以執行插入操作。如果插入的行與第一個事務的查詢的選擇條件匹配,那麼這些行可能作為幻像出現在後續產生的結果數據集中。其他事務對其他行所做的更改,在提交之前是不可見的。

那麼,讀穩定性隔離級別會如何影響旅館預訂應用程序的工作方式呢?當一個顧客檢索某個日期段內的所有可用房間列表時,您可以更改這個顧客的列表之外的任何房間的房價。同樣,其他顧客可以進行或取消房間預訂,如果第一個顧客再次運行同樣的查詢,其他顧客的操作可能會影響他獲得的可用房間列表。如果第一個顧客再次查詢同一個日期段內的所有可用房間列表,產生的列表中有可能包含新的房價或第一次產生列表時不可用的房間。圖 3 說明了這種行為。

圖 3. 讀穩定性隔離級別的示例

DB2 9 基礎(730 考試)認證指南,第 6 部分: 數據並發性

游標穩定性隔離級別

游標穩定性隔離級別在隔離事務效果方面非常寬松。它可以防止髒讀;但有可能出現不可重復的讀和幻像。這是因為在大多數情況下,游標穩定性隔離級別只鎖定事務聲明並打開的游標當前引用的行。

當使用游標穩定性隔離級別的事務通過游標從表中檢索行時,其他事務不能更新或刪除游標所引用的行。但是,如果被鎖定的行本身不是用索引訪問的,那麼其他事務可以將新的行添加到表中,以及對被鎖定行前後的行進行更新和/或刪除操作。所獲取的鎖一直有效,直到游標重定位或事務終止為止。(如果游標重定位,原來行上的鎖就被釋放,並獲得游標現在引用的行上的鎖。)此外,如果事務修改了它檢索到的任何行,那麼在事務終止之前,其他事務不能更新或刪除該行,即使在游標不再位於被修改的行。與可重復讀和讀穩定性隔離級別一樣,其他事務在其他行上進行的更改,在這些更改提交之前對於使用游標穩定性隔離級別的事務(這是默認的隔離級別)是不可見的。

如果旅館預訂應用程序在游標穩定性隔離級別下運行,那麼有什麼影響呢?當一個顧客檢索某個日期段內所有可用房間的列表,然後查看關於產生的列表上每個房間的信息時(每次查看一個房間),您可以更改旅館中任何房間的房價,但是這個顧客當前正在查看的房間除外 (對於指定的日期段)。同樣,其他顧客可以對任何房間進行或取消預訂,但是這個顧客當前正在查看的房間除外 (對於指定的日期段)。但是,對於第一個顧客當前正在查看的房間記錄,您和其他顧客都不能進行任何操作。當第一個顧客查看列表中另一個房間的信息時,您和其他顧客就可以修改他剛才查看的房間記錄(如果這個顧客沒有預訂這個房間的話);但不能修改第一個顧客當前正在查看的房間記錄。圖 4 說明了這種行為。

圖 4. 游標穩定性隔離級別的示例

DB2 9 基礎(730 考試)認證指南,第 6 部分: 數據並發性

未提交的讀隔離級別

未提交的讀隔離級別是最不嚴格的隔離級別。實際上,在使用這個隔離級別時,僅當另一個事務試圖刪除或更改被檢索的行所在的表時,才會鎖定一個事務檢索的行。因為在使用這種隔離級別時,行通常保持未鎖定狀態,所以髒讀、不可重復的讀和幻像都可能會發生。因此,未提交的讀隔離級別通常用於那些訪問只讀表和視圖的事務,以及某些執行 SELECT 語句的事務(只要其他事務的未提交數據對這些語句沒有負面效果)。

顧名思義,其他事務對行所做的更改在已經提交之前對於使用未提交的讀隔離級別的事務是可見的。但是,此類事務不能看見或訪問其他事務所創建的表、視圖或索引,直到那些事務被提交為止。類似地,如果其他事務刪除了現有的表、視圖或索引,那麼僅當進行刪除操作的事務終止時,使用未提交的讀隔離級別的事務才能知道這些對象不再存在了。(一定要注意一點:當運行在未提交的讀隔離級別下的事務使用可更新游標時,該事務的行為和在游標穩定性隔離級別下運行一樣,並應用游標穩定性隔離級別的約束。)

那麼未提交的讀隔離級別對旅館預訂應用程序有什麼影響呢?現在,當一個顧客檢索某個日期段內的所有可用房間列表時,您可以更改旅館中任何房間任何日期的房價,而其他顧客也可以對任何房間進行或取消預訂,包括第一個顧客當前正在查看的房間記錄(對於指定的日期段)。另外,第一個顧客生成的房間列表可能包含其他顧客正在預訂(因此實際上不可用)的房間。圖 5 說明了這種行為。

圖 5. 未提交的讀隔離級別示例

DB2 9 基礎(730 考試)認證指南,第 6 部分: 數據並發性

選擇正確的隔離級別

使用的隔離級別不僅影響數據庫對並發性的支持如何,而且影響並發應用程序的性能。通常,使用的隔離級別越嚴格,並發性就越小,某些應用程序的性能可能會越低,因為它們要等待資源上的鎖被釋放。那麼,如何決定要使用哪種隔離級別呢?最好的方法是確定哪些現象是不可接受的,然後選擇能夠防止這些現象發生的隔離級別:

如果正在執行大型查詢,而且不希望並發事務所做的修改導致查詢的多次運行返回不同的結果,則使用可重復的讀隔離級別。

如果希望在應用程序之間獲得一定的並發性,還希望限定的行在事務執行期間保持穩定,則使用讀穩定性隔離級別。

如果希望獲得最大的並發性,同時不希望查詢看到未提交的數據,則使用游標穩定性隔離級別。

如果正在只讀的表/視圖/數據庫上執行查詢,或者並不介意查詢是否返回未提交的數據,則使用未提交的讀隔離級別。

指定要使用的隔離級別

盡管隔離級別控制事務級上的行為,但實際上是在應用程序級指定它們的:

對於嵌入式 SQL 應用程序,在預編譯時或在將應用程序綁定到數據庫(如果使用延遲綁定)時指定隔離級別。在這種情況下,使用 PRECOMPILE 或 BIND 命令 的 ISOLATION 選項來設置隔離級別。

對於開放數據庫連接(Open Database Connectivity,ODBC)和調用級接口(Call Level Interface,CLI)應用程序,隔離級別是在應用程序運行時通過調用指定了 SQL_ATTR_TXN_ISOLATION 連接屬性的 SQLSetConnectAttr() 函數進行設置的。(另外,也可以通過指定 db2cli.ini 配置文件中的 TXNISOLATION 關鍵字的值來設置 ODBC/CLI 應用程序的隔離級別;但是,這種方法不夠靈活,不能像第一種方法那樣為一個應用程序中的不同事務修改隔離級別。)

對於 Java 數據庫連接(Java Database Connectivity,JDBC)和 SQLJ 應用程序,隔離級別是在應用程序運行時通過調用 DB2 的 Java.sql 連接接口中的 setTransactionIsolation() 方法設置的。

當沒有使用這些方法顯式指定應用程序的隔離級別時,默認使用游標穩定性隔離級別。這個默認設置應用於從命令行處理程序(CLP)執行的 DB2 命令、SQL 語句和腳本以及嵌入式 SQL、ODBC/CLI、JDBC 和 SQLJ 應用程序。因此,也可以為從 CLP 執行的操作(以及傳遞給 DB2 CLP 進行處理的腳本)指定隔離級別。在這種情況下,隔離級別是通過在建立數據庫連接之前在 CLP 中執行 CHANGE ISOLATION 命令設置的。

在 DB2 UDB 8.1 及更高版本中,能夠指定特定查詢所用的隔離級別,方法是在 SELECT SQL 語句中加上 WITH [RR | RS | CS | UR] 子句。使用這個子句的簡單 SELECT 語句示例如下所示:

SELECT * FROM EMPLOYEE WHERE EMPID = '001' WITH RR

如果應用程序在大多數時候需要比較寬松的隔離級別(以支持最大的並發性),但是對於其中的某些查詢必須防止某些現象出現,那麼這個子句就是幫助您實現目標的好方法。

鎖的工作原理

在 並發性和隔離級別 一節中,我們看到 DB2 通過使用鎖 把事務彼此隔離開來。鎖是一種用來將數據資源與單個事務關聯起來的機制,其用途是當某個資源與擁有它的事務關聯在一起時,控制其他事務如何與該資源進行交互。(我們稱與被鎖定的資源關聯的事務持有 或擁有 該鎖。)DB2 數據庫管理程序用鎖來禁止事務訪問其他事務寫入的未提交數據(除非使用了未提交的讀隔離級別),並禁止其他事務在擁有鎖的事務使用限制性隔離級別時對這些行進行更新。一旦獲取了鎖,在事務終止之前,就一直持有該鎖;該事務終止時釋放鎖,其他事務就可以使用被解鎖的數據資源了。

如果一個事務嘗試訪問數據資源的方式與另一個事務所持有的鎖不兼容(稍後我們將研究鎖兼容性),則該事務必須等待,直到擁有鎖的事務終止為止。這被稱為鎖等待 事件。當鎖等待事件發生時,嘗試訪問數據資源的事務所做的只是停止執行,直到擁有鎖的事務終止和不兼容的鎖被釋放為止。

鎖的屬性

所有的鎖都有下列基本屬性:

object:object 屬性標識要鎖定的數據資源。DB2 數據庫管理程序在需要時鎖定數據資源(如表空間、表和行)。

size:size 屬性指定要鎖定的數據資源部分的物理大小。鎖並不總是必須控制整個數據資源。例如,DB2 數據庫管理程序可以讓應用程序獨占地控制表中的特定行,而不是讓該應用程序獨占地控制整個表。

duration:duration 屬性指定持有鎖的時間長度。事務的隔離級別通常控制著鎖的持續時間。

mode:mode 屬性指定允許鎖的擁有者執行的訪問類型,以及允許並發用戶對被鎖定數據資源執行的訪問類型。這個屬性通常稱為鎖狀態。

鎖狀態

鎖狀態確定允許鎖的擁有者執行的訪問類型,以及允許並發用戶對被鎖定數據資源執行的訪問類型。表 1 說明了可用的鎖狀態,按照控制遞增的次序排列。

表 1. 鎖狀態

鎖狀態(模式) 適用對象 描述 意向無(Intent None,IN) 表空間和表 鎖的擁有者可以讀取被鎖定表中的數據(包括未提交數據),但不能更改這些數據。在這種模式中,鎖的擁有者不獲取行級鎖;因此,其他並發應用程序可以讀取和更改表中的數據。 意向共享(Intent Share,IS) 表空間和表 鎖的擁有者可以讀取被鎖定表中的數據,但不能更改這些數據。同樣,因為鎖的擁有者不獲取行級鎖;所以,其他並發的應用程序可以讀取和更改表中的數據。(當事務擁有表上的意向共享鎖時,就在它所讀取的每個行上進行共享鎖定。)當事務沒有表達更新表中行的意圖時,就獲取這種鎖。(SELECT FOR UPDATE、UPDATE ... WHERE 和 INSERT 語句表達更新的意圖。) 下一鍵共享(Next Key Share,NS) 行 鎖擁有者和所有並發的事務都可以讀(但不能更改)被鎖定行中的數據。這種鎖用來在使用讀穩定性或游標穩定性事務隔離級別讀取的數據上代替共享鎖。 共享(Share,S) 表和行 鎖擁有者和任何其他並發的事務都可以讀(但不能更改)被鎖定的表或行中的數據。只要表不是使用共享鎖鎖定的,那麼該表中的單個行可以使用共享鎖鎖定。但是,如果表是用共享鎖定的,則鎖擁有者不能在該表中獲取行級的共享鎖。如果表或行是用共享鎖鎖定的,則其他並發事務可以讀取數據,但不能對它進行更改。 意向互斥(Intent Exclusive,IX) 表空間和表 鎖擁有者和任何其他並發的應用程序都可以讀取和更改被鎖定表中的數據。當鎖擁有者從表中讀取數據時,它在所讀取的每一行上獲取一個共享鎖,而在它更新的每一行上獲取更新鎖和互斥鎖。其他並發的應用程序可以讀取和更新被鎖定的表。當事務表達更新表中行的意圖時,就獲取這種鎖。 帶意向互斥的共享(Share With Intent Exclusive,SIX) 表 鎖擁有者可以讀取和更改被鎖定表中的數據。鎖擁有者在它更新的行上獲取互斥鎖,但不在它讀取的行上獲取鎖;因此,其他並發的應用程序可以讀取但不能更新被鎖定表中的數據。 更新(Update,U) 表和行 鎖的擁有者可以更新被鎖定表中的數據,而且鎖的擁有者在它所更新的任何行上自動獲得互斥鎖。其他並發的應用程序可以讀取但不能更新被鎖定表中的數據。 下一鍵弱互斥(Next Key Weak Exclusive,NW) 行 鎖的擁有者可以讀取但不能更新被鎖定的行。當在非編目表的索引中插入行時,在表中的下一行上獲得這種鎖。 互斥(Exclusive,X) 表和行 鎖的擁有者可以讀取和更改被鎖定的表或行中的數據。如果獲取了互斥鎖,則只允許使用未提交的讀隔離級別的應用程序訪問被鎖定的表或行。對於用 INSERT、UPDATE 和/或 DELETE 語句操作的數據資源,將獲取互斥鎖。 弱互斥(Weak Exclusive,WE) 行 鎖的擁有者可以讀取和更改被鎖定的行。當向非編目表中插入行時,該行上將獲得這種鎖。 超級互斥(Super Exclusive,Z) 表空間和表 鎖的擁有者可以更改表、刪除表、創建索引或刪除索引。當事務嘗試執行上述任何一種操作時,表上就自動獲得這種鎖。在釋放這個鎖之前,不允許其他並發事務讀取或更新該表。

如何獲取鎖

在大多數情況下,DB2 數據庫管理程序在需要鎖時隱式地獲取它們,因此這些鎖在 DB2 數據庫管理程序的控制之下。除了使用未提交讀隔離級別的情況外,事務從不需要顯式地請求鎖。實際上,惟一有可能被事務顯式地鎖定的數據庫對象是表。圖 6 說明了用何種邏輯確定為所引用的對象獲取什麼類型的鎖。

圖 6. 如何獲取鎖

DB2 9 基礎(730 考試)認證指南,第 6 部分: 數據並發性

DB2 數據庫管理程序總是嘗試獲取行級鎖。但是,可以通過執行特殊形式的 ALTER TABLE 語句來修改這種行為,如下所示:

ALTER TABLE [TableName] LOCKSIZE TABLE

其中的 TableName 標識一個現有表的名稱,所有事務在訪問它時都要獲取表級鎖。

也可以通過執行 LOCK TABLE 語句,強制 DB2 數據庫管理程序為特定事務在表上獲取表級鎖,如下所示:

LOCK TABLE [TableName] IN [SHARE | EXCLUSIVE] MODE

其中的 TableName 標識一個現有表的名稱,對於這個表應該獲取表級鎖(假定其他事務在該表上沒有不兼容的鎖)。如果在執行這個語句時指定了共享(SHARE)模式,就會獲得一個允許其他事務讀取(但不能更改)存儲在表中的數據的表級鎖;如果執行時指定了互斥(EXCLUSIVE)模式,就會獲得一個不允許其他事務讀取或修改存儲在表中的數據的表級鎖。

鎖和性能

鎖兼容性

如果數據資源上的一種鎖狀態允許在同一資源上放置另一個鎖,就認為這兩種鎖(或兩種狀態)是兼容的。每當一個事務持有數據資源上的鎖,而第二個事務請求同一資源上的鎖時,DB2 數據庫管理程序會檢查兩種鎖狀態以判斷它們是否兼容。如果鎖是兼容的,則將鎖授予第二個事務(假定沒有其他事務在等待該數據資源)。但是,如果鎖不兼容,則第二個事務必須等待,直到第一個事務釋放它的鎖為止,然後它才可以獲取對資源的訪問權並繼續處理。(如果資源上有多個與新請求的鎖不兼容的鎖,則第二個事務必須等到它們全部被釋放為止。)請參閱 IBM DB2 9 Administration Guide: Performance 文檔(或在 DB2 信息中心搜索 Lock type compatibility 主題)以獲取關於各個鎖之間是否兼容的特定信息。

鎖轉換

當事務嘗試訪問它已經持有鎖的數據資源,但是所需的訪問模式需要比已持有的鎖更嚴格的鎖時,則所持有的鎖的狀態更改成更嚴格的狀態。將已經持有的鎖的狀態更改成更嚴格狀態的操作稱為鎖轉換。發生鎖轉換是因為一個事務同一時間內只能在一個數據資源上持有一個鎖。

在大多數情況下,對行級鎖執行鎖轉換,轉換過程相當簡單。例如,如果持有共享(S)或更新(U)行級鎖,但是需要互斥(X)鎖,則所持有的鎖將被轉換成互斥(X)鎖。但意向互斥(IX)鎖和共享(S)鎖是特例,因為無法確定其中哪個更嚴格。因此,如果持有其中一種行級鎖但又需要另一種鎖,則所持有的鎖將轉換成帶意向互斥的共享(SIX)鎖。假定所請求的鎖狀態更嚴格的話,則類似的轉換會使所請求的鎖狀態成為持有鎖的新狀態。(僅當所持有的鎖可以增加其嚴格性時,才會發生鎖轉換。)在轉換了鎖狀態之後,鎖處於所獲取的最高狀態,直到持有該鎖的事務終止為止。

鎖升級

所有的鎖都需要存儲空間;因為可用空間並不是無限的,所以 DB2 數據庫管理程序必須限制鎖可以使用的空間(這是通過 maxlocks 數據庫配置參數完成的)。為了防止特定數據庫代理超過已建立的鎖空間限制,當獲取的(任意類型的)鎖過多時,會自動執行稱為鎖升級 的過程。鎖升級是一種轉換,它將同一表內幾個單獨的行級鎖轉換成一個單一的表級鎖。因為鎖升級是在內部處理的,所以惟一可從外部檢測到的結果可能只是對一個和多個表的並發訪問減少了。

以下是鎖升級的工作原理:當事務請求鎖而鎖存儲空間已滿時,就選擇與該事務相關聯的一個表,讓它獲取一個表級鎖,釋放該表的所有行級鎖(從而在鎖列表數據結構中讓出空間),並將表級鎖添加到鎖列表。如果這個過程所釋放的空間不夠,則選擇另一個表,重復這個過程,直到釋放了足夠的可用空間為止。這時,事務將獲取所請求的鎖並繼續執行。但是,如果在該事務的所有行級鎖都已經升級之後,仍然沒有獲得必要的可用鎖空間,則(通過 SQL 錯誤編碼)要求事務提交或回滾它啟動以來所做的所有更改,然後事務終止。

鎖超時

每當一個事務在特定數據資源(例如,表或行)上持有鎖時,直到持有鎖的事務終止並釋放它所獲取的所有鎖之前,其他事務對該資源的訪問都可能被拒絕。如果沒有某種鎖超時檢測機制,則事務可能無限期地等待鎖的釋放。例如,有可能出現這種情況:一個事務在等待另一個用戶的應用程序所持有的鎖被釋放,而該用戶離開了他或她的工作站,但忘了執行一些允許應用程序終止擁有鎖的事務的交互。顯然,此類情況會導致極差的應用程序性能。要避免發生此類情況時阻礙其他應用程序的執行,可以在數據庫的配置文件中指定鎖超時值(通過 locktimeout 數據庫配置參數)。該參數控制任何事務等待獲取所請求的鎖的時間。如果在指定的時間間隔過去之後還未獲得想要的鎖,則等待的應用程序接收一個錯誤,並回滾請求該鎖的事務。分布式事務應用程序環境尤其容易產生此類情況;可以通過使用鎖超時避免它們。

死鎖

盡管可以通過建立鎖超時來避免一個事務無限期地等待另一個事務釋放鎖的情況,但是鎖超時無法解決兩個或更多事務對鎖的爭用。這種情況稱為死鎖 或死鎖循環。說明死鎖的發生原因的最佳方式是舉例說明:假定事務 1 在表 A 上獲取了互斥(X)鎖,而事務 2 在表 B 上獲取了互斥(X)鎖。現在,假定事務 1 嘗試在表 B 上獲取互斥(X)鎖,而事務 2 嘗試在表 A 上獲取互斥(X)鎖。這兩個事務的處理都將被掛起,直到同意第二個鎖請求為止。但是,因為在任何一個事務釋放它目前持有的鎖(通過執行或回滾操作)之前,這兩個事務的鎖請求都不會被同意,而且因為這兩個事務都不能釋放它目前持有的鎖(因為它們都已掛起並等待鎖),所以它們都陷入了死鎖循環。圖 7 說明了這個死鎖場景。

圖 7. 死鎖循環

DB2 9 基礎(730 考試)認證指南,第 6 部分: 數據並發性

當死鎖循環發生時,除非某些外部代理進行干涉,否則所涉及的所有事務將無限期地等待釋放鎖。在 DB2 UDB 中,用於處理死鎖的代理是稱為死鎖檢測器 的異步系統後台進程。死鎖檢測器的惟一職責是定位和解決在鎖定子系統中找到的任何死鎖。每個數據庫有自己的死鎖檢測器,它在數據庫初始化過程中激活。激活之後,死鎖檢測器在大多數時間處於 “休眠” 狀態,但會以預置的時間間隔被 “喚醒”,以確定鎖定子系統中是否存在死鎖循環。如果死鎖檢測器在鎖定子系統中發現死鎖,則隨機選擇死鎖涉及的一個事務,終止並回滾它。選擇的事務收到一個 SQL 錯誤編碼,它所獲得的所有鎖都被釋放;這樣,剩下的事務就可以繼續執行了,因為死鎖循環已經被打破了。

鎖粒度

正如先前提到的,每當一個事務在特定數據資源上持有鎖時,在持有鎖的事務終止之前,其他事務對該資源的訪問都可能被拒絕。因此,為了進行優化以獲取最大的並發性,行級鎖通常比表級鎖更好,因為它們所限制訪問的資源要小得多。但是,因為所獲取的每個鎖都需要一定數量的處理時間和存儲空間,才能獲取鎖並進行管理,所以單個表級鎖需要的開銷比幾個單獨的行級鎖少。除非另外指定,否則默認情況下獲取行級鎖。

可以通過使用 ALTER TABLE ... LOCKSIZE TABLE、ALTER TABLE ... LOCKSIZE ROW 和 LOCK TABLE 語句控制鎖的粒度(即,獲取行級鎖還是表級鎖)。ALTER TABLE ... LOCKSIZE TABLE 語句提供了確定粒度的全局方法,它使得所有訪問特定表中行的事務都獲取表級鎖。另一方面,LOCK TABLE 語句允許在單個事務級別獲取表級鎖。在使用這兩種語句時,事務在需要鎖時就獲取單個共享(S)或互斥(X)表級鎖。結果,因為只需獲取和釋放一個表級鎖,而不是多個不同的行級鎖,所以鎖定性能通常會提高。但是,在使用表級鎖時,如果長時間運行的事務獲取互斥而不是共享表級鎖,那麼並發性會降低。

事務和鎖定

從鎖定的角度來看,所有事務通常歸為以下幾類之一:

只讀:這是指只讀性的事務,它們包含 SELECT 語句(它們本質上就是只讀的)、指定了 FOR READ ONLY 子句的 SELECT 語句或意義雖不明確但因為在預編譯和/或綁定過程中指定了 BLOCKING 選項而看作是只讀的 SQL 語句。

傾向於更改:這是指有可能進行更改的事務,它們包含指定了 FOR UPDATE 子句的 SELECT 語句或者那些意義雖不明確但因為 SQL 預編譯器解釋它的方式而看作是傾向於進行更改的 SQL 語句。

更改:這是指一定會進行更改的事務,它們包含 INSERT、UPDATE 和/或 DELETE 語句,但不包括 UPDATE ... WHERE CURRENT OF ... 或 DELETE ... WHERE CURRENT OF ... 語句。

游標控制:這是指包含 UPDATE ... WHERE CURRENT OF ... 和 DELETE ... WHERE CURRENT OF ... 語句的事務。

只讀事務通常使用意向共享(IS)和/或共享(S)鎖。另一方面,傾向於更改的事務將更新(U)、意向互斥(IX)和互斥(X)鎖用於表,將共享(S)、更新(U)和互斥(X)鎖用於行。更改事務往往使用意向互斥(IX)和/或互斥(X)鎖,而游標控制的事務通常使用意向互斥(IX)和/或互斥(X)鎖。

當 SQL 語句准備執行時,DB2 優化器研究各種滿足該語句請求的方法,並估計每種方法所涉及的執行成本。然後,DB2 優化器根據這一評估選擇它認為最優的訪問計劃(訪問計劃指定滿足 SQL 請求所需的操作,以及執行這些操作的順序)。訪問計劃可以使用兩種方法之一來訪問表中的數據:通過直接地讀取表(稱為執行表 或關系掃描);或通過讀取該表上的索引,然後檢索特定索引項所引用的表行(稱為執行索引掃描)。

DB2 優化器選擇的訪問路徑(通常是根據數據庫的設計確定的)會對所獲取鎖的數目和所使用的鎖狀態產生顯著的影響。例如,當使用索引掃描來查找特定行時,DB2 數據庫管理程序極有可能獲取一個或多個意向共享(IS)行級鎖。但是,如果使用表掃描,因為必須依次掃描整個表來找到特定行,所以 DB2 數據庫管理程序可能會選擇獲取單個共享(S)表級鎖。

結束語

本教程旨在介紹數據一致性的概念,以及 DB2 9 在單用戶和多用戶環境下用來維護數據庫一致性的各種機制。如果用戶忘記了進行所有必要的更改,或者如果在用戶進行更改的過程中系統崩潰了,又或者數據庫應用程序由於某種原因過早地停止了,數據庫都會變得不一致。當幾個用戶同時訪問同一數據庫時,也可能發生不一致的情況。例如,一個用戶可能在適當地更新所有表之前讀取另一個用戶的更改,並根據所讀取的不是最終的數據值進行了一些不適當的操作或進行了錯誤的更改。為了防止數據不一致(尤其是在多用戶環境中),DB2 9 的開發人員將下列數據一致性支持機制合並到其設計中:

事務

隔離級別

事務(也稱為工作單元)是一種將一個或多個 SQL 操作組織成一個單元的可恢復序列,通常位於應用程序進程中。事務的啟動和終止定義了數據庫一致性點;要麼將事務中執行的所有 SQL 操作的結果都應用於數據庫(提交),要麼完全取消並丟棄已執行的所有 SQL 操作的結果(回滾)。在這兩種情況下,數據庫都保證在每個事務結束後處於一致的狀態。

維護數據庫一致性和數據完整性,同時又允許多個應用程序同時訪問同一數據,這種特性稱為並發性。在 DB2 中,並發性是通過使用隔離級別實現的。可以使用四種不同的隔離級別:

可重復的讀

讀穩定性

游標穩定性

未提交的讀

可重復的讀隔離級別可以防止所有現象,但是會大大降低並發性的程度(可以同時訪問同一資源的事務數量)。未提交的讀隔離級別提供了最大的並發性,但是髒讀、不可重復的讀和幻像都可能出現。

除了隔離級別,DB2 通過對鎖的使用在多用戶環境下提供並發性。鎖是一種用來將數據資源與單個事務關聯起來的機制,其用途是控制其他事務在資源與擁有鎖的事務相關聯的情況下如何與資源進行交互。可以使用幾種不同類型的鎖:

意向無(IN)

意向共享(IS)

下一鍵共享(NS)

共享(S)

意向互斥(IX)

帶意向互斥的共享(SIX)

更新(U)

下一鍵弱互斥(NW)

互斥(X)

弱互斥(W)

超級互斥(Z)

為了維護數據完整性,DB2 數據庫管理程序隱式地獲取鎖,獲取的所有鎖都在 DB2 數據庫管理程序的控制之下。鎖可以放置在表空間、表和行上。

為了進行優化以獲取最大的並發性,行級鎖通常比表級鎖更好,因為它們所限制訪問的資源要小得多。但是,因為所獲取的每個鎖都需要一定數量的存儲空間和處理時間來進行管理,所以單個表級鎖需要的開銷比幾個單獨的行級鎖低。

本系列其他教程推薦

DB2 9 基礎(730 考試)認證指南,第 1 部分: DB2 規劃 1

DB2 9 基礎(730 考試)認證指南,第 1 部分: DB2 規劃 2

DB2 9 基礎(730 考試)認證指南,第 2 部分: 安全性

DB2 9 基礎(730 考試)認證指南,第 3 部分: 訪問 DB2 數據

DB2 9 基礎(730 考試)認證指南,第 4 部分: 處理 DB2 數據

DB2 9 基礎(730 考試)認證指南,第 5 部分: 處理 DB2 對象

DB2 9 基礎(730 考試)認證指南,第 7 部分: XQuery 簡介

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved