一篇文章帶你輕松了解MySQL之事務的簡介
前言:
事務(Transaction)是一組SQL組成的執(zhí)行單元(Unit),是數(shù)據(jù)庫并發(fā)控制和恢復回滾的基本單位。一個事務中可能包含多個SQL,要么都失敗,要么都成功,今天我們就詳細學習一下。
一、事務的起源
對于大部分程序員來說,他們的任務就是把現(xiàn)實世界的業(yè)務場景映射到數(shù)據(jù)庫世界。比如銀行為了存儲人們的賬戶信息會建立一個account表:
mysql> CREATE TABLE account (
id INT NOT NULL AUTO_INCREMENT COMMENT '自增id',
name VARCHAR(100) COMMENT '客戶名稱',
balance INT COMMENT '余額',
PRIMARY KEY (id)
);
Query OK, 0 rows affected (0.04 sec)張三和李四是一對好基友,他們都到銀行開一個賬戶,他們在現(xiàn)實世界中擁有的資產(chǎn)就會體現(xiàn)在數(shù)據(jù)庫世界的account表中。比如現(xiàn)在張三有11元,李四只有2元,那么現(xiàn)實中的這個情況映射到數(shù)據(jù)庫的account表就是這樣:
mysql> INSERT INTO account(name,balance) VALUES ('張三',11),('李四',2);
Query OK, 2 rows affected (0.01 sec)
Records: 2 Duplicates: 0 Warnings: 0
mysql> SELECT * FROM account;
+----+--------+---------+
| id | name | balance |
+----+--------+---------+
| 1 | 張三 | 11 |
| 2 | 李四 | 2 |
+----+--------+---------+
2 rows in set (0.00 sec)在某個特定的時刻,張三李四這些家伙在銀行所擁有的資產(chǎn)是一個特定的值,這些特定的值也可以被描述為賬戶在這個特定的時刻現(xiàn)實世界的一個狀態(tài)。隨著時間的流逝,張三和李四可能陸續(xù)進行向賬戶中存錢、取錢或者向別人轉(zhuǎn)賬等操作,這樣他們賬戶中的余額就可能發(fā)生變動,每一個操作都相當于現(xiàn)實世界中賬戶的一次狀態(tài)轉(zhuǎn)換。數(shù)據(jù)庫世界作為現(xiàn)實世界的一個映射,當然也要進行相應的變動。不變不知道,一變嚇一跳,現(xiàn)實世界中一些看似很簡單的狀態(tài)轉(zhuǎn)換,映射到數(shù)據(jù)庫世界卻不是那么容易的。比方說有一次李四需要10元錢,急忙打電話給張三要借10塊錢,現(xiàn)實世界中的張三走向了ATM機,輸入了李四的賬號以及10元的轉(zhuǎn)賬金額,然后按下確認,張三就拔卡走人了。對于數(shù)據(jù)庫世界來說,相當于執(zhí)行了下邊這兩條語句:
mysql> UPDATE account SET balance = balance - 10 WHERE id = 1; Query OK, 1 row affected (0.03 sec) Rows matched: 1 Changed: 1 Warnings: 0 mysql> UPDATE account SET balance = balance + 10 WHERE id = 2; Query OK, 1 row affected (0.01 sec) Rows matched: 1 Changed: 1 Warnings: 0
但是這里頭有個問題,上述兩條語句只執(zhí)行了一條時忽然服務器斷電了咋辦?把張三的錢扣了,但是沒給李四轉(zhuǎn)過去~ 即使對于單獨的一條語句,我們前邊嘮叨Buffer Pool時也說過,在對某個頁面進行讀寫訪問時,都會先把這個頁面加載到Buffer Pool中,之后如果修改了某個頁面,也不會立即把修改同步到磁盤,而只是把這個修改了的頁面加到Buffer Pool的flush鏈表中,在之后的某個時間點才會刷新到磁盤。如果在將修改過的頁刷新到磁盤之前系統(tǒng)崩潰了那豈不是李四還是沒錢?或者在刷新磁盤的過程中(只刷新部分數(shù)據(jù)到磁盤上)系統(tǒng)奔潰了李四也沒錢?
怎么才能保證讓可憐的李四有錢呢?其實再仔細想想,我們只是想讓某些數(shù)據(jù)庫操作符合現(xiàn)實世界中狀態(tài)轉(zhuǎn)換的規(guī)則而已,設計數(shù)據(jù)庫的大叔們仔細盤算了盤算,現(xiàn)實世界中狀態(tài)轉(zhuǎn)換的規(guī)則有好幾條,我們慢慢道來。
1.1 原子性(Atomicity)
現(xiàn)實世界中轉(zhuǎn)賬操作是一個不可分割的操作,也就是說要么壓根就沒轉(zhuǎn),要么轉(zhuǎn)賬成功,不能存在中間的狀態(tài),也就是轉(zhuǎn)了一半的這種情況。設計數(shù)據(jù)庫的時候把這種要么全做,要么全不做的規(guī)則稱之為原子性。但是在現(xiàn)實世界中的一個不可分割的操作卻可能對應著數(shù)據(jù)庫世界若干條不同的操作,數(shù)據(jù)庫中的一條操作也可能被分解成若干個步驟(比如先修改緩存頁,之后再刷新到磁盤等),最要命的是在任何一個可能的時間都可能發(fā)生意想不到的錯誤(可能是數(shù)據(jù)庫本身的錯誤,或者是操作系統(tǒng)錯誤,甚至是直接斷電之類的)而使操作執(zhí)行不下去,所以李四可能也沒錢。為了保證在數(shù)據(jù)庫世界中某些操作的原子性,設計數(shù)據(jù)庫的時候需要費一些心機來保證如果在執(zhí)行操作的過程中發(fā)生了錯誤,把已經(jīng)做了的操作恢復成沒執(zhí)行之前的樣子,這也是我們后邊章節(jié)要仔細嘮叨的內(nèi)容。
1.2 隔離性(Isolation)
現(xiàn)實世界中的兩次狀態(tài)轉(zhuǎn)換應該是互不影響的,比如說張三向李四同時進行的兩次金額為5元的轉(zhuǎn)賬(假設可以在兩個ATM機上同時操作)。那么最后張三的賬戶里肯定會少10元,李四的賬戶里肯定多了10元。但是到對應的數(shù)據(jù)庫世界中,事情又變的復雜了一些。為了簡化問題,我們粗略的假設張三向李四轉(zhuǎn)賬5元的過程是由下邊幾個步驟組成的:
- 步驟一:讀取張三賬戶的余額到變量A中,這一步驟簡寫為read(A)
- 步驟二:將張三賬戶的余額減去轉(zhuǎn)賬金額,這一步驟簡寫為A = A - 5
- 步驟三:將張三賬戶修改過的余額寫到磁盤里,這一步驟簡寫為write(A)
- 步驟四:讀取李四賬戶的余額到變量B,這一步驟簡寫為read(B)
- 步驟五:將李四賬戶的余額加上轉(zhuǎn)賬金額,這一步驟簡寫為B = B + 5
- 步驟六:將李四賬戶修改過的余額寫到磁盤里,這一步驟簡寫為write(B)
我們將張三向李四同時進行的兩次轉(zhuǎn)賬操作分別稱為T1和T2,在現(xiàn)實世界中T1和T2是應該沒有關(guān)系的,可以先執(zhí)行完T1,再執(zhí)行T2,或者先執(zhí)行完T2,再執(zhí)行T1,對應的數(shù)據(jù)庫操作就像這樣:

