1.?什么是事務?
數據庫中的事務是指對數據庫執行一批操作,而這些操作最終要么全部執行成功,要么全部失敗,不會存在部分成功的情況。這個時候就需要用到事務。
最經典的例子就是轉賬,你要給朋友小白轉 1000 塊錢,而此時你的銀行卡只有 1000 塊錢。
轉賬過程具體到程序里會有一系列的操作,比如查詢余額、做加減法、更新余額等,這些操作必須保證是一體的,不然等程序查完之后,還沒做減法之前,你這 1000 塊錢,完全可以借著這個時間差再查一次,然后再給另外一個朋友轉賬,如果銀行這么整,不就亂了么?這時就要用到“事務”這個概念了。
簡單來說,事務就是要保證一組數據庫操作,要么全部成功,要么全部失敗。
2.事務的幾個特性(ACID)
ACID(Atomicity、Consistency、Isolation、Durability,即原子性、一致性、隔離性、持久性)。
原子性(Atomicity)
事務的整個過程如原子操作一樣,最終要么全部成功,或者全部失敗,這個原子性是從最終結果來看的,從最終結果來看這個過程是不可分割的。
一致性(Consistency)
一個事務必須使數據庫從一個一致性狀態變換到另一個一致性狀態。
所謂一致性,指的是數據處于一種有意義的狀態,這種狀態是語義上的而不是語法上的。最常見的例子是轉帳。例如從帳戶A轉一筆錢到帳戶B上,如果帳戶A上的錢減少了,而帳戶B上的錢卻沒有增加,那么我們認為此時數據處于不一致的狀態。
隔離性(Isolation)
一個事務的執行不能被其他事務干擾。即一個事務內部的操作及使用的數據對并發的其他事務是隔離的,并發執行的各個事務之間不能互相干擾。
持久性(Durability)
一個事務一旦提交,他對數據庫中數據的改變就應該是永久性的。當事務提交之后,數據會持久化到硬盤,修改是永久性的。
3.如何開啟事務
?事務分為隱式事務和顯式事務。
隱式事務
mysql中事務默認是隱式事務,執行insert、update、delete操作的時候,數據庫自動開啟事務、提交或回滾事務。
是否開啟隱式事務是由變量autocommit控制的。
mysql> show variables like 'autocommit';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| autocommit | ON |
+---------------+-------+
1 row in set (0.01 sec)--autocommit為ON表示開啟了自動提交
顯式事務
- 顯式啟動事務語句, begin 或 start transaction。配套的提交語句是 commit,回滾語句是 rollback。
- set autocommit=0,這個命令會將這個線程的自動提交關掉。意味著如果你只執行一個 select 語句,這個事務就啟動了,而且并不會自動提交。這個事務持續存在直到你主動執行 commit 或 rollback 語句,或者斷開連接。
?建議你總是使用 set autocommit=1, 通過顯式語句的方式來啟動事務。
mysql> begin;
Query OK, 0 rows affected (0.00 sec)mysql> update test set name='sss' where id=7;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0mysql> commit;
Query OK, 0 rows affected (0.01 sec)
4.隔離級別?
當數據庫上有多個事務同時執行的時候,就可能出現臟讀(dirty read)、不可重復讀(non-repeatable read)、幻讀(phantom read)的問題。
(1)臟讀:一個事務讀到另外一個事務沒有提交的數據
解釋:例如兩個并發的事務a和b,其中事務A查完數據庫中的一條記錄后,事務a繼續修改了一條記錄,此時事務A并未提交,此時并發事務b讀取了事務a修改的記錄,這就導致事務b讀取到事務a未提交的數據
(2)不可重復讀:一個事務先后讀取同一條記錄,但是兩次讀取的數據不同
解釋:例如兩個并發事務a和b,其中事務a讀取數據庫中的一條記錄后,事務b對數據庫的這條記錄進行修改后,提交事務b,此時事務a繼續讀取這條記錄,發現和上次讀取的數據不一樣
(3)幻讀:一個事務按照條件查詢數據,沒有對應的數據行,準備插入數據時,發現這行數據存在
解釋:例如兩個并發事務a和事務b,其中事務a讀取了id=1的數據時,發現沒有這條記錄,然后事務b插入id=1的數據,并且提交了事務b,此時事務a準備插入id=1的數據時發現已經存在這條數據,因為插入數據時報錯,顯示有這條記錄
不可重復讀,關注是其他事務修改數據并提交了事務,前后兩次讀取到的數據不一致的問題;幻讀是新插入的行或刪除行導致出現的問題。
解決不可重復讀的問題只需鎖住滿足條件的行,解決幻讀需要鎖表。
為了解決這些問題,就有了“隔離級別”的概念。?
mysql中一共有4中隔離級別,其中可重復讀是默認級別。
- 讀未提交是指,一個事務還沒提交時,它做的變更就能被別的事務看到。
- 讀提交是指,一個事務提交之后,它做的變更才會被其他事務看到。
- 可重復讀是指,一個事務執行過程中看到的數據,總是跟這個事務在啟動時看到的數據是一致的。當然在可重復讀隔離級別下,未提交變更對其他事務也是不可見的。
- 串行化,顧名思義是對于同一行記錄,“寫”會加“寫鎖”,“讀”會加“讀鎖”。當出現讀寫鎖沖突的時候,后訪問的事務必須等前一個事務執行完成,才能繼續執行。
?上面4中隔離級別越來越強,會導致數據庫的并發性也越來越低。
--查看隔離級別(兩條任一條都行,這是查詢當前會話的)
show variables like 'transaction_isolation';
select @@transaction_isolation;--查看全局隔離級別
SELECT @@GLOBAL.TRANSACTION_ISOLATION;--設置隔離級別
SET [ SESSION | GLOBAL ] TRANSACTION ISOLATION LEVEL { READ UNCOMMITTED |
READ COMMITTED | REPEATABLE READ | SERIALIZABLE }--MySQL的session和global一般使用在終端,用來對配置進行暫時設置,當數據庫服務重啟就會失效。session和global體現在新的設置生效的范圍。
--session:當前會話,也就是當前連接立即生效。
--global:全局,不包含當前連接,之后新獲取的連接都會生效。
5.隔離級別的演示
?每個隔離級別可能出現的問題如下?
?讀未提交級別
出現臟讀
?讀已提交級別
解決臟讀,出現不可重復讀問題。
步驟5更新數據后還未提交,到步驟6查看,數據沒有改變,說明讀已提交解決了臟讀問題。
而步驟6和步驟8查看的數據是不一致的,這個就出現了不可重復讀問題。(在同一個事務先后讀取同一條記錄,但是兩次讀取的數據不同)。
?可重復讀級別
解決不可重復讀問題,出現幻讀。
解決了不可重復讀問題,步驟3,6,8讀到的數據都是一致的。在步驟9提交后,步驟10讀到的數據就是更新后的數據。
出現幻讀。
步驟3查看是沒有id=7的行,步驟4中插入id=7的行(步驟4是隱性提交),步驟5中再次查看還是沒有。之后步驟7插入數據,出現錯誤。這就是出現了幻讀。幻讀是針對新插入的行的。
步驟8提交后,步驟9中查看可以看到另一事務插入的id=7的數據了。
可串行化級別
解決所有問題,但也是性能最差的一個。
步驟5中會一直被卡住,等到超時。