MySQL事務相關
一、 什么是事務
事務(Transaction)是一個邏輯序列,該序列要么執行,要么不執行,
例如:轉換業務
A 給 B 轉賬 1000 元,設計兩個操作:
- A減少1000元
- B增加1000元
如果在這兩個操作中間,發現了意料不到的事件,如斷電,系統崩潰等,這樣轉賬并沒有完成,
事務就是讓上述這組操作要么全部成功,要么全部不成功,即所有步驟完成,提交事務(commit),完成轉賬;其中一個步驟失敗,回滾事務(rollback),撤銷到事務開始時的所有操作,
二、 事務的四大特性(ACID)
事務具有四大特性,即:
- 原子性(atomicity)
- 一致性(consitstency)
- 隔離性(isolation)
- 持久性(durability)
1. 原子性
原子性:終止出錯的事務,并撤銷該事務進行的所有變更,
當一個事務在執行程序中發生錯誤,我們往往很難知道哪些更改已生效,哪些沒有生效,如果進行重試可能造成數
據重復或資料錯誤,為了簡化問題,資料庫丟棄或撤銷迄今為止的所有寫入,即安全的重試,
事務是最小執行單位,不可分割,原子性保證操作要么都完成,要么都不起作用,
2. 一致性
一致性:執行事務前后,資料保持一致,多個事務對同一個資料讀取的結果是相同的,
例如:A 轉賬給 B 1000 元,轉賬后,A賬戶必須減少1000,B賬戶必須增加1000.
這并不是資料庫能保證的,而應該是應用程式來保證,
原子性,隔離性、持久性都是資料庫的屬性,而一致性是應用程式的屬性,應用程式可以用原子性和隔離性來實作
一致性,所以ACID中的C就是用來湊縮寫單詞的,
3. 隔離性
隔離性:并發訪問資料庫時,一個用戶的事務不被其他事務干擾,各并發事務之間的資料庫是獨立的,
例如:兩個客戶端同時增長一個計數器(當前值為10),每個客戶端需要讀取當前計數器的值,加 1后再回寫新值,
如果兩次增長,計數器應為 10 --> 12 ,當由于兩個客戶端同時取得10,加1 后回寫,計數器最終值為11.資料錯誤,
隔離性與并發編程原子性的聯系:從上面的例子可以看到,當采用并發編程中的原子性的自增操作,就不會發生錯誤,多執行緒中的原子性,對應的就是事務的隔離性,
4. 持久性
持久性:一個事務被提交之后,它對資料庫中資料的改變是持久的,及時資料庫發生故障也不應該對其有任何影響,
當發生硬碟故障或崩潰時,資料庫的預寫日志或分布式系統的復制都可以為持久性做出承諾,當并不能提供絕對的保證,
三、 并發事務帶來的問題
在應用程式中,多個事務并發運行,經常會操作相同的資料完成各自的任務,而可能造成一些問題,如:
- 臟讀(Dirty read)、臟寫(Dirty wirte)
- 丟失修改(Lost to modify)
- 不可重復讀(Unrepeatableread)
- 幻讀(Phantom read)
1. 臟讀、臟寫
臟讀:指一個事務已經將資料寫入資料庫,但未提交,另一個事務訪問了這些資料,為臟讀,這些資料是臟資料,
臟寫:指兩個事務更新資料庫相同的資料,先前事務已經寫入,未提交;后面事務覆寫尚未提交的資料,為臟寫,(正常邏輯為:先前事務寫入,提交;后面事務寫入,覆寫),
使用 讀已提交(讀取已經提交的資料)這種隔離級別解決,只提供兩個保證,不臟讀,不臟寫,
2. 丟失修改
丟失修改:一個事務訪問某個資料,另一個事務也訪問該資料,第一個事務修改資料后,第二個事務也修改該資料,這樣第一個事務修改的結果被丟失,
例如:
事務1讀取某表資料 A = 20 ,事務2也讀取該資料為 A = 20
事務1:A = A - 1; 事務2: A = A - 1;
結果:A = 19,事務1結果被丟棄,(正常結果應為 A = 18)
各種解決方案:
①原子寫:
UPDATE counter SET value = https://www.cnblogs.com/wenhuohuo/p/value + 1 WHERE id ='whatever';
②for update 顯示鎖定
BEGIN TRANSACTION;
# for update就是為這行資料加了鎖,提交或回滾后釋放
SELECT * FROM users WHERE id = 'Eddie' FOR UPDATE;
# 拿到資料后,應用程式做校驗,然后...
UPDATE users SET money = '99999999' WHERE id = 'Eddie';
COMMIT;
③比較并設定(CAS,CompareAndSet)
UPDATE wiki_pages SET content = '新內容' WHERE id = '007' AND content = '舊內容';
# 根據資料庫的實作,這可能也不安全
# 如果資料庫允許WHERE子句從舊快照中讀取,則此陳述句也無法保證防止丟失更新
3. 不可重復讀
指一個事務內多次讀取同一資料,該事務還沒結束時,另一個事務也訪問該資料,并在第一個事務的兩次讀取中間修改了該資料,導致第一個事務兩次讀到的資料不一致的情況,

