什么是事務?
當多個用戶訪問同一份數據時,一個用戶在更改數據的過程中可能有其他用戶同時發起更改請求,為保證數據庫記錄的更新從一個一致性狀態更改為另一個一致性狀態,這樣的操作過程就是事務。事務具有的ACID屬性:
-
- 原子性(Atomicity):事務中所有的操作視為一個原子單元,即對於事務所進行的修改、刪除等操作只能是全部提交或者全部回滾。
- 一致性(Consistency):事務在完成操作后,必須使所用的數據從一種一致性狀態變為另外一種一致性狀態,所有的變更都必須應用於事務的修改,以確保數據的完整性。
- 隔離性(Isolation):一個事務中的操作語句所做的修改必須與其他事務相隔離,在進行事務查看數據時數據所處的狀態,要么是被另一個事務並發修改之前的狀態,要么是修改之后的狀態,即當前事務不會查看由另一個並發事務正在修改的數據。這種特性通過鎖機制實現。
- 持久性(Durability):事務完成之后,所做的修改對數據的影響是永久的,即使系統重啟或者是出現故障數據仍可以恢復。
MySQL中提供多種事務型存儲引擎,如InnoDB,BDB。但是MyISAM不支持事務,InnoDB支持事務、行級鎖、高並發。
InnoDB事務實現原理:undo日志和redo日志,配合MySQL自身的鎖機制
redo日志對應磁盤上MySQL安裝目錄的ib_logfileN ,
在這個目錄下,沒有對應的undo日志,當用戶rollback一個事務的時候,緩沖區中所有的數據都被放在了表空間對應的.idb數據文件中。
如圖這是某個數據庫中的 escore 表 在磁盤上對應的表空間文件,當事務回滾時,會將緩沖區的數據放在 escore.idb 中,並在適當的時候刷新到磁盤。
MySQL事務隔離級別
SQL標准定義了4種隔離級別,高級別的隔離級別事務安全,並發性能較低,占用資源較多,低級別反之。
- READ UNCOMMITTED(讀未提交內容)
- READ COMMITTED(讀已提交內容)
- REPEATABLE READ(可重復讀)
- SERIALIZABLE(串行化)
MySQL查看當前隔離級別和設置隔離級別:
-- 查看隔離級別 mysql> show variables like 'tx_isolation'; --設置隔離級別 mysql> set global transaction isolation level READ UNCOMMITTED;
READ UNCOMMITTED(讀未提交內容)
在該隔離級別中,所有事務都可以看到其他未提交事務的執行結果,然而其性能也不必其他級別的高很多,因此很少使用,讀未提交的數據稱為“臟讀”。
事務A | 事務B |
mysql> select @@tx_isolation; |
mysql> select @@tx_isolation; |
mysql> begin; mysql> select a from test; |
mysql> begin; mysql> update test set a=a*2 where a=2; |
mysql> select a from test; |
mysql> rollback; |
mysql> select a from test; |
READ COMMITTED(讀取提交內容)
一個事務從開始所做的任何改變都是不可見的,事務只能看見已近提交的事務所做的改變,但是一個事務在處理期間可能會發生其他事務修改了數據並提交,那么那么在未提交的事務中就會出現兩次讀取結果不同的現象,這就是所謂的“不可重復讀”。
事務A | 事務B |
mysql> select @@tx_isolation; |
mysql> select @@tx_isolation; |
mysql> begin; mysql> select a from test; |
mysql> begin; mysql> update test set a=a*2 where a=2; |
mysql> select a from test; |
mysql> commit; |
mysql> commit; |
REPEATABLE READ(可重復讀)
這是MySQL默認的隔離級別,能確保同一事務的多個實例在並發讀取數據時,能看到相同的數據行。但是這種隔離級別也有一個問題:假設一個事務對表中的一行數據進行修改,這種修改會導致整張表的數據都需要更新;同時第二個事務開啟,第二個事務是在這張表中插入一行數據。那么,此時就會出現第一個事務發現表中的數據還沒有被全部更新的假象,這就是“幻讀”。所幸的是在MySQL的InnoDB存儲引擎中,通過並發多版本控制的機制已經解決了這個問題。
事務A | 事務B |
mysql> select @@tx_isolation; |
mysql> select @@tx_isolation; |
mysql> begin; mysql> select a from test; |
mysql> begin; mysql> update test set a=0; |
mysql> select a from test; |
mysql> commit; |
mysql> commit; mysql> select a from test; |
(InnoDB存儲引擎 MVCC機制:InnoDB通過給每個數據行增加兩個隱含值的方式來實現。這兩個隱含值記錄了數據行的創建時間、以及過期時間、每一行存儲事件發生時的系統版本號。每次開啟一個新的事物版本號就加一,每個事物都會保存開始時的版本號,每次查詢根據事物的版本號來查詢數據。)
SERIALIZABLE(串行化)
這是最高的事務隔離級別,通過強制事務排序,使之不可能產生沖突,從而解決了幻讀問題。簡單來說就是在每個讀取的數據行加上共享鎖,這個級別上,可能會導致大量的超時現象和所競爭,一般不使用。
InnoDB鎖機制
在並發系統中,同一時刻可能有多個客戶端在對同一張表做更新操作,為了保證數據的一致性,需要對並發加以控制,因此產生了鎖。同時為實現事務隔離級別提供保證。
鎖的類型:
1. 共享鎖:代號S,Share的縮寫。粒度是行或者N行,一個事務獲取共享鎖后,可以對鎖定范圍的數據執行讀操作。
2. 排他鎖:代號X,是eXclusive的縮寫,粒度同共享鎖,一個事務獲得排他鎖后,可以對鎖定范圍執行寫操作。
3. 意向鎖:是一種表鎖,粒度為整張表。分為意向共享鎖(IS)和意向排他鎖(IX),意向共享鎖表示一個事務有意對數據上共享鎖或者排它鎖,“有意”表示事務想執行操作但是還沒有執行。鎖與鎖之間的關系,要么相容,要么互斥。相容是指:操作同樣一組數據時,如果事務A獲取了鎖a,另一個事務B還可以獲取鎖b;互斥是指:操作同一組數據時,如果事務A獲取了鎖a,另一個事務B在事務A釋放鎖a之前無法獲取鎖b。
為了提高數據庫的並發控制,每次鎖的范圍越小越好,越小耗費的資源就越多,系統性能又下降,所以為了找到在並發響應和性能的平衡點,“鎖粒度應運而生”。
鎖粒度
鎖的粒度主要分為表鎖和行鎖,
表鎖管理表的開銷最小,同時允許的並發量也是最小的鎖機制,MyISAM存儲引擎使用該鎖機制,當要寫入數據時,將整個表鎖住,此時讀、寫動作一律等待,同時一些修改表的記錄也是表鎖。
行鎖可以支持最大的並發,InnoDB存儲引擎使用該機制,因為采用行級鎖,所以可以獲得更多性能。
InnoDB 行級鎖演示
事務A | 事務B |
mysql> select @@tx_isolation; |
mysql> select @@tx_isolation; |
mysql> begin; mysql> update test set a= 1 where a=0; |
mysql> begin; mysql> update test set a= 2 where a=0; |
mysql> commit; |
mysql> update test set a= 1 where a=0; mysql> commit; |