數據庫事務(wù)的概念 事務(wù)是由相關(guān)操作構成的一個(gè)完整的操作單元。兩次連續成功的COMMIT或ROLLBACK之間的操作,稱(chēng)為一個(gè)事務(wù)。在一個(gè)事務(wù)內,數據的修改一起提交或撤銷(xiāo),如果發(fā)生故障或系統錯誤,整個(gè)事務(wù)也會(huì )自動(dòng)撤銷(xiāo)。
比如,我們去銀行轉賬,操作可以分為下面兩個(gè)環(huán)節:
(1) 從第一個(gè)賬戶(hù)劃出款項。
(2) 將款項存入第二個(gè)賬戶(hù)。
整個(gè)交易過(guò)程,可以看作是一個(gè)事物,成功則全部成功,失敗則需要全部撤消,這樣可以避免當操作的中間環(huán)節出現問(wèn)題時(shí),產(chǎn)生數據不一致的問(wèn)題。
數據庫事務(wù)是一個(gè)邏輯上的劃分,有的時(shí)候并不是很明顯,它可以是一個(gè)操作步驟,也可以是多個(gè)操作步驟。
我們可以這樣理解數據庫事物:對數據庫所做的一系列修改,在修改過(guò)程中,暫時(shí)不寫(xiě)入數據庫,而是緩存起來(lái),用戶(hù)在自己的終端可以預覽變化,直到全部修改完成,并經(jīng)過(guò)檢查確認無(wú)誤后,一次性提交并寫(xiě)入數據庫,在提交之前,必要的話(huà)所做的修改都可以取消。提交之后,就不能撤銷(xiāo),提交成功后其他用戶(hù)才可以通過(guò)查詢(xún)?yōu)g覽數據的變化。
以事務(wù)的方式對數據庫進(jìn)行訪(fǎng)問(wèn),有如下的優(yōu)點(diǎn):
* 把邏輯相關(guān)的操作分成了一個(gè)組。
* 在數據永久改變前,可以預覽數據變化。
* 能夠保證數據的讀一致性。
數據庫事務(wù)的應用 數據庫事務(wù)處理可分為隱式和顯式兩種。顯式事務(wù)操作通過(guò)命令實(shí)現,隱式事務(wù)由系統自動(dòng)完成提交或撤銷(xiāo)(回退)工作,無(wú)需用戶(hù)的干預。
隱式提交的情況包括:當用戶(hù)正常退出SQL*Plus或執行CREATE、DROP、GRANT、REVOKE等命令時(shí)會(huì )發(fā)生事務(wù)的自動(dòng)提交。
還有一種情況,如果把系統的環(huán)境變量AUTOCOMMIT設置為ON(默認狀態(tài)為OFF),則每當執行一條INSERT、DELETE或UPDATE命令對數據進(jìn)行修改后,就會(huì )馬上自動(dòng)提交。設置命令格式如下:
SET AUTOCOMMIT ON/OFF
隱式回退的情況包括:當異常結束SQL*Plus或系統故障發(fā)生時(shí),會(huì )發(fā)生事務(wù)的自動(dòng)回退。
顯式事務(wù)處理的數據庫事務(wù)操作語(yǔ)句有3條,
COMMIT:數據庫事務(wù)提交,將變化寫(xiě)入數據庫
ROLLBACK:數據庫事務(wù)回退,撤銷(xiāo)對數據的修改
SAVEPOINT:創(chuàng )建保存點(diǎn),用于事務(wù)的階段回退
COMMIT操作把多個(gè)步驟對數據庫的修改,一次性地永久寫(xiě)入數據庫,代表數據庫事務(wù)的成功執行。ROLLBACK操作在發(fā)生問(wèn)題時(shí),把對數據庫已經(jīng)作出的修改撤消,回退到修改前的狀態(tài)。在操作過(guò)程中,一旦發(fā)生問(wèn)題,如果還沒(méi)有提交操作,則隨時(shí)可以使用ROLLBACK來(lái)撤消前面的操作。SAVEPOINT則用于在事務(wù)中間建立一些保存點(diǎn),ROLLBACK可以使操作回退到這些點(diǎn)上邊,而不必撤銷(xiāo)全部的操作。一旦COMMIT完成,就不能用ROLLBACK來(lái)取消已經(jīng)提交的操作。一旦ROLLBACK完成,被撤消的操作要重做,必須重新執行相關(guān)操作語(yǔ)句。
如何開(kāi)始一個(gè)新的事務(wù)呢?一般情況下,開(kāi)始一個(gè)會(huì )話(huà)(即連接數據庫),執行第一條SQL語(yǔ)句將開(kāi)始一個(gè)新的事務(wù),或執行COMMIT提交或ROLLBACK撤銷(xiāo)事務(wù),也標志新的事務(wù)的開(kāi)始。另外,執行DDL(如CREATE)或DCL命令也將自動(dòng)提交前一個(gè)事務(wù)而開(kāi)始一個(gè)新的事務(wù)。
數據在修改的時(shí)候會(huì )對記錄進(jìn)行鎖定,其他會(huì )話(huà)不能對鎖定的記錄進(jìn)行修改或加鎖,只有當前會(huì )話(huà)提交或撤銷(xiāo)后,記錄的鎖定才會(huì )釋放。
觀(guān)察數據的讀一致性 步驟1:顯示剛插入的雇員小馬:
- SELECT empno,ename FROM emp WHERE empno=3000;
執行結果:
- EMPNO ENAME
- ------------------ -----------
- 3000 小馬
步驟2:刪除雇員小馬:
- DELETE FROM emp WHERE empno=3000;
執行結果:
已刪除 1 行。
步驟3:再次顯示該雇員,顯示結果為該雇員不存在:
- SELECT empno,ename FROM emp WHERE empno=3000;
執行結果:
未選定行
步驟4:另外啟動(dòng)第2個(gè)SQL*Plus,并以SCOTT身份連接。執行以下命令,結果為該記錄依舊存在。
- SELECT empno,ename FROM emp WHERE empno=3000;
執行結果:
- EMPNO ENAME
- ------------------ -----------
- 3000 小馬
步驟5:在第1個(gè)SQL*Plus中提交刪除:
執行結果:
提交完成。
步驟6:在第2個(gè)SQL*Plus中再次顯示該雇員,顯示結果與步驟3的結果一致:
- SELECT empno,ename FROM emp WHERE empno=3000;
執行結果:
未選定行
說(shuō)明:在以上訓練中,當第1個(gè)SQL*Plus會(huì )話(huà)刪除小馬后,第2個(gè)SQL*Plus會(huì )話(huà)仍然可以看到該雇員,直到第1個(gè)SQL*Plus會(huì )話(huà)提交該刪除操作后,兩個(gè)會(huì )話(huà)看到的才是一致的數據。
鎖的概念 鎖出現在數據共享的場(chǎng)合,用來(lái)保證數據的一致性。當多個(gè)會(huì )話(huà)同時(shí)修改一個(gè)表時(shí),需要對數據進(jìn)行相應的鎖定。
鎖有“只讀鎖”、“排它鎖”,“共享排它鎖”等多種類(lèi)型,而且每種類(lèi)型又有“行級鎖”(一次鎖住一條記錄),“頁(yè)級鎖”(一次鎖住一頁(yè),即數據庫中存儲記錄的最小可分配單元),“表級鎖”(鎖住整個(gè)表)。
排它鎖和共享鎖排它鎖又稱(chēng)為寫(xiě)鎖((Exclusive lock,簡(jiǎn)記為X鎖)),若事務(wù)T對數據對象A加上X鎖,則只允許T讀取和修改A,其它任何事務(wù)都不能再對A加任何類(lèi)型的鎖,直到T釋放A上的鎖。它防止任何其它事務(wù)獲取資源上的鎖,直到在事務(wù)的末尾將資源上的原始鎖釋放為止。在更新操作(INSERT、UPDATE 或 DELETE)過(guò)程中始終應用排它鎖。
共享鎖又稱(chēng)為讀鎖(Share lock,簡(jiǎn)記為S鎖),若事務(wù)T對數據對象A加上S鎖,則其它事務(wù)只能再對A加S鎖,而不能加X(jué)鎖,直到T釋放A上的S鎖。
共享鎖:由非更新(讀?。┎僮鲃?chuàng )建的鎖。其他用戶(hù)可以并發(fā)讀取數據,但任何事務(wù)都不能獲取數據上的排它鎖,直到已釋放所有共享鎖。
若為“行級排它鎖”,則除被鎖住的行外,該表中其他行均可被其他的用戶(hù)進(jìn)行修改(Update)或刪除(delete)。若為“表級排它鎖”,則所有其他用戶(hù)只能對該表進(jìn)行查詢(xún)(select)操作,而無(wú)法對其中的任何記錄進(jìn)行修改或刪除。當程序對所做的修改進(jìn)行提交(commit)或回滾(rollback)后,鎖住的資源便會(huì )得到釋放,從而允許其他用戶(hù)進(jìn)行操作。
有時(shí),由于程序的原因,鎖住資源后長(cháng)時(shí)間未對其工作進(jìn)行提交;或是由于用戶(hù)的原因,調出需要修改的數據后,未及時(shí)修改并提交,而是放置于一旁;或是由于客戶(hù)服務(wù)器方式中客戶(hù)端出現“死機”,而服務(wù)器端卻并未檢測到,從而造成鎖定的資源未被及時(shí)釋放,影響到其他用戶(hù)的操作。
如果兩個(gè)事務(wù),分別鎖定一部分數據,而都在等待對方釋放鎖才能完成事務(wù)操作,這種情況下就會(huì )發(fā)生死鎖。
隱式鎖和顯式鎖 在Oracle數據庫中,修改數據操作時(shí)需要一個(gè)隱式的獨占鎖,以鎖定修改的行,直到修改被提交或撤銷(xiāo)為止。如果一個(gè)會(huì )話(huà)鎖定了數據,那么第二個(gè)會(huì )話(huà)要想對數據進(jìn)行修改,只能等到第一個(gè)會(huì )話(huà)對修改使用COMMIT命令進(jìn)行提交或使用ROLLBACK命令進(jìn)行回滾撤銷(xiāo)后,才開(kāi)始執行。因此應養成一個(gè)良好的習慣:執行修改操作后,要盡早地提交或撤銷(xiāo),以免影響其他會(huì )話(huà)對數據的修改。
對emp表的SCOTT雇員記錄進(jìn)行修改,
測試隱式鎖。
步驟1:?jiǎn)?dòng)第一個(gè)SQL*Plus,以SCOTT賬戶(hù)登錄數據庫(第一個(gè)會(huì )話(huà)),修改SCOTT記錄,隱式加鎖。
- UPDATE emp SET sal=3500 where empno=7788;
執行結果:
已更新 1 行。
步驟2:?jiǎn)?dòng)第二個(gè)SQL*Plus,以SCOTT賬戶(hù)登錄數據庫(第二個(gè)會(huì )話(huà)),進(jìn)行記錄修改操作。
- UPDATE emp SET sal=4000 where empno=7788;
執行結果,沒(méi)有任何輸出(處于等待解鎖狀態(tài))。
步驟3:對第一個(gè)會(huì )話(huà)進(jìn)行解鎖操作:
步驟4:查看第二個(gè)會(huì )話(huà),此時(shí)有輸出結果:
已更新 1 行。
步驟5:提交第二個(gè)會(huì )話(huà),防止長(cháng)時(shí)間鎖定。
說(shuō)明:兩個(gè)會(huì )話(huà)對同一表的同一條記錄進(jìn)行修改。步驟1修改SCOTT工資為3500,沒(méi)有提交或回滾之前,SCOTT記錄處于加鎖狀態(tài)。步驟2的第二個(gè)會(huì )話(huà)對SCOTT進(jìn)行修改處于等待狀態(tài)。
步驟3解鎖之后(即第一個(gè)會(huì )話(huà)對SCOTT的修改已經(jīng)完成),第二個(gè)會(huì )話(huà)掛起的修改此時(shí)可以執行。最后結果為第二個(gè)會(huì )話(huà)的修改結果,即SCOTT的工資修改為4000。讀者可以使用查詢(xún)語(yǔ)句檢查。
以上是隱式加鎖,用戶(hù)也可以使用如下兩種方式
主動(dòng)鎖定行或表,防止其他會(huì )話(huà)對數據的修改。
SELECT FOR UPDATE:鎖定表行,防止其他會(huì )話(huà)對行的修改
LOCK TABLE:鎖定表,防止其他會(huì )話(huà)對表的修改
對emp表的部門(mén)10的雇員記錄加顯式鎖,并測試。
步驟1:對部門(mén)10加顯式鎖:
- SELECT empno,ename,job,sal FROM emp WHERE deptno=10 FOR UPDATE;
結果為:
- EMPNO ENAME JOB SAL
- ------------------ -------------- ------------------ ------------------
- 7782 CLARK MANAGER 2450
- 7839 KING PRESIDENT 5000
- 7934 MILLER CLERK 1300
步驟2:?jiǎn)?dòng)第二個(gè)SQL*Plus(第二個(gè)會(huì )話(huà)),以SCOTT賬戶(hù)登錄數據庫,對部門(mén)10的雇員CLARK進(jìn)行修改操作。
- UPDATE emp SET sal=sal+100 where empno=7782;
執行結果:
沒(méi)有任何輸出(處于等待解鎖狀態(tài))。 步驟3:在第一個(gè)會(huì )話(huà)進(jìn)行解鎖操作:
步驟4:查看第二個(gè)會(huì )話(huà),有輸出結果:
已更新 1 行。 說(shuō)明:步驟1對選定的部門(mén)10的雇員加鎖,之后其他會(huì )話(huà)不能對部門(mén)10的雇員數據進(jìn)行修改或刪除。如果此時(shí)要進(jìn)行修改或刪除,則會(huì )處于等待狀態(tài)。使用COMMIT語(yǔ)句進(jìn)行解鎖之后,如果有掛起的修改或刪除操作,則等待的操作此時(shí)可以執行。
鎖定表 LOCK語(yǔ)句用于對整張表進(jìn)行鎖定。語(yǔ)法如下:
LOCK TABLE 表名 IN {SHARE|EXCLUSIVE} MODE
對表的鎖定可以是共享(SHARE)或獨占(EXCLUSIVE)模式。共享模式下,其他會(huì )話(huà)可以加共享鎖,但不能加獨占鎖。在獨占模式下,其他會(huì )話(huà)不能加共享或獨占鎖。
【訓練1】 對emp表添加獨占鎖。
步驟1:對emp表加獨占鎖:
- LOCK TABLE emp IN EXCLUSIVE MODE;
結果為:
表已鎖定。
步驟2:對表進(jìn)行解鎖操作:
說(shuō)明:當使用LOCK語(yǔ)句顯式鎖定一張表時(shí),死鎖的概率就會(huì )增加。同樣地,使用COMMIT或ROLLBACK命令可以釋放鎖。
注意:必須沒(méi)有其他會(huì )話(huà)對該表的任何記錄加鎖,此操作才能成功。
黑色頭發(fā):http://heisetoufa.iteye.com/