但是很不幸,真實的數(shù)據(jù)庫中T1和T2的操作可能交替執(zhí)行,比如這樣:

如果按照上圖中的執(zhí)行順序來進行兩次轉(zhuǎn)賬的話,最終張三的賬戶里還剩6元錢,相當于只扣了5元錢,但是李四的賬戶里卻成了12元錢,相當于多了10元錢,這銀行豈不是要虧死了?
所以對于現(xiàn)實世界中狀態(tài)轉(zhuǎn)換對應的某些數(shù)據(jù)庫操作來說,不僅要保證這些操作以原子性的方式執(zhí)行完成,而且要保證其它的狀態(tài)轉(zhuǎn)換不會影響到本次狀態(tài)轉(zhuǎn)換,這個規(guī)則被稱之為隔離性。這時設計數(shù)據(jù)庫的大叔們就需要采取一些措施來讓訪問相同數(shù)據(jù)(上例中的A賬戶和B賬戶)的不同狀態(tài)轉(zhuǎn)換(上例中的T1和T2)對應的數(shù)據(jù)庫操作的執(zhí)行順序有一定規(guī)律,這也是我們后邊章節(jié)要仔細嘮叨的內(nèi)容。
1.3 一致性(Consistency)
我們生活的這個世界存在著形形色色的約束,比如身份證號不能重復,性別只能是男或者女,高考的分數(shù)只能在0~750之間,人民幣面值最大只能是100,紅綠燈只有3種顏色,房價不能為負的,學生要聽老師話,吧啦吧啦有點兒扯遠了~ 只有符合這些約束的數(shù)據(jù)才是有效的,比如有個小孩兒跟你說他高考考了1000分,你一聽就知道他胡扯呢。數(shù)據(jù)庫世界只是現(xiàn)實世界的一個映射,現(xiàn)實世界中存在的約束當然也要在數(shù)據(jù)庫世界中有所體現(xiàn)。如果數(shù)據(jù)庫中的數(shù)據(jù)全部符合現(xiàn)實世界中的約束(all defined rules),我們說這些數(shù)據(jù)就是一致的,或者說符合一致性的。
如何保證數(shù)據(jù)庫中數(shù)據(jù)的一致性(就是符合所有現(xiàn)實世界的約束)呢?這其實靠兩方面的努力:
方面一: 數(shù)據(jù)庫本身能為我們保證一部分一致性需求(就是數(shù)據(jù)庫當身可以保證一部分現(xiàn)實世界的約束永遠有效)。
我們知道MySQL數(shù)據(jù)庫可以為表建立主鍵、唯一索引、外鍵、聲明某個列為NOT NULL來拒絕NULL值的插入。比如說當我們對某個列建立唯一索引時,如果插入某條記錄時該列的值重復了,那么MySQL就會報錯并且拒絕插入。除了這些我們已經(jīng)非常熟悉的保證一致性的功能,MySQL還支持CHECK語法來當定義約束,比如這樣:
CREATE TABLE account (
id INT NOT NULL AUTO_INCREMENT COMMENT '自增id',
name VARCHAR(100) COMMENT '客戶名稱',
balance INT COMMENT '余額',
PRIMARY KEY (id),
CHECK (balance >= 0)
);上述例子中的CHECK語句本意是想規(guī)定balance列不能存儲小于0的數(shù)字,對應的現(xiàn)實世界的意思就是銀行賬戶余額不能小于0。但是很遺憾,MySQL僅僅支持 CHECK語法,但實際上并沒有一點卵用,也就是說即使我們使用上述帶有CHECK子句的建表語句來創(chuàng)建account表,那么在后續(xù)插入或更新記錄時,MySQL并不會去檢查CHECK子句中的約束是否成立。
小提示:
其它的一些數(shù)據(jù)庫,比如SQL Server或者Oracle支持的CHECK語法是有實實在在的作用的,每次進行插入或更新記錄之前都會檢查一下數(shù)據(jù)是否符合CHECK子句中指定的約束條件是否成立,如果不成立的話就會拒絕插入或更新。
雖然CHECK子句對一致性檢查沒什么卵用,但是我們還是可以通過定義觸發(fā)器的方式來當定義一些約束條件以保證數(shù)據(jù)庫中數(shù)據(jù)的一致性。
方面二: 更多的一致性需求需要靠寫業(yè)務代碼的程序員當己保證。
為建立現(xiàn)實世界和數(shù)據(jù)庫世界的對應關(guān)系,理論上應該把現(xiàn)實世界中的所有約束都反應到數(shù)據(jù)庫世界中,但是很不幸,在更改數(shù)據(jù)庫數(shù)據(jù)時進行一致檢查是一個耗費性能的?作,比方說我們?yōu)?code>account表建立了一個觸發(fā)器,每當插入或者更新記錄時都會校驗一下balance列的值是不是大于0,這就會影響到插入或更新的速度。僅僅是校驗一行記錄符不符合一致性需求倒也不是什么大問題,有的一致性需求簡直變態(tài),比方說銀行會建立一張代表賬單的表,里邊兒記錄了每個賬戶的每筆交易,每一筆交易完成后,都需要保證整個系統(tǒng)的余額等于所有賬戶的收入減去所有賬戶的支出。如果在數(shù)據(jù)庫層面實現(xiàn)這個一致性需求的話,每次發(fā)生交易時,都需要將所有的收入加起來減去所有的支出,再將所有的賬戶余額加起來,看看兩個值相不相等。這不是搞笑呢么,如果賬單表里有幾億條記錄,光是這個校驗的過程可能就要跑好幾個小時,也就是說你在煎餅攤買個煎餅,使用銀行卡付款之后要等好幾個小時才能提示付款成功,這樣的性能代價是完全承受不起的。
現(xiàn)實生活中復雜的一致性需求比比皆是,而由于性能問題把一致性需求交給數(shù)據(jù)庫去解決這是不現(xiàn)實的,所以這個鍋就甩給了業(yè)務端程序員。比方說我們的account表,我們也可以不建立觸發(fā)器,只要編寫業(yè)務的程序員在當己的業(yè)務代碼里判斷一下,當某個操作會將balance列的值更新為小于0的值時,就不執(zhí)行該操作就好了嘛!
我們前邊嘮叨的原子性和隔離性都會對一致性產(chǎn)生影響,比如我們現(xiàn)實世界中轉(zhuǎn)賬操作完成后,有一個一致性需求就是參與轉(zhuǎn)賬的賬戶的總的余額是不變的。如果數(shù)據(jù)庫不遵循原子性要求,也就是轉(zhuǎn)了一半就不轉(zhuǎn)了,也就是說給張三扣了錢而沒給李四轉(zhuǎn)過去,那最后就是不符合一致性需求的;類似的,如果數(shù)據(jù)庫不遵循隔離性要求,就像我們前邊嘮叨隔離性時舉的例子中所說的,最終張三賬戶中扣的錢和李四賬戶中漲的錢可能就不一樣了,也就是說不符合一致性需求了。所以說,數(shù)據(jù)庫某些操作的原子性和隔離性都是保證一致性的一種手段,在操作執(zhí)行完成后保證符合所有既定的約束則是一種結(jié)果。那滿足原子性和隔離性的操作一定就滿足一致性么?那倒也不一定,比如說張三要轉(zhuǎn)賬20元給李四,雖然在滿足原子性和隔離性,但轉(zhuǎn)賬完成了之后張三的賬戶的余額就成負的了,這顯然是不滿足一致性的。那不滿足原子性和隔離性的操作就一定不滿足一致性么?這也不一定,只要最后的結(jié)果符合所有現(xiàn)實世界中的約束,那么就是符合一致性的。
1.4 持久性(Durability)
當現(xiàn)實世界的一個狀態(tài)轉(zhuǎn)換完成后,這個轉(zhuǎn)換的結(jié)果將永久的保留,這個規(guī)則被設計數(shù)據(jù)庫的大叔們稱為持久性。比方說張三向李四轉(zhuǎn)賬,當ATM機提示轉(zhuǎn)賬成功了,就意味著這次賬戶的狀態(tài)轉(zhuǎn)換完成了,張三就可以拔卡走人了。如果當張三走掉之后,銀行又把這次轉(zhuǎn)賬操作給撤銷掉,恢復到?jīng)]轉(zhuǎn)賬之前的樣子,那李四還是沒錢,所以這個持久性是非常重要的。當把現(xiàn)實世界的狀態(tài)轉(zhuǎn)換映射到數(shù)據(jù)庫世界時,持久性意味著該轉(zhuǎn)換對應的數(shù)據(jù)庫操作所修改的數(shù)據(jù)都應該在磁盤上保留下來,不論之后發(fā)生了什么事故,本次轉(zhuǎn)換造成的影響都不應該被丟失掉。
二、事務的概念
為了方便大家記住我們上邊嘮叨的現(xiàn)實世界狀態(tài)轉(zhuǎn)換過程中需要遵守的4個特性,我們把原子性(Atomicity)、隔離性(Isolation)、一致性(Consistency)和持久性(Durability)這四個詞對應的英文單詞首字母提取出來就是A、I、C、D,稍微變換一下順序可以組成一個完整的英文單詞:ACID。想必大家都是學過初高中英語的,ACID是英文酸的意思,以后我們提到ACID這個詞兒,大家就應該想到原子性、一致性、隔離性、持久性這幾個規(guī)則。另外,設計數(shù)據(jù)庫的大叔為了方便起見,把需要保證原子性、隔離性、一致性和持久性的一個或多個數(shù)據(jù)庫操作稱之為一個事務(英文名是:transaction)。
我們現(xiàn)在知道事務是一個抽象的概念,它其實對應著一個或多個數(shù)據(jù)庫操作,設計數(shù)據(jù)庫的大叔根據(jù)這些操作所執(zhí)行的不同階段把事務大致上劃分成了這么幾個狀態(tài):
- 活動的(active)
事務對應的數(shù)據(jù)庫操作正在執(zhí)行過程中時,我們就說該事務處在活動的狀態(tài)。 - 部分提交的(partially committed)
當事務中的最后一個操作執(zhí)行完成,但由于操作都在內(nèi)存中執(zhí)行,所造成的影響并沒有刷新到磁盤時,我們就說該事務處在部分提交的狀態(tài)。 - 失敗的(failed)
當事務處在活動的或者部分提交的狀態(tài)時,可能遇到了某些錯誤(數(shù)據(jù)庫當身的錯誤、操作系統(tǒng)錯誤或者直接斷電等)而無法繼續(xù)執(zhí)行,或者人為的停止當前事務的執(zhí)行,我們就說該事務處在失敗的狀態(tài)。 - 中止的(aborted)
如果事務執(zhí)行了半截而變?yōu)槭〉臓顟B(tài),比如我們前邊嘮叨的張三向李四轉(zhuǎn)賬的事務,當張三賬戶的錢被扣除,但是李四賬戶的錢沒有增加時遇到了錯誤,從而當前事務處在了失敗的狀態(tài),那么就需要把已經(jīng)修改的張三賬戶余額調(diào)整為未轉(zhuǎn)賬之前的金額,換句話說,就是要撤銷失敗事務對當前數(shù)據(jù)庫造成的影響。書面一點的話,我們把這個撤銷的過程稱之為回滾。當回滾操作執(zhí)行完畢時,也就是數(shù)據(jù)庫恢復到了執(zhí)行事務之前的狀態(tài),我們就說該事務處在了中止的狀態(tài)。 - 提交的(committed)
當一個處在部分提交的狀態(tài)的事務將修改過的數(shù)據(jù)都同步到磁盤上之后,我們就可以說該事務處在了提交的狀態(tài)。
隨著事務對應的數(shù)據(jù)庫操作執(zhí)行到不同階段,事務的狀態(tài)也在不斷變化,一個基本的狀態(tài)轉(zhuǎn)換圖如下所示:

