MySQL(五)MySQL事务
事務是數據庫管理系統(DBMS)執行過程中的一個邏輯單位,由一個有限的數據庫操作序列構成。
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??------維基百科的定義
MySQL中 InnoDB支持事務,這個也是它成為默認的存儲引擎的一個重要原因,對于需要事務支持的業務場景有更好的適用性
數據庫事務
事務的四大特性
事務的四大特性:ACID
1.原子性,Atomicity ?原子(atom)是化學反應中不可再分的基本微粒,顧名思義,這也就說明對數據庫的一系列的操作,是不可分割的整體,要么都是成功,要么都是失敗,不可能出現部分成功或者部分失敗的情況。
原子性,在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,如果在數據刷盤之前出現異常,在重啟后就可以讀取redo log的內容,寫入到磁盤,保證數據的持久性。當然,恢復成功的前提是數據頁本身沒有被破壞,是完整的,這個通過雙寫緩沖(double write)保證。
事務的開啟方式
事務開啟分為兩種方式,手動開啟提交和自動開啟提交。
我們平時寫一個SQL:update user set ?name='chenpp' where id=1;這里就已經自動開啟了一個事務,并且提交了。
InnoDB里面有一個autocommit的參數(分成兩個級別, session級別和global級別),可以通過set session autocommit = on/off; -- 設定事務是否自動開啟?。平時是默認開啟的
手動開啟事務也有兩種方式,一種是使用begin;另一種是用start transaction。
結束事務也有兩種方式,提交一個事務commit;或者回滾 rollback
還有一種情況,客戶端的連接斷開的時候,事務也會結束。
事務并發會造成的問題
當多個事務并發操作數據庫的表或者行的時候,如果沒有事務的Isolation隔離性,會帶來哪些問題呢?
事務并發的三大問題:臟讀,不可重復讀,幻讀
臟讀
這種讀取到其他事務未提交的數據的情況,我們把它叫做臟讀。
不可重復讀
這種一個事務讀取到了其他事務已提交的數據導致前后兩次讀取數據不一致的情況,我們把它叫做不可重復讀。
幻讀
由于其他事務插入數據造成一個事務前后兩次讀取數據數據不一致,這種情況我們把它叫做幻讀。
不可重復讀和幻讀的區別在于 不可重復讀是由于修改或者刪除導致,幻讀是由于插入導致的
無論是臟讀,還是不可重復讀,幻讀,都是數據庫的讀一致性的問題,都是在一個事務里面前后兩次讀取出現了不一致的情況。
讀一致性的問題,必須要由數據庫提供一定的事務隔離機制來解決
?SQL92 標準
http://www.contrib.andrew.cmu.edu/~shadow/sql/sql1992.txt
Possible代表在這個隔離級別下,這個問題有可能發生
第一個隔離級別:Read Uncommitted(未提交讀),一個事務可以讀取到其他事務未提交的數據,可能出現臟讀,它沒有解決任何的問題。
第二個隔離級別:Read Committed(已提交讀),一個事務只能讀取到其他事務已提交的數據,不能讀取到其他事務未提交的數據,它解決了臟讀的問題,但是可能會出現不可重復讀的問題。
第三個隔離級別:Repeatable Read (可重復讀),它解決了不可重復讀的問題,就是說在同一個事務里面多次執行同樣的SQ其返回的數據結果是一樣的,但是在這個級別下,沒有解決幻讀的問題。
第四個隔離級別:Serializable(串行化),在這個隔離級別里面,所有的事務都是串行執行的,也就不存在事務的并發操作問題了,所以它解決了所有的問題。
MySQL InnoDB 對隔離級別的支持
在MySQL InnoDB里面,不需要使用串行化的隔離級別去解決所有問題。
看下MySQL InnoDB里面對數據庫事務隔離級別的支持程度是什么樣的
注意一下,InnoDB在Repeatable Read(RR)的級別就解決了幻讀的問題。這個也是InnoDB默認使用RR作為事務隔離級別的原因,既保證了數據的一致性,又支持較高的并發度。
解決讀一致性的實現方案
LBCC
要保證前后兩次讀取數據一致,那么直接在讀取數據的時候,鎖定讀取的數據,不允許其他的事務修改就行了。這種方案我們叫做基于鎖的并發控制Lock Based Concurrency Control(LBCC)。
如果僅僅是基于鎖來實現事務隔離,一個事務讀取的時候是不允許其他事務修改的,那就意味著不支持并發的讀寫操作,這樣會極大地影響操作數據的效率,畢竟我們的大多數應用都是讀多寫少的
MVCC
另一種解決方就是使用快照,就是在修改數據的時候給它建立一個備份或者快照,后面在本事務里再次執行的時候只要讀取這個快照就行了。這種方案我們叫做多版本的并發控制 Multi Version Concurrency Control(MVCC)。MVCC使得InnoDB的事務隔離級別下執行一致性讀操作有了保證。簡單說就是為了查詢一些正在被另一個事務更新的行,并且可以看到它們被更新之前的值
MVCC的核心思想是: 我可以查到在我這個事務開始之前已經存在的數據,即使它在后面被修改或者刪除了。而在我這個事務開始之后新增的數據,我是查不到的。
為了實現MVCC , InnoDB為每行記錄都實現了兩個隱藏字段:
DB_TRX_ID,6字節:插入或更新該行的最后一個事務的事務ID,事務編號是自動遞增的(可以理解為類似創建版本號)
DB_ROLL_PTR,7字節:回滾指針(可以理解為刪除版本號,當數據被刪除或記錄為舊數據的時候,當前的事務ID)。
看下MVCC在不同場景下的使用:
1)新增數據
事務1插入了2條數據并提交,事務2此時查詢了當前表,可以看到2條記錄,此時事務3又插入了一條新的記錄,之后事務2又查詢了一次
MVCC的查找規則:只能查找創建時間小于等于當前事務ID的數據,和刪除時間大于當前事務ID的行(或未刪除)。
也就是說不能查到在 本次事務開始之后插入的數據,Bob的創建事務ID大于2,所以還是只能查到兩條記錄。
2)刪除數據
事務3刪除了id=1的數據,事務2此時又查詢了當前表
Bob的創建版本大于2不滿足,chenpp的刪除版本大于2仍舊滿足,所以查詢到的還是id=1和id=2兩條記錄
3)修改數據
此時事務5執行了一個更新SQL update user set name='Mon' where id=2; ,之后事務2又查詢了一次
Mon,Bob創建版本大于2不滿足,CoCo刪除版本大于2仍舊滿足,所以還是2條記錄
在InnoDB中,MVCC是通過Undo log實現的。
快照讀和當前讀
- 快照讀:讀取的是快照版本,也就是歷史版本
- 當前讀:讀取的是最新版版
在InnoDB中,MVCC和鎖是協同使用的:在RR隔離級別下,普通的select使用快照讀(snapshot read),底層使用MVCC來實現。
加鎖的 select(select ... in share mode / select ... for update)以及更新操作update, delete 等語句使用當前讀(current read),底層基于鎖實現(LBCC)。
總結
以上是生活随笔為你收集整理的MySQL(五)MySQL事务的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: MySQL(四)索引的使用
- 下一篇: MySQL(六)InnoDB锁详解