照例,咱們先來一個場景~mysql
面試官:"知道事務的四大特性麼?"
你:"懂,ACID嘛,原子性(Atomicity)、一致性(Consistency)、隔離性(Isolation)、持久性(Durability)!"
面試官:"大家是用mysql數據庫吧,能簡單說說innodb中怎麼實現這四大特性的麼?「
你:"我只知道隔離性是怎麼作的balabala~~"
面試官:"仍是回去等通知吧~"面試
OK,回到正題。說到事務的四大特性原子性(Atomicity
)、一致性(Consistency
)、隔離性(Isolation
)、持久性(Durability
),懂的人不少。可是稍微涉及細節一點,這四大特性在數據庫中的實現原理是怎麼樣的?那就沒有幾我的可以答得上來了。所以,咱們這篇文章着重討論一下四大特性在Mysql中的實現原理。sql
咱們以從A帳戶轉帳50元到B帳戶爲例進行說明一下ACID,四大特性。數據庫
根據定義,原子性是指一個事務是一個不可分割的工做單位,其中的操做要麼都作,要麼都不作。即要麼轉帳成功,要麼轉帳失敗,是不存在中間的狀態!
若是沒法保證原子性會怎麼樣?
OK,就會出現數據不一致的情形,A帳戶減去50元,而B帳戶增長50元操做失敗。系統將無端丟失50元~併發
根據定義,隔離性是指多個事務併發執行的時候,事務內部的操做與其餘事務是隔離的,併發執行的各個事務之間不能互相干擾。
若是沒法保證隔離性會怎麼樣?
OK,假設A帳戶有200元,B帳戶0元。A帳戶往B帳戶轉帳兩次,金額爲50元,分別在兩個事務中執行。若是沒法保證隔離性,會出現下面的情形
日誌
如圖所示,若是不保證隔離性,A扣款兩次,而B只加款一次,憑空消失了50元,依然出現了數據不一致的情形!code
ps
:可能有細心的讀者已經發現了,mysql中是依靠鎖來解決隔離性問題。嗯,咱們後面來講明。blog
根據定義,持久性是指事務一旦提交,它對數據庫的改變就應該是永久性的。接下來的其餘操做或故障不該該對其有任何影響。事務
若是沒法保證持久性會怎麼樣?
在Mysql中,爲了解決CPU和磁盤速度不一致問題,Mysql是將磁盤上的數據加載到內存,對內存進行操做,而後再回寫磁盤。好,假設此時宕機了,在內存中修改的數據所有丟失了,持久性就沒法保證。內存
設想一下,系統提示你轉帳成功。可是你發現金額沒有發生任何改變,此時數據出現了不合法的數據狀態,咱們將這種狀態認爲是數據不一致的情形。
根據定義,一致性是指事務執行先後,數據處於一種合法的狀態,這種狀態是語義上的而不是語法上的。
那什麼是合法的數據狀態呢?
oK,這個狀態是知足預約的約束就叫作合法的狀態,再通俗一點,這狀態是由你本身來定義的。知足這個狀態,數據就是一致的,不知足這個狀態,數據就是不一致的!
若是沒法保證一致性會怎麼樣?
例一:A帳戶有200元,轉帳300元出去,此時A帳戶餘額爲-100元。你天然就發現了此時數據是不一致的,爲何呢?由於你定義了一個狀態,餘額這列必須大於0。
例二:A帳戶200元,轉帳50元給B帳戶,A帳戶的錢扣了,可是B帳戶由於各類意外,餘額並無增長。你也知道此時數據是不一致的,爲何呢?由於你定義了一個狀態,要求A+B的餘額必須不變。
問題一:Mysql怎麼保證一致性的?
OK,這個問題分爲兩個層面來講。
從數據庫層面,數據庫經過原子性、隔離性、持久性來保證一致性。也就是說ACID四大特性之中,C(一致性)是目的,A(原子性)、I(隔離性)、D(持久性)是手段,是爲了保證一致性,數據庫提供的手段。數據庫必需要實現AID三大特性,纔有可能實現一致性。例如,原子性沒法保證,顯然一致性也沒法保證。
可是,若是你在事務裏故意寫出違反約束的代碼,一致性仍是沒法保證的。例如,你在轉帳的例子中,你的代碼裏故意不給B帳戶加錢,那一致性仍是沒法保證。所以,還必須從應用層角度考慮。
從應用層面,經過代碼判斷數據庫數據是否有效,而後決定回滾仍是提交數據!
問題二: Mysql怎麼保證原子性的?
OK,是利用Innodb的undo log
。
undo log
名爲回滾日誌,是實現原子性的關鍵,當事務回滾時可以撤銷全部已經成功執行的sql語句,他須要記錄你要回滾的相應日誌信息。
例如
undo log
記錄了這些回滾須要的信息,當事務執行失敗或調用了rollback,致使事務須要回滾,即可以利用undo log中的信息將數據回滾到修改以前的樣子。
ps
:具體的undo log日誌長啥樣,這個能夠寫一篇文章了。並且寫出來,看的人也很少,姑且先這麼簡單的理解吧。
問題三: Mysql怎麼保證持久性的?
OK,是利用Innodb的redo log
。
正如以前說的,Mysql是先把磁盤上的數據加載到內存中,在內存中對數據進行修改,再刷回磁盤上。若是此時忽然宕機,內存中的數據就會丟失。
怎麼解決這個問題?
簡單啊,事務提交前直接把數據寫入磁盤就行啊。
這麼作有什麼問題?
因而,決定採用redo log
解決上面的問題。當作數據修改的時候,不只在內存中操做,還會在redo log
中記錄此次操做。當事務提交的時候,會將redo log
日誌進行刷盤(redo log
一部分在內存中,一部分在磁盤上)。當數據庫宕機重啓的時候,會將redo log
中的內容恢復到數據庫中,再根據undo log
和binlog
內容決定回滾數據仍是提交數據。
採用redo log的好處?
其實好處就是將redo log
進行刷盤比對數據頁刷盤效率高,具體表現以下
redo log
體積小,畢竟只記錄了哪一頁修改了啥,所以體積小,刷盤快。redo log
是一直往末尾進行追加,屬於順序IO。效率顯然比隨機IO來的快。ps
:不想具體去談redo log
具體長什麼樣,由於內容太多了。
問題四: Mysql怎麼保證隔離性的?
OK,利用的是鎖和MVCC機制。仍是拿轉帳例子來講明,有一個帳戶表以下
表名t_balance
id | user_id | balance |
---|---|---|
1 | A | 200 |
2 | B | 0 |
其中id是主鍵,user_id爲帳戶名,balance爲餘額。仍是以轉帳兩次爲例,以下圖所示
至於MVCC,即多版本併發控制(Multi Version Concurrency Control),一個行記錄數據有多個版本對快照數據,這些快照數據在undo log
中。
若是一個事務讀取的行正在作DELELE或者UPDATE操做,讀取操做不會等行上的鎖釋放,而是讀取該行的快照版本。
因爲MVCC機制在可重複讀(Repeateable Read)和讀已提交(Read Commited)的MVCC表現形式不一樣,就不贅述了。
可是有一點說明一下,在事務隔離級別爲讀已提交(Read Commited)時,一個事務可以讀到另外一個事務已經提交的數據,是不知足隔離性的。可是當事務隔離級別爲可重複讀(Repeateable Read)中,是知足隔離性的。
本文講了Mysql中事務ACID四大特性的實現原理,但願你們有所收穫。