摘 要:本文討論了Oracle和SQL Server的在事務(wù)處理上的區(qū)別,并分析了其實(shí)現(xiàn)機(jī)制的差異。
關(guān)鍵詞:Oracle;SQL Server;事務(wù);事務(wù)隔離級(jí)別;DDL。
事務(wù)處理是所有大型數(shù)據(jù)庫(kù)產(chǎn)品的一個(gè)關(guān)鍵問(wèn)題,各數(shù)據(jù)庫(kù)廠商都在這個(gè)方面花費(fèi)了很大精力,不同的事務(wù)處理方式會(huì)導(dǎo)致數(shù)據(jù)庫(kù)性能和功能上的巨大差異。
事務(wù)處理也是數(shù)據(jù)庫(kù)管理員與數(shù)據(jù)庫(kù)應(yīng)用程序開(kāi)發(fā)人員必須深刻理解的一個(gè)問(wèn)題,對(duì)這個(gè)問(wèn)題的疏忽可能會(huì)導(dǎo)致應(yīng)用程序邏輯錯(cuò)誤以及效率低下。
下面我們針對(duì)Oracle及SQL Server這兩種當(dāng)前廣泛使用的大型數(shù)據(jù)庫(kù)產(chǎn)品,探討一下它們?cè)谑聞?wù)處理方面的一些差異。如沒(méi)有特殊說(shuō)明,本文內(nèi)容適用的數(shù)據(jù)庫(kù)產(chǎn)品版本為Oracle9i及SQL Server 2000,其中的示例SQL語(yǔ)句,對(duì)于Oracle是在SQL*Plus中執(zhí)行,而對(duì)于SQL Server 2000是在osql中執(zhí)行。
一.事務(wù)的概念
事務(wù)可以看作是由對(duì)數(shù)據(jù)庫(kù)的若干操作組成的一個(gè)單元,這些操作要么都完成,要么都取消,從而保證數(shù)據(jù)滿(mǎn)足一致性的要求。事務(wù)的一個(gè)典型例子是銀行中的轉(zhuǎn)帳操作,帳戶(hù)A把一定數(shù)量的款項(xiàng)轉(zhuǎn)到帳戶(hù)B上,這個(gè)操作包括兩個(gè)步驟,一個(gè)是從帳戶(hù)A上把存款減去一定數(shù)量,二是在帳戶(hù)B上把存款加上相同的數(shù)量。這兩個(gè)步驟顯然要么都完成,要么都取消,否則銀行就會(huì)受損失。顯然,這個(gè)轉(zhuǎn)帳操作中的兩個(gè)步驟就構(gòu)成一個(gè)事務(wù)。
數(shù)據(jù)庫(kù)中的事務(wù)還有如下ACID特征。
ACID分別是四個(gè)英文單詞的首寫(xiě)字母,這四個(gè)英文單詞是Atomicity、Consistency、Isolation、Durability,分別翻譯為原子性、一致性、隔離性、持久性。
原子性:指事務(wù)中的操作,或者都完成,或者都取消。
一致性:指事務(wù)中的操作保證數(shù)據(jù)庫(kù)中的數(shù)據(jù)不會(huì)出現(xiàn)邏輯上不一致的情況,一致性一般會(huì)隱含的包括在其他屬性之中。
隔離性:指當(dāng)前的事務(wù)與其他未完成的事務(wù)是隔離的。在不同的隔離級(jí)別下,事務(wù)的讀取操作,可以得到的結(jié)果是不同的。
持久性:指對(duì)事務(wù)發(fā)出COMMIT命令后,即使這時(shí)發(fā)生系統(tǒng)故障,事務(wù)的效果也被持久化了。與此相反的是,當(dāng)在事務(wù)執(zhí)行過(guò)程中,系統(tǒng)發(fā)生故障,則事務(wù)的操作都被回滾,即數(shù)據(jù)庫(kù)回到事務(wù)開(kāi)始之前的狀態(tài)。
對(duì)數(shù)據(jù)庫(kù)中的數(shù)據(jù)修改都是在內(nèi)存中完成的,這些修改的結(jié)果可能已經(jīng)寫(xiě)到硬盤(pán)也可能沒(méi)有寫(xiě)到硬盤(pán),如果在操作過(guò)程中,發(fā)生斷電或系統(tǒng)錯(cuò)誤等故障,數(shù)據(jù)庫(kù)可以保證未結(jié)束的事務(wù)對(duì)數(shù)據(jù)庫(kù)的數(shù)據(jù)修改結(jié)果即使已經(jīng)寫(xiě)入磁盤(pán),在下次數(shù)據(jù)庫(kù)啟動(dòng)后也會(huì)被全部撤銷(xiāo);而對(duì)于結(jié)束的事務(wù),即使其修改的結(jié)果還未寫(xiě)入磁盤(pán),在數(shù)據(jù)庫(kù)下次啟動(dòng)后會(huì)通過(guò)事務(wù)日志中的記錄進(jìn)行“重做”,即把丟失的數(shù)據(jù)修改結(jié)果重新生成,并寫(xiě)入磁盤(pán),從而保證結(jié)束事務(wù)對(duì)數(shù)據(jù)修改的永久化。這樣也保證了事務(wù)中的操作要么全部完成,要么全部撤銷(xiāo)。
二.事務(wù)設(shè)置及類(lèi)型的區(qū)別
在SQL Server中有三種事務(wù)類(lèi)型,分別是:隱式事務(wù)、顯式事務(wù)、自動(dòng)提交事務(wù),缺省為自動(dòng)提交。
自動(dòng)提交,是指對(duì)于用戶(hù)發(fā)出的每條SQL語(yǔ)句,SQL Server都會(huì)自動(dòng)開(kāi)始一個(gè)事務(wù),并且在執(zhí)行后自動(dòng)進(jìn)行提交操作來(lái)完成這個(gè)事務(wù),也可以說(shuō)在這種事務(wù)模式下,一個(gè)SQL語(yǔ)句就是一個(gè)事務(wù)。
顯式事務(wù),是指在自動(dòng)提交模式下以Begin Transaction開(kāi)始一個(gè)事務(wù),以Commit或Rollback結(jié)束一個(gè)事務(wù),以Commit結(jié)束事務(wù)是把事務(wù)中的修改永久化,即使這時(shí)發(fā)生斷電這樣的故障。例如下面是SQL Server中的一個(gè)顯式事務(wù)的例子。
Begin Tran
Update emp Set ename=’Smith’ Where empno=7369
Insert Into dept Values(60,’HR’,’GZh’)
Commit
隱式事務(wù),是指在當(dāng)前會(huì)話中用Set Implicit_Transactions On命令設(shè)置的事務(wù)類(lèi)型,這時(shí)任何DML語(yǔ)句(Delete、Update、Insert)都會(huì)開(kāi)始一個(gè)事務(wù),而事務(wù)的結(jié)束也是用Commit或Rollback。
在Oracle中沒(méi)有SQL Server的這些事務(wù)類(lèi)型,缺省情況下任何一個(gè)DML語(yǔ)句都會(huì)開(kāi)始一個(gè)事務(wù),直到用戶(hù)發(fā)出Commit或Rollback操作,這個(gè)事務(wù)才會(huì)結(jié)束,這與SQL Server的隱式事務(wù)模式相似。
三.事務(wù)隔離級(jí)別
在SQL92標(biāo)準(zhǔn)中,事務(wù)隔離級(jí)別分為四種,分別為:Read Uncommitted、Read Committed、Read Repeatable、Serializable,其中Read Uncommitted與Read Committed為語(yǔ)句級(jí)別的,而Read Repeatable與Serializable是針對(duì)事務(wù)級(jí)別的。
在Oracle和SQL Server中設(shè)置事務(wù)隔離級(jí)別的語(yǔ)句是相同的,都使用SQL92標(biāo)準(zhǔn)語(yǔ)法,即:
Set Transaction Isolation Level Read Committed
上面示例中的Read Committed可以被替換為其他三種隔離級(jí)別中的任意一種。
1.SQL Server中的隔離級(jí)別及實(shí)現(xiàn)機(jī)制
在SQL Server中提供了所有這四種隔離級(jí)別。
下面我們討論在SQL Server中,這幾種隔離級(jí)別的含義及其實(shí)現(xiàn)方式。
Read Uncommitted:一個(gè)會(huì)話可以讀取其他事務(wù)未提交的更新結(jié)果,如果這個(gè)事務(wù)最后以回滾結(jié)束,這時(shí)的讀取結(jié)果就可能是錯(cuò)誤的,所以多數(shù)的數(shù)據(jù)庫(kù)應(yīng)用都不會(huì)使用這種隔離級(jí)別。
Read Committed:這是SQL Server的缺省隔離級(jí)別,設(shè)置為這種隔離級(jí)別的事務(wù)只能讀取其他事務(wù)已經(jīng)提交的更新結(jié)果,否則,發(fā)生等待,但是其他會(huì)話可以修改這個(gè)事務(wù)中被讀取的記錄,而不必等待事務(wù)結(jié)束,顯然,在這種隔離級(jí)別下,一個(gè)事務(wù)中的兩個(gè)相同的讀取操作,其結(jié)果可能不同。
Read Repeatable:在一個(gè)事務(wù)中,如果在兩次相同條件的讀取操作之間沒(méi)有添加記錄的操作,也沒(méi)有其他更新操作導(dǎo)致在這個(gè)查詢(xún)條件下記錄數(shù)增多,則兩次讀取結(jié)果相同。換句話說(shuō),就是在一個(gè)事務(wù)中第一次讀取的記錄保證不會(huì)在這個(gè)事務(wù)期間發(fā)生改變。SQL Server是通過(guò)在整個(gè)事務(wù)期間給讀取的記錄加鎖實(shí)現(xiàn)這種隔離級(jí)別的,這樣,在這個(gè)事務(wù)結(jié)束前,其他會(huì)話不能修改事務(wù)中讀取的記錄,而只能等待事務(wù)結(jié)束,但是SQL Server不會(huì)阻礙其他會(huì)話向表中添加記錄,也不阻礙其他會(huì)話修改其他記錄。
Serializable:在一個(gè)事務(wù)中,讀取操作的結(jié)果是在這個(gè)事務(wù)開(kāi)始之前其他事務(wù)就已經(jīng)提交的記錄,SQL Server通過(guò)在整個(gè)事務(wù)期間給表加鎖實(shí)現(xiàn)這種隔離級(jí)別。在這種隔離級(jí)別下,對(duì)這個(gè)表的所有DML操作都是不允許的,即要等待事務(wù)結(jié)束,這樣就保證了在一個(gè)事務(wù)中的兩次讀取操作的結(jié)果肯定是相同的。
2.Oracle中的隔離級(jí)別及實(shí)現(xiàn)機(jī)制
在Oracle中,沒(méi)有Read Uncommitted及Repeatable Read隔離級(jí)別,這樣在Oracle中不允許一個(gè)會(huì)話讀取其他事務(wù)未提交的數(shù)據(jù)修改結(jié)果,從而避免了由于事務(wù)回滾發(fā)生的讀取錯(cuò)誤。Oracle中的Read Committed和Serializable級(jí)別,其含義與SQL Server類(lèi)似,但是實(shí)現(xiàn)方式卻大不一樣。
在Oracle中,存在所謂的回滾段(Oracle9i之前版本)或撤銷(xiāo)段(Oracle9i版本),Oracle在修改數(shù)據(jù)記錄時(shí),會(huì)把這些記錄被修改之前的結(jié)果存入回滾段或撤銷(xiāo)段中,就是因?yàn)檫@種機(jī)制,Oracle對(duì)于事務(wù)隔離級(jí)別的實(shí)現(xiàn)與SQL Server截然不同。在Oracle中,讀取操作不會(huì)阻礙更新操作,更新操作也不會(huì)阻礙讀取操作,這樣在Oracle中的各種隔離級(jí)別下,讀取操作都不會(huì)等待更新事務(wù)結(jié)束,更新操作也不會(huì)因?yàn)榱硪粋€(gè)事務(wù)中的讀取操作而發(fā)生等待,這也是Oracle事務(wù)處理的一個(gè)優(yōu)勢(shì)所在。
Oracle缺省的設(shè)置是Read Committed隔離級(jí)別(也稱(chēng)為語(yǔ)句級(jí)別的隔離),在這種隔離級(jí)別下,如果一個(gè)事務(wù)正在對(duì)某個(gè)表進(jìn)行DML操作,而這時(shí)另外一個(gè)會(huì)話對(duì)這個(gè)表的記錄進(jìn)行讀取操作,則Oracle會(huì)去讀取回滾段或撤銷(xiāo)段中存放的更新之前的記錄,而不會(huì)象SQL Server一樣等待更新事務(wù)的結(jié)束。
在Serializable隔離級(jí)別(也稱(chēng)為事務(wù)級(jí)別的隔離),事務(wù)中的讀取操作只能讀取這個(gè)事務(wù)開(kāi)始之前已經(jīng)提交的數(shù)據(jù)結(jié)果。如果在讀取時(shí),其他事務(wù)正在對(duì)記錄進(jìn)行修改,則Oracle就會(huì)在回滾段或撤銷(xiāo)段中去尋找對(duì)應(yīng)的原來(lái)未經(jīng)更改的記錄(而且是在讀取操作所在的事務(wù)開(kāi)始之前存放于回滾段或撤銷(xiāo)段的記錄),這時(shí)讀取操作也不會(huì)因?yàn)橄鄳?yīng)記錄被更新而等待。
四.DDL語(yǔ)句對(duì)事務(wù)的影響
1.Oracle中DDL語(yǔ)句對(duì)事務(wù)的影響
在Oracle中,執(zhí)行DDL語(yǔ)句(如Create Table、Create View等)時(shí),會(huì)在執(zhí)行之前自動(dòng)發(fā)出一個(gè)Commit命令,并在隨后發(fā)出一個(gè)Commit或者Rollback命令,也就是說(shuō),DDL會(huì)象如下偽碼一樣執(zhí)行:
Commit;
DDL_Statement;
If (Error) then
Rollback;
Else
Commit;
End if;
我們通過(guò)分析下面例子來(lái)看Oracle中,DDL語(yǔ)句對(duì)事務(wù)的影響:
Insert into some_table values(‘Before’);
Creaate table T(x int);
Insert into some_table values(‘After’);
Rollback;
由于在Oracle執(zhí)行Create table語(yǔ)句之前進(jìn)行了提交,而在Create table執(zhí)行后也會(huì)自動(dòng)發(fā)出Commit命令,所以只有插入After的行被回滾,而插入Before的行不會(huì)被回滾,Create table命令的結(jié)果也不會(huì)被回滾,即使Create table語(yǔ)句失敗,所進(jìn)行的Before插入也會(huì)被提交。如果最后發(fā)出Commit命令,因?yàn)椴迦隑efore及Create table的操作結(jié)果已經(jīng)在之前提交,所以Commit命令影響的只有插入After的操作。
2.SQL Server中DDL語(yǔ)句對(duì)事務(wù)的影響
在SQL Server中,DDL語(yǔ)句對(duì)事務(wù)的影響與其他DML語(yǔ)句相同,也就是說(shuō),在DML語(yǔ)句發(fā)出之前或之后,都不會(huì)自動(dòng)發(fā)出Commit命令。
在SQL Server 2000中,對(duì)于與上面Oracle同樣的例子,最后發(fā)出Rollback后,數(shù)據(jù)庫(kù)會(huì)回滾到插入Before之前的狀態(tài),即插入Before和After的行都會(huì)被回滾,數(shù)據(jù)表T也不會(huì)被創(chuàng)建。
如果最后發(fā)出Commit操作,則會(huì)把三個(gè)操作的結(jié)果全部提交。
五.用戶(hù)斷開(kāi)數(shù)據(jù)庫(kù)連接對(duì)事務(wù)的影響
另外,對(duì)應(yīng)于Oracle的管理客戶(hù)端工具SQL*Plus,在SQL Server 2000中是osql,兩種管理工具都是命令行工具,使用方式及作用也類(lèi)似,但是在SQL*Plus中,用戶(hù)退出連接時(shí),會(huì)自動(dòng)先發(fā)出Commit命令,然后再退出,而在osql中,如果用戶(hù)退出連接,會(huì)自動(dòng)發(fā)出Rollback命令,這對(duì)于SQL Server的自動(dòng)提交模式?jīng)]有什么影響,但如果處于隱式事務(wù)模式,其影響是顯而易見(jiàn)的。對(duì)于兩種數(shù)據(jù)庫(kù)產(chǎn)品的其他客戶(hù)端管理工具也有類(lèi)似的不同之處。