從圖中大家也可以看出了,只有當事務處于提交的或者中止的狀態(tài)時,一個事務的生命周期才算是結(jié)束了。對于已經(jīng)提交的事務來說,該事務對數(shù)據(jù)庫所做的修改將永久生效,對于處于中止狀態(tài)的事務,該事務對數(shù)據(jù)庫所做的所有修改都會被回滾到?jīng)]執(zhí)行該事務之前的狀態(tài)。
小提示:
大家知道我們的計算機術(shù)語基本上全是從英文翻譯成中文的,事務的英文是transaction,英文直譯就是交易,買賣的意思,交易就是買的人付錢,賣的人交貨,不能付了錢不交貨,交了貨不付錢把,所以交易本身就是一種不可分割的操作。不知道是哪位大神把transaction翻譯成了事務(我想估計是他們也想不出什么更好的詞兒,只能隨便找一個了),事務這個詞兒完全沒有交易、買賣的意思,所以大家理解起來也會比較困難,外國人理解transaction可能更好理解一點吧~
三、MySQL中事務的語法
我們說事務的本質(zhì)其實只是一系列數(shù)據(jù)庫操作,只不過這些數(shù)據(jù)庫操作符合ACID特性而已,那么MySQL中如何將某些操作放到一個事務里去執(zhí)行的呢?我們下邊就來重點嘮叨嘮叨。
3.1 開啟事務
我們可以使用下邊兩種語句之一來開啟一個事務:
語句一: BEGIN [WORK];
BEGIN語句代表開啟一個事務,后邊的單詞WORK可有可無。開啟事務后,就可以繼續(xù)寫若干條語句,這些語句都屬于剛剛開啟的這個事務。
mysql> BEGIN; Query OK, 0 rows affected (0.00 sec) mysql> 加入事務的語句...
語句二: START TRANSACTION;
START TRANSACTION語句和BEGIN語句有著相同的功效,都標志著開啟一個事務,比如這樣:
mysql> START TRANSACTION; Query OK, 0 rows affected (0.00 sec) mysql> 加入事務的語句...
不過比BEGIN語句牛逼一點兒的是,可以在START TRANSACTION語句后邊跟隨幾個修飾符,就是它們幾個:
READ ONLY:標識當前事務是一個只讀事務,也就是屬于該事務的數(shù)據(jù)庫操作只能讀取數(shù)據(jù),而不能修改數(shù)據(jù)。
小提示:
其實只讀事務中只是不允許修改那些其他事務也能訪問到的表中的數(shù)據(jù),對于臨時表來說(我們使用CREATE TMEPORARY
TABLE創(chuàng)建的表),由于它們只能在當前會話中可見,所以只讀事務其實也是可以對臨時表進行增、刪、改操作的。
READ WRITE:標識當前事務是一個讀寫事務,也就是屬于該事務的數(shù)據(jù)庫操作既可以讀取數(shù)據(jù),也可以修改數(shù)據(jù)。WITH CONSISTENT SNAPSHOT:啟動一致性讀(先不用關(guān)心啥是個一致性讀,后邊的章節(jié)才會嘮叨)。
比如我們想開啟一個只讀事務的話,直接把READ ONLY這個修飾符加在START TRANSACTION語句后邊就好,比如這樣:
START TRANSACTION READ ONLY;
如果我們想在START TRANSACTION后邊跟隨多個修飾符的話,可以使用逗號將修飾符分開,比如開啟一個只讀事務和一致性讀,就可以這樣寫:
START TRANSACTION READ ONLY, WITH CONSISTENT SNAPSHOT;
或者開啟一個讀寫事務和一致性讀,就可以這樣寫:
START TRANSACTION READ WRITE, WITH CONSISTENT SNAPSHOT
不過這里需要大家注意的一點是,READ ONLY和READ WRITE是用來設置所謂的事務訪問模式的,就是以只讀還是讀寫的方式來訪問數(shù)據(jù)庫中的數(shù)據(jù),一個事務的訪問模式不能同時既設置為只讀的也設置為讀寫的,所以我們不能同時把READ ONLY和READ WRITE放到START TRANSACTION語句后邊。另外,如果我們不顯式指定事務的訪問模式,那么該事務的訪問模式就是讀寫模式。
3.2 提交事務
開啟事務之后就可以繼續(xù)寫需要放到該事務中的語句了,當最后一條語句寫完了之后,我們就可以提交該事務了,提交的語句也很簡單:COMMIT [WORK]
COMMIT語句就代表提交一個事務,后邊的WORK可有可無。比如我們上邊說張三給李四轉(zhuǎn)10元錢其實對應MySQL中的兩條語句,我們就可以把這兩條語句放到一個事務中,完整的過程就是這樣:
mysql> BEGIN; Query OK, 0 rows affected (0.00 sec) mysql> UPDATE account SET balance = balance - 10 WHERE id = 1; Query OK, 1 row affected (0.02 sec) Rows matched: 1 Changed: 1 Warnings: 0 mysql> UPDATE account SET balance = balance + 10 WHERE id = 2; Query OK, 1 row affected (0.00 sec) Rows matched: 1 Changed: 1 Warnings: 0 mysql> COMMIT; Query OK, 0 rows affected (0.00 sec)
3.3 手動中止事務
如果我們寫了幾條語句之后發(fā)現(xiàn)上邊的某條語句寫錯了,我們可以手動的使用下邊這個語句來將數(shù)據(jù)庫恢復到事務執(zhí)行之前的樣子:ROLLBACK [WORK]
ROLLBACK語句就代表中止并回滾一個事務,后邊的WORK可有可無類似的。比如我們在寫張三給李四轉(zhuǎn)賬10元錢對應的MySQL語句時,先給張三扣了10元,然后一時大意只給李四賬戶上增加了1元,此時就可以使用ROLLBACK語句進行回滾,完整的過程就是這樣:
mysql> BEGIN; Query OK, 0 rows affected (0.00 sec) mysql> UPDATE account SET balance = balance - 10 WHERE id = 1; Query OK, 1 row affected (0.00 sec) Rows matched: 1 Changed: 1 Warnings: 0 mysql> UPDATE account SET balance = balance + 1 WHERE id = 2; Query OK, 1 row affected (0.00 sec) Rows matched: 1 Changed: 1 Warnings: 0 mysql> ROLLBACK; Query OK, 0 rows affected (0.00 sec)
這里需要強調(diào)一下,ROLLBACK語句是我們程序員手動的去回滾事務時才去使用的,如果事務在執(zhí)行過程中遇到了某些錯誤而無法繼續(xù)執(zhí)行的話,事務當身會當動的回滾。
小提示:
我們這里所說的開啟、提交、中止事務的語法只是針對使用?框框時通過mysql客戶端程序與服務器進行交互時控制事務的語法,如果大家使用的是別的客戶端程序,比如JDBC之類的,那需要參考相應的文檔來看看如何控制事務。
3.4 支持事務的存儲引擎
MySQL中并不是所有存儲引擎都支持事務的功能,?前只有InnoDB和NDB存儲引擎支持(,如果某個事務中包含了修改使用不支持事務的存儲引擎的表,那么對該使用不支持事務的存儲引擎的表所做的修改將無法進行回滾。比方說我們有兩個表,tbl1使用支持事務的存儲引擎InnoDB,tbl2使用不支持事務的存儲引擎MyISAM,它們的建表語句如下所示:
mysql> CREATE TABLE demo14 (
i int
) engine=InnoDB;
Query OK, 0 rows affected (0.05 sec)
mysql> CREATE TABLE demo15 (
i int
) ENGINE=MyISAM;
Query OK, 0 rows affected (0.01 sec)
我們看看先開啟一個事務,寫一條插入語句后再回滾該事務,demo14和demo15的表現(xiàn)有什么不同:
mysql> SELECT * FROM demo14; Empty set (0.00 sec) mysql> BEGIN; Query OK, 0 rows affected (0.00 sec) mysql> INSERT INTO demo14 VALUES(1); Query OK, 1 row affected (0.00 sec) mysql> ROLLBACK; Query OK, 0 rows affected (0.00 sec) mysql> SELECT * FROM demo14; Empty set (0.00 sec)
以看到,對于使用支持事務的存儲引擎的demo14表來說,我們在插入一條記錄再回滾后,demo14就恢復到?jīng)]有插入記錄時的狀態(tài)了。再看看demo15表的表現(xiàn):
mysql> SELECT * FROM demo14; Empty set (0.01 sec) mysql> SELECT * FROM demo15; Empty set (0.00 sec) mysql> BEGIN; Query OK, 0 rows affected (0.00 sec) mysql> INSERT INTO demo15 VALUES(1); Query OK, 1 row affected (0.00 sec) mysql> ROLLBACK; Query OK, 0 rows affected, 1 warning (0.00 sec) mysql> SELECT * FROM demo15; +------+ | i | +------+ | 1 | +------+ 1 row in set (0.01 sec)
可以看到,雖然我們使用了ROLLBACK語句來回滾事務,但是插入的那條記錄還是留在了demo15表中。
3.5 自動提交
MySQL中有一個系統(tǒng)變量autocommit:
mysql> SHOW VARIABLES LIKE 'autocommit'; +---------------+-------+ | Variable_name | Value | +---------------+-------+ | autocommit | ON | +---------------+-------+ 1 row in set (0.00 sec) mysql> set persist autocommit = 'ON'; Query OK, 0 rows affected (0.00 sec)
可以看到它的默認值為ON,也就是說默認情況下,如果我們不顯式的使用START TRANSACTION或者BEGIN語句開啟一個事務,那么每一條語句都算是一個獨立的事務,這種特性稱之為事務的當動提交。假如我們在張三向李四轉(zhuǎn)賬10元時不以START TRANSACTION或者BEGIN語句顯式的開啟一個事務,那么下邊這兩條語句就相當于放到兩個獨立的事務中去執(zhí)行:
UPDATE account SET balance = balance - 10 WHERE id = 1; UPDATE account SET balance = balance + 10 WHERE id = 2;
當然,如果我們想關(guān)閉這種當動提交的功能,可以使用下邊兩種方法之一:
- 顯式的的使用
START TRANSACTION或者BEGIN語句開啟一個事務。這樣在本次事務提交或者回滾前會暫時關(guān)閉掉當動提交的功能。 - 把系統(tǒng)變量
autocommit的值設置為OFF,就像這樣:SET autocommit = OFF;
這樣的話,我們寫入的多條語句就算是屬于同一個事務了,直到我們顯式的寫出COMMIT語句來把這個事務提交掉,或者顯式的寫出ROLLBACK語句來把這個事務回滾掉。
3.6 隱式提交
當我們使用START TRANSACTION或者BEGIN語句開啟了一個事務,或者把系統(tǒng)變量autocommit的值設置為OFF時,事務就不會進行當動提交,但是如果我們輸入了某些語句之后就會悄悄的提交掉,就像我們輸入了COMMIT語句了一樣,這種因為某些特殊的語句而導致事務提交的情況稱為隱式提交,這些會導致事務隱式提交的語句包括:
- 定義或修改數(shù)據(jù)庫對象的數(shù)據(jù)定義語言(Data definition language,縮寫為:
DDL)。所謂的數(shù)據(jù)庫對象,指的就是數(shù)據(jù)庫、表、視圖、存儲過程等等這些東西。當我們使用CREATE、ALTER、DROP等語句去修改這些所謂的數(shù)據(jù)庫對象時,就會隱式的提交前邊語句所屬于的事務,就像這樣:
BEGIN;SELECT ...#事務中的一條語句UPDATE ...#事務中的一條語句...#事務中的其它語句CREATE TABLE ...# `此語句會隱式的提交前邊語句所屬于的事務
- 隱式使用或修改
mysql數(shù)據(jù)庫中的表
當我們使用ALTER USER、CREATE USER、DROP USER、GRANT、RENAME USER、REVOKE、SET PASSWORD等語句時也會隱式的提交前邊語句所屬于的事務。
- 事務控制或關(guān)于鎖定的語句
當我們在一個事務還沒提交或者回滾時就又使用START TRANSACTION或者BEGIN語句開啟了另一個事務時,會隱式的提交上一個事務,比如這樣:
BEGIN;SELECT ...#事務中的一條語句UPDATE ...#事務中的一條語句...# 事務中的其它語句BEGIN;# 此語句會隱式的提交前邊語句所屬于的事務
或者當前的autocommit系統(tǒng)變量的值為OFF,我們手動把它調(diào)為ON時,也會隱式的提交前邊語句所屬的事務。
或者使用`LOCK TABLES、UNLOCK TABLES等關(guān)于鎖定的語句也會隱式的提交前邊語句所屬的事務。
- 加載數(shù)據(jù)的語句:比如我們使用
LOAD DATA語句來批量往數(shù)據(jù)庫中導入數(shù)據(jù)時,也會隱式的提交前邊語句所屬的事務。 - 關(guān)于MySQL復制的一些語句
使用START SLAVE、STOP SLAVE、RESET SLAVE、CHANGE MASTER TO等語句時也會隱式的提交前邊語句所屬的事務。 - 其它的一些語句
使用ANALYZE TABLE、CACHE INDEX、CHECK TABLE、FLUSH、 LOAD INDEX INTO CACHE、OPTIMIZE TABLE、REPAIR TABLE、RESET等語句也會隱式的提交前邊語句所屬的事務。
小提示:
上邊提到的一些語句,如果你都認識并且知道是干嘛用的那再好不過了,不認識也不要?餒,這里寫出來只是為了內(nèi)容的完整性,把可能會導致事務隱式提交的情況都列舉一下,具體每個語句都是干嘛用的等我們遇到了再說哈。
3.7 保存點
如果你開啟了一個事務,并且已經(jīng)敲了很多語句,忽然發(fā)現(xiàn)上一條語句有點問題,你只好使用ROLLBACK語句來讓數(shù)據(jù)庫狀態(tài)恢復到事務執(zhí)行之前的樣子,然后一切從頭再來,總有一種一夜回到解放前的感覺。所以數(shù)據(jù)庫的提出了一個保存點(英文:savepoint)的概念,就是在事務對應的數(shù)據(jù)庫語句中打幾個點,我們在調(diào)用ROLLBACK語句時可以指定會滾到哪個點,而不是回到最初的原點。定義保存點的語法如下:SAVEPOINT 保存點名稱
當我們想回滾到某個保存點時,可以使用下邊這個語句(下邊語句中的單詞WORK和SAVEPOINT是可有可無的):ROLLBACK [WORK] TO [SAVEPOINT] 保存點名稱
不過如果ROLLBACK語句后邊不跟隨保存點名稱的話,會直接回滾到事務執(zhí)行之前的狀態(tài)。
如果我們想刪除某個保存點,可以使用這個語句:RELEASE SAVEPOINT 保存點名稱
下邊還是以張三向李四轉(zhuǎn)賬10元的例子展示一下保存點的用法,在執(zhí)行完扣除張三賬戶的錢10元的語句之后打一個保存點:
mysql> SELECT * FROM account; +----+--------+---------+ | id | name | balance | +----+--------+---------+ | 1 | 張三 | 12 | | 2 | 李四 | 2 | +----+--------+---------+ 2 rows in set (0.00 sec) mysql> BEGIN; Query OK, 0 rows affected (0.00 sec) mysql> UPDATE account SET balance = balance - 10 WHERE id = 1; Query OK, 1 row affected (0.01 sec) Rows matched: 1 Changed: 1 Warnings: 0 mysql> SAVEPOINT s1; # 一個保存點 Query OK, 0 rows affected (0.00 sec) mysql> SELECT * FROM account; +----+--------+---------+ | id | name | balance | +----+--------+---------+ | 1 | 張三 | 2 | | 2 | 李四 | 2 | +----+--------+---------+ 2 rows in set (0.00 sec) mysql> UPDATE account SET balance = balance + 1 WHERE id = 2; # 更新錯了 Query OK, 1 row affected (0.01 sec) Rows matched: 1 Changed: 1 Warnings: 0 mysql> ROLLBACK TO s1; # 回滾到保存點s1處 Query OK, 0 rows affected (0.00 sec) mysql> SELECT * FROM account; +----+--------+---------+ | id | name | balance | +----+--------+---------+ | 1 | 張三 | 2 | | 2 | 李四 | 2 | +----+--------+---------+ 2 rows in set (0.01 sec)
總結(jié)
到此這篇關(guān)于MySQL之事務的簡介的文章就介紹到這了,更多相關(guān)MySQL事務簡介內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
MySQL刪除數(shù)據(jù)后自增主鍵ID不連貫問題及解決
這篇文章主要介紹了MySQL刪除數(shù)據(jù)后自增主鍵ID不連貫問題及解決,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-09-09
mysql時間戳格式化函數(shù)from_unixtime使用的簡單說明
mysql中的FROM_UNIXTIME函數(shù)可以數(shù)據(jù)庫中整型類的時間戳格式化為字符串的日期時間格式,下面這篇文章主要給大家介紹了關(guān)于mysql時間戳格式化函數(shù)from_unixtime使用的簡單說明,需要的朋友可以參考下2022-08-08
CentOS7環(huán)境下安裝MySQL5.5數(shù)據(jù)庫
大家好,本篇文章主要講的是CentOS7環(huán)境下安裝MySQL5.5數(shù)據(jù)庫,感興趣的同學趕快來看一看吧,對你有幫助的話記得收藏一下,方便下次瀏覽2021-12-12