4. 幻讀
與不可重復讀類似,
指 事務1讀取了幾行資料,接著另一個事務插入了一些資料,在隨后的查詢中,事務1就會發現多出了一些原本不存在的記錄,如幻覺,稱為幻讀,
即一個事務的寫入改變了另一個事務查詢結果的正確性,稱為幻讀,
不可重復讀與幻讀的區別:
- 前者重點在于修改;
- 后者重點在于新增或洗掉,
四、 事務的隔離級別
SQL標準定義了4個隔離級別:
- 讀未提交(READ-UNCOMMITTED)
- 讀已提交(READ-COMMITTED)
- 可重復讀(REPEATABLE-READ)
- 可串行化(SERIALIZABLE)
1. 讀未提交
最低的隔離級別,允許讀取尚未提交的資料變更,可能導致臟讀、臟寫、幻讀、不可重復讀,
2. 讀已提交
允許讀取并發事務已經提交的資料,可以阻止臟讀、臟寫,但幻讀,讀已提交仍有可能發生,
總結讀已提交:
- 從資料庫讀取時,只能看到已提交的資料(不臟讀),
- 寫入資料庫時,只會覆寫已經提交的寫入資料(不臟寫),
為了防止臟讀,每次寫入前,資料庫都會記住舊值, 當前事務尚未提交時,其他事務的讀取都會拿到舊值,當前
事務提交后,其他事務才能讀取到新值,
為了防止臟寫,資料庫一般用行鎖,當事務想要修改特定的行時,必須先獲得該行的鎖,一次只有一個事務可持有
任何給定行的鎖,如果另一個事務要寫入同一行,就必須等到第一個事務提交或回滾后,
3. 可重復讀
對同一欄位的多次讀取結果都是一致的,除非資料是被本身事務自己所修改,可以阻止臟讀和不可重復讀,但幻讀
仍有可能發生,
4. 可串行化
最高的隔離級別,完全服從ACID的隔離級別,所有的事務依次逐個執行,事務之間完全不可能產生干擾,可阻止 臟讀臟寫、幻讀、不可重復讀,
| 隔離級別 | 臟讀 | 不可重復讀 | 幻讀 |
|---|---|---|---|
| 讀未提交 | × | × | × |
| 讀已提交 | √ | × | × |
| 可重復讀 | √ | √ | × |
| 可串行化 | √ | √ | √ |
五、 默認隔離級別
MySQL InnoDB 存盤引擎的默認隔離級別為:可重復讀(REPEATABLE-READ),
通過 SELECT @@tx-isolation; 命令查看:
mysql> SELECT @@tx_isolation;
+-----------------+
| @@tx_isolation |
+-----------------+
| REPEATABLE-READ |
+-----------------+
這里需要注意的是:與 SQL 標準不同的地方在于InnoDB 存盤引擎在 REPEATABLE-READ(可重讀)事務隔離級
別下使用的是Next-Key Lock 鎖演算法,因此可以避免幻讀的產生,這與其他資料庫系統(如 SQL Server)是不同的,
所以說InnoDB 存盤引擎的默認支持的隔離級別是 REPEATABLE-READ(可重讀) 已經可以完全保證事務的隔離
性要求,即達到了 SQL標準的SERIALIZABLE(可串行化)隔離級別,
因為隔離級別越低,事務請求的鎖越少,所以大部分資料庫系統的隔離級別都是READ-COMMITTED(讀已提
交):,但是要知道的是InnoDB 存盤引擎默認使用 REPEATABLE-READ(可重讀)并不會有任何性能損失,
InnoDB 存盤引擎在 分布式事務 的情況下一般會用到SERIALIZABLE(可串行化)隔離級別,
參考
通俗易懂 事務、ACID、臟讀、臟寫、幻讀、讀已提交、快照隔離、讀寫鎖、兩階段鎖定 的區別與聯系
可能是全網最好的MySQL重要知識點 | 面試必備
轉載請註明出處,本文鏈接:https://www.uj5u.com/shujuku/173204.html
標籤:MySQL
上一篇:mysql的各種join連接
下一篇:mysql索引
