從一個問題開始
最近銀行這個事情鬧的比較厲害啊,很多儲戶的錢放在銀行,就不翼而飛了,而銀行還不管不問,說是用戶的責任,打官司,用戶還能輸了,這就是“社會主義”。咱還是少發牢騷,多種樹,莫談國事。
說到銀行存錢,就不得不說一下從銀行取錢這件事情,從ATM機取錢這件簡單的事情,實際上主要分為以下幾個步驟:
登陸ATM機,輸入密碼;
連接數據庫,驗證密碼;
驗證成功,獲得用戶信息,比如存款余額等;
用戶輸入需要取款的金額,按下確認鍵;
從後台數據庫中減掉用戶賬戶上的對應金額;
ATM吐出錢;
用戶把錢拿走。
一個簡單的取錢,主要分為以上幾步。不知道大家有沒有“天真”的想過,如果在第5步中,後台數據庫中已經把錢減掉了,但是ATM還就是沒有吐出錢(雖然實際也發生過,但是畢竟是低概率事件),這該怎麼辦?
關於這個問題,銀行系統的開發人員早就想過了,那麼他們是怎麼來搞定這個問題的呢?這就要說到今天總結的事務這個概念了。
簡單說說事務
對於上面的取錢這個事情,如果有一步出現了錯誤,那麼就取消整個取錢的動作;簡單來說,就是取錢這7步,要麼都完成,要麼就啥也不做。在數據庫中,事務也是這個道理。
事務由一條或者多條sql語句組成,在事務中的操作,這些sql語句要麼都執行,要麼都不執行,這就是事務的目的。
對於事務而言,它需要滿足ACID特性,下面就簡要的說說事務的ACID特性。
A,表示原子性;原子性指整個數據庫事務是不可分割的工作單位。只有使事務中所有的數據庫操作都執行成功,整個事務的執行才算成功。事務中任何一個sql語句執行失敗,那麼已經執行成功的sql語句也必須撤銷,數據庫狀態應該退回到執行事務前的狀態;
C,表示一致性;也就是說一致性指事務將數據庫從一種狀態轉變為另一種一致的狀態,在事務開始之前和事務結束以後,數據庫的完整性約束沒有被破壞;
I,表示隔離性;隔離性也叫做並發控制、可串行化或者鎖。事務的隔離性要求每個讀寫事務的對象與其它事務的操作對象能相互分離,即該事務提交前對其它事務都不可見,這通常使用鎖來實現;
D,持久性,表示事務一旦提交了,其結果就是永久性的,也就是數據就已經寫入到數據庫了,如果發生了宕機等事故,數據庫也能將數據恢復。
總結了一些事務的基本概念,在MySQL中,事務還是分為很多中的,下面就來看看到底有哪些事務。
有哪些事務
你能想象到嗎?就這麼個破事務還會分以下這麼多種:
扁平事務;
帶有保存點的扁平事務;
鏈事務;
嵌套事務;
分布式事務。
現在就來對這些事務從概念的層面上進行簡單的總結一下。
扁平事務
扁平事務是最簡單的一種,也是實際開發中使用的最多的一種事務。在這種事務中,所有操作都處於同一層次,最常見的方式如下:
BEGIN WORK
Operation 1
Operation 2
Operation 3
...
Operation N
COMMIT WORK
或者是這種:
BEGIN WORK
Operation 1
Operation 2
Operation 3
...
Operation N
(Error Occured)
ROLLBACK WORK
扁平事務的主要缺點是不能提交或回滾事務的某一部分,或者分幾個獨立的步驟去提交。比如有這樣的一個例子,我從呼和浩特去深圳,為了便宜,我可能這麼干:
BEGIN WORK
Operation1:呼和浩特---火車--->北京
Operation2:北京---飛機--->深圳
ROLLBACK WORK
但是,如果Operation1,從呼和浩特到北京的火車晚點了,錯過了航班,怎麼辦?感覺扁平事務的特性,那我就需要回滾,我再回到呼和浩特,那麼這樣成本是不是也太高了啊,所以就有了下面的第二種事務——帶有保存點的扁平事務。
帶有保存點的扁平事務
這種事務除了支持扁平事務支持的操作外,允許在事務執行過程中回滾到同一事務中較早的一個狀態,這是因為可能某些事務在執行過程中出現的錯誤並不會對所有的操作都無效,放棄整個事務不合乎要求,開銷也太大。保存點用來通知系統應該記住事務當前的狀態,以便以後發生錯誤時,事務能回到該狀態。
鏈事務
鏈事務,就是指回滾時,只能恢復到最近一個保存點;而帶有保存點的扁平事務則可以回滾到任意正確的保存點。
嵌套事務
看下面這個,你就能明白了,啥是嵌套事務:
BEGIN WORK
SubTransaction1:
BEGIN WORK
SubOperationX
COMMIT WORK
SubTransaction2:
BEGIN WORK
SubOperationY
COMMIT WORK
...
SubTransactionN:
BEGIN WORK
SubOperationN
COMMIT WORK
COMMIT WORK
這就是嵌套事務,在事務中再嵌套事務,位於根節點的事務稱為頂層事務。事務的前驅稱為父事務,其它事務稱為子事務。事務的前驅稱為父事務,事務的下一層稱為子事務。
子事務既可以提交也可以回滾,但是它的提交操作並不馬上生效,除非由其父事務提交。因此就可以確定,任何子事務都在頂層事務提交後才真正的被提交了。同理,任意一個事務的回滾都會引起它的所有子事務一同回滾。
分布式事務
分布式事務通常是指在一個分布式環境下運行的扁平事務,因此需要根據數據所在位置訪問網絡中的不同節點,比如:通過建設銀行向招商銀行轉賬,建設銀行和招商銀行肯定用的不是同一個數據庫,同時二者的數據庫也不在一個網絡節點上,那麼當用戶跨行轉賬,就是通過分布式事務來保證數據的ACID的。
MySQL中使用事務
理論總結的再好,終歸都要通過實踐來進行理解。下面就來說說MySQL中是如何使用事務的。
在MySQL命令行的默認設置下,事務都是自動提交的,即執行SQL語句後就會馬上執行COMMIT操作。因此要顯示地開啟一個事務須使用命令BEGIN或START TRANSACTION,或者執行命令SET AUTOCOMMIT=0,用來禁止使用當前會話的自動提交。
來看看我們可以使用哪些事務控制語句。
BEGIN或START TRANSACTION;顯示地開啟一個事務;
COMMIT;也可以使用COMMIT WORK,不過二者是等價的。COMMIT會提交事務,並使已對數據庫進行的所有修改稱為永久性的;
ROLLBACK;有可以使用ROLLBACK WORK,不過二者是等價的。回滾會結束用戶的事務,並撤銷正在進行的所有未提交的修改;
SAVEPOINT identifier;SAVEPOINT允許在事務中創建一個保存點,一個事務中可以有多個SAVEPOINT;
RELEASE SAVEPOINT identifier;刪除一個事務的保存點,當沒有指定的保存點時,執行該語句會拋出一個異常;
ROLLBACK TO identifier;把事務回滾到標記點;
SET TRANSACTION;用來設置事務的隔離級別。InnoDB存儲引擎提供事務的隔離級別有READ UNCOMMITTED、READ COMMITTED、REPEATABLE READ和SERIALIZABLE。
這些不用你“管”
有的時候有些SQL語句會產生一個隱式的提交操作,即執行完成這些語句後,會有一個隱式的COMMIT操作。有以下SQL語句,不用你去“管”:
DDL語句,ALTER DATABASE、ALTER EVENT、ALTER PROCEDURE、ALTER TABLE、ALTER VIEW、CREATE TABLE、DROP TABLE、RENAME TABLE、TRUNCATE TABLE等;
修改MYSQL架構的語句,CREATE USER、DROP USER、GRANT、RENAME USER、REVOKE、SET PASSWORD;
管理語句,ANALYZE TABLE、CACHE INDEX、CHECK TABLE、LOAD INDEX INTO CACHE、OPTIMIZE TABLE、REPAIR TABLE等。
以上的這些SQL操作都是隱式的提交操作,不需要手動顯式提交。
事務的隔離級別
上面也說到了SET TRANSACTION用來設置事務的隔離級別。那事務的隔離級別是什麼東東?
在數據庫操作中,為了有效保證並發讀取數據的正確性,提出的事務隔離級別。
InnoDB存儲引擎提供事務的隔離級別有READ UNCOMMITTED、READ COMMITTED、REPEATABLE READ和SERIALIZABLE。這些隔離級別之間的區別如下:
髒讀:一個事務讀取到了另外一個事務沒有提交的數據;
比如:事務T1更新了一行記錄的內容,但是並沒有提交所做的修改。事務T2讀取到了T1更新後的行,然後T1執行回滾操作,取消了剛才所做的修改。現在T2所讀取的行就無效了;
不可重復讀:在同一事務中,兩次讀取同一數據,得到內容不同;
比如:事務T1讀取一行記錄,緊接著事務T2修改了T1剛才讀取的那一行記錄。然後T1又再次讀取這行記錄,發現與剛才讀取的結果不同。這就稱為“不可重復”讀,因為T1原來讀取的那行記錄已經發生了變化;
幻讀:同一事務中,用同樣的操作讀取兩次,得到的記錄數不相同;
比如:事務T1讀取一條指定的WHERE子句所返回的結果集。然後事務T2新插入 一行