一、什麼是數據庫的事務?
1.1 事務的定義
維基百科的定義:
事務是數據庫管理系統(DBMS)執行過程中的一個邏輯單位,
由一個有限的數據庫操作序列構成。
這裏面有兩個關鍵點,第一個,它是數據庫最小的工作單元,是不可以再分的。
第二個,它可能包含了一個或者一系列的 DML 語句,包括 insert delete update。
(單條 DDL(create drop)和 DCL(grant revoke)也會有事務)
1.2 事務的典型場景
在項目裏面,什麼地方會開啓事務,或者配置了事務?
無論是在方法上加註解,還是配置切面。
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="save*" rollback-for="Throwable" />
<tx:method name="add*" rollback-for="Throwable" />
<tx:method name="send*" rollback-for="Throwable" />
<tx:method name="insert*" rollback-for="Throwable" />
</tx:attributes>
</tx:advice>
比如下單,會操作訂單表,資金錶,物流表等等,這個時候我們需要讓這些操作都在一個事務裏面完成。
當一個業務流程涉及多個表的操作的時候,我們希望它們要麼是全部成功的,要麼都不成功,這個時候我們會啓用事務。
1.3 哪些存儲引擎支持事務?
InnoDB 支持事務,這個也是它成爲默認的存儲引擎的一個重要原因:
https://dev.mysql.com/doc/refman/5.7/en/storage-engines.html
另一個是 NDB。
1.4 事務的四大特性
事務的四大特性:ACID
1)原子性(Atomicity)
指我們對數據庫一系列的操作,要麼都是成功,要麼都是失敗,不可能有部分成功或部分失敗的情況。
例如轉賬,一個賬戶餘額減少,對應一個賬戶增加,這兩個賬戶一定是同時成功或者同時失敗的。
如果前面一個操作已經成功了,後面的操作失敗了,怎麼讓它全部失敗呢?這個時候我們必須要回滾。
原子性,在InnoDB中是通過undo log來實現的,它記錄數據修改前的值,一旦異常,就通過undo log來實現回滾操作。
2)一致性(consistent)
一致性指的是數據庫的完整性約束沒有被破壞,事務執行的前後都是合法的數據狀態。
比如主鍵必須是唯一的,字段長度符合要求。
除了數據庫自身的完整性約束,還有一個是用戶自定義的完整性:
例如轉賬,A 賬戶餘額減少 1000,B 賬戶餘額只增加了 500,
這個時候因爲兩個操作都成功了,按照我們對原子性的定義,它是滿足原子性的,
但是它沒有滿足一致性,因爲它導致了會計科目的不平衡。
用戶自定義的完整性通常要在代碼中控制。
3)隔離性(Isolation)
多個併發事物同時操作同一張表或同一行數據,必然會引發一些併發和干擾的問題。
隔離性就是指多個事物對錶或行的併發操作,應該是透明的互不干擾的。
通過這種規則,也是爲了保證數據的一致性。
4)持久性(Durable)
我們對數據庫的任意的操作,增刪改,只要事務提交成功,那麼結果就是永久性的。
不可能因爲我們系統宕機或者重啓了數據庫的服務器,它又恢復到原來的狀態了。
持久性以及數據庫崩潰恢復(crash-safe)是通過什麼實現的?
持久性是通過redo log 和 double write 來實現的,操作數據時,
會先寫到內存的buffer pool中,同時記錄redo log,如果在刷入磁盤前出現異常,
在重啓後就可以讀取redolog中的內容,寫入磁盤,保證數據的持久性。
但是恢復成功的前提是數據頁本身沒有被破壞,是完整的,這個通過雙寫緩衝(double write)保證。
原子性、隔離性、持久性最終目的都是爲了實現一致性。
1.5 數據庫事物的開啓
無論是在 Navicat 的這種工具裏面去操作,還是在我們的 Java 代碼裏面通過API 去操作,
還是加上@Transactional 的註解或者 AOP 配置,其實最終都是發送一個指令到數據庫去執行,
Java 的 JDBC 只不過是把這些命令封裝起來了。
操作環境爲版本(5.7),存儲引擎(InnnoDB),事務隔離級別(RR)。
select version();
show variables like '%engine%';
show global variables like "tx_isolation";
執行這樣一條更新語句的時候,它有事務嗎?
update user set name = 'admin2' where id=2;
實際上,它自動開啓了一個事物,並且提交了,所以最終寫入了磁盤。
這個是開啓事務的第一種方式,自動開啓和自動提交。
InnoDB 裏有一個 autocommit 的參數(分成兩個級別, session 級別和 global級別)。
show variables like 'autocommit';
它的默認值是 ON。autocommit 代表是否自動提交。如果它的值是 true/on 的話,
在操作數據的時候,會自動開啓一個事務,和自動提交事務。
否則,如果把 autocommit 設置成 false/off,那麼數據庫的事務就需要手動地開啓和手動地結束。
手動開啓事務也有幾種方式,一種是用 begin;一種是用 start transaction。
怎麼結束一個事務呢?我們結束也有兩種方式,第一種就是提交一個事務, commit;
還有一種就是 rollback,回滾的時候,事務也會結束。
還有一種情況,客戶端的連接斷開的時候,事務也會結束。
1.6 事物併發會帶來什麼問題?
假如沒有隔離性,併發事物會產生什麼問題呢?
1)髒讀
如圖,有A,B兩個事物,A事物讀取一條記錄name值是Jack,B事物修改這條數據,把name修改成Rose,沒有提交。
此時A事物又進行了一次查詢,查到這條記錄中name變成了Rose。
這種在一個事物中,前後兩次讀到的數據不一致,讀到了其他事物沒有提交的數據的情況,就被稱爲髒讀。
2)不可重複讀
A,B兩個事務,A事務讀取到的name是Jack,B事務修改後進行了提交,A事務再次讀取到的name是Rose。
由於A事務再次讀取時,讀到了其他事務提交後的數據,讀取到了兩個不一致的數據。name到底時Jack還是Rose呢?
這種一個事務讀取到了其他事務已提交的數據導致前後兩次讀取數據不一致的情況,被稱爲不可重複讀。
3)幻讀
在A事物中進行條件查詢,滿足條件的數據只有1條,在B事物中插入一條數據,並且提交。
在A事物再次查詢時發現多了一條數據。
一個事務前後兩次讀取數據數據不一致,是由於其他事務插入數據造成的,這種情況我們把它叫做幻讀。
不可重複讀和幻讀的區別是什麼?
不可重複讀是修改或者刪除,幻讀是插入。
注意:只有insert操作導致的數據不一致纔是幻讀,修改或刪除則是不可重複讀。