???????事務(wù)是由一組SQL語句組成的邏輯處理單元,事務(wù)具有以下4個(gè)屬性,通常簡(jiǎn)稱為事務(wù)的ACID屬性。 原子性(Atomicity)?:事務(wù)是一個(gè)原子操作單元,其對(duì)數(shù)據(jù)的修改,要么全都執(zhí)行,要么全都不執(zhí)行。 一致性(Consistent)?:在事務(wù)開始和完成時(shí),數(shù)據(jù)都必須保持一致狀態(tài)。這意味著所有相關(guān)的數(shù)據(jù)規(guī)則都必須應(yīng)用于事務(wù)的修改,以保持?jǐn)?shù)據(jù)的完整性;事務(wù)結(jié)束時(shí),所有的內(nèi)部數(shù)據(jù)結(jié)構(gòu)(如B樹索引或雙向鏈表)也都必須是正確的。 隔離性(Isolation)?:數(shù)據(jù)庫系統(tǒng)提供一定的隔離機(jī)制,保證事務(wù)在不受外部并發(fā)操作影響的“獨(dú)立”環(huán)境執(zhí)行。這意味著事務(wù)處理過程中的中間狀態(tài)對(duì)外部是不可見的,反之亦然。 持久性(Durable)?:事務(wù)完成之后,它對(duì)于數(shù)據(jù)的修改是永久性的,即使出現(xiàn)系統(tǒng)故障也能夠保持。日志系統(tǒng)主要有Redo Log(重做日志)、Undo Log和binlog(歸檔日志)。Redo Log是InnoDB存儲(chǔ)引擎層的日志,binlog是MySQL Server層記錄的日志, 兩者都是記錄了某些操作的日志(不是所有),自然有些重復(fù)(但兩者記錄的格式不同)
? ? ? ?通過上面我們可以知道,事務(wù)的特點(diǎn)為:原子性、持久性、隔離性、一致性,是什么機(jī)制才能保證事務(wù)的這四個(gè)特性呢?
事務(wù)的原子性是通過undo log來實(shí)現(xiàn)的
事務(wù)的持久性是通過redo??log來實(shí)現(xiàn)的
事務(wù)的隔離性是通過(讀寫鎖+MVCC)來實(shí)現(xiàn)的
事務(wù)的一致性是通過原子性、持久性、隔離性來實(shí)現(xiàn)的
2.1.1、原子性實(shí)現(xiàn)原理---Undo Log???????●?Undo Log是為了實(shí)現(xiàn)事務(wù)的原子性,在MySQL數(shù)據(jù)庫InnoDB存儲(chǔ)引擎中,還用Undo Log來進(jìn)行多版本并發(fā)控制(簡(jiǎn)稱MVCC)????????●?在操作任何數(shù)據(jù)之前,首先將數(shù)據(jù)備份到一個(gè)地方(這個(gè)存儲(chǔ)數(shù)據(jù)備份的地方稱為Undo Log)。然后進(jìn)行數(shù)據(jù)的修改。如果出現(xiàn)了錯(cuò)誤或者用戶執(zhí)行了ROLLBACK語句,系統(tǒng)可以利用Undo Log中的備份將數(shù)據(jù)恢復(fù)到事務(wù)開始之前的狀態(tài)。????????●?注意: Undo Log是邏輯日志,可以理解為:? ? ? ? ? ? ? 當(dāng)delete一條記錄時(shí),Undo Log中會(huì)記錄一條對(duì)應(yīng)insert記錄? ? ? ? ? ??? 當(dāng)insert一條記錄時(shí),Undo ?Log中會(huì)記錄一條對(duì)應(yīng)delete記錄? ? ? ???????當(dāng)update一條記錄時(shí),Undo Log中會(huì)記錄一條對(duì)應(yīng)相反的update記錄2.1.2、持久性實(shí)現(xiàn)原理---Redo Log?和Undo Log相反,Redo Log記錄的是新數(shù)據(jù)的備份。在事務(wù)提交前,只將Redo Log持久化即可,不需要將數(shù)據(jù)持久化,當(dāng)系統(tǒng)崩潰時(shí),雖然數(shù)據(jù)沒有持久化,但是Redo Log已經(jīng)持久化,系統(tǒng)可以根據(jù)Redo Log的內(nèi)容,將所有數(shù)據(jù)恢復(fù)到最新的狀態(tài)。
2.1.3、隔離性實(shí)現(xiàn)原理---鎖?在MySQL的InnoDB存儲(chǔ)引擎中,鎖可以分為兩類:
(1)共享鎖: 共享鎖定是將對(duì)象數(shù)據(jù)變?yōu)?strong>只讀形式,不能進(jìn)行更新,所以也成為讀取鎖定,簡(jiǎn)稱讀鎖。
(2)排他鎖: 排他鎖定是當(dāng)執(zhí)行插入/修改/刪除操作的時(shí)候,其它事務(wù)不能讀取該數(shù)據(jù),因此也成為寫入鎖定,簡(jiǎn)稱寫鎖。
?????相對(duì)其他數(shù)據(jù)庫而言,MySQL 的鎖機(jī)制比較簡(jiǎn)單,其最顯著的特點(diǎn)是不同的存儲(chǔ)引擎支持不同的鎖機(jī)制。比如,MylSAM、MEMORY存儲(chǔ)引擎采用的是表級(jí)鎖,InnoDB存儲(chǔ)引擎既支持行級(jí)鎖,也支持表級(jí)鎖,但默認(rèn)情況下是采用行級(jí)鎖。(1)表級(jí)鎖: 開銷小、加鎖快、不會(huì)出現(xiàn)死鎖、鎖定粒度大、發(fā)生鎖沖突的概率最高、并發(fā)度最低。
(2)行級(jí)鎖:?開銷大、加鎖慢、會(huì)出現(xiàn)死鎖、鎖定粒度最小、發(fā)生鎖沖突的概率最低、并發(fā)度也最高。
? ? ? ? ?對(duì)于表級(jí)鎖和行級(jí)鎖并沒有優(yōu)劣之分,需根據(jù)實(shí)際需求進(jìn)行選擇,比如對(duì)并發(fā)度要求高可以選擇行級(jí)鎖等。(1)?Redo Log重做日志,提供前滾操作; Undo?Log是回退日志,提供回滾操作。
(2)?Redo?Log通常是物理日志,記錄的是數(shù)據(jù)頁的物理修改而不是某一行或某幾行修改成怎樣怎樣,它用來恢復(fù)提交后的物理數(shù)據(jù)頁恢復(fù)數(shù)據(jù)頁,且只能恢復(fù)到最后一次提交的位置)。
(3) Undo Log用來回滾行記錄到某個(gè)版本。Undo?Log一般是邏輯日志,根據(jù)每行記錄進(jìn)行記錄。
竟然說到了MySQL的日志,binlog不得不提,它記錄了所有的DDL和DML語句(除了數(shù)據(jù)查詢語句select),以事件形式記錄,還包含語句所執(zhí)行的消耗的時(shí)間。
binlog三種模式及其優(yōu)缺點(diǎn):(1)?statement: 基于SQL語句的模式,某些語句中含有-些函數(shù),例如UUID NOW等在復(fù)制過程可能導(dǎo)致數(shù)據(jù)不一致甚至出錯(cuò)。
(2)row: 基于行的模式,記錄的是行的變化,很安全。但是binlog的磁盤占用會(huì)比其他兩種模式大很多,在一些大表中清除大量數(shù)據(jù)時(shí)在binlog中會(huì)生成很多條語句,可能導(dǎo)致從庫延遲變大。
(3)?mixed: 混合模式,根據(jù)語句來選用是statement還是row模式。
????????單個(gè)事務(wù)對(duì)數(shù)據(jù)庫的操作是串行的,不會(huì)存在并發(fā)問題,但是多個(gè)事務(wù)對(duì)數(shù)據(jù)庫的操作就會(huì)產(chǎn)生并發(fā)問題,分別為:臟讀、不可重復(fù)讀、幻讀。
1、臟讀:事務(wù)A讀取了事務(wù)B更新的數(shù)據(jù),然后B回滾操作,那么A讀取到的數(shù)據(jù)是臟數(shù)據(jù)。通俗講就是事務(wù)A讀取了其它事務(wù)未提交的數(shù)據(jù)(臟數(shù)據(jù))。2、不可重復(fù)讀:事務(wù) A 多次讀取同一數(shù)據(jù),事務(wù) B 在事務(wù)A多次讀取的過程中,對(duì)數(shù)據(jù)作了更新并提交,導(dǎo)致事務(wù)A多次讀取同一數(shù)據(jù)時(shí),結(jié)果不一致。3、幻讀:系統(tǒng)管理員A將數(shù)據(jù)庫中所有學(xué)生的成績(jī)從具體分?jǐn)?shù)改為ABCDE等級(jí),但是系統(tǒng)管理員B就在這個(gè)時(shí)候插入了一條具體分?jǐn)?shù)的記錄,當(dāng)系統(tǒng)管理員A改結(jié)束后發(fā)現(xiàn)還有一條記錄沒有改過來,就好像發(fā)生了幻覺一樣,這就叫幻讀。小結(jié):不可重復(fù)讀的和幻讀很容易混淆,不可重復(fù)讀側(cè)重于修改和刪除,幻讀側(cè)重于新增。解決不可重復(fù)讀的問題只需鎖住滿足條件的行,解決幻讀需要鎖表。
下面通過舉例來說明這4種事務(wù)隔離級(jí)別:
1、讀未提交
????(1)打開一個(gè)客戶端A,并設(shè)置當(dāng)前事務(wù)模式為read uncommitted(未提交讀),查詢表account的初始值。

????(2)在客戶端A的事務(wù)提交之前,打開另一個(gè)客戶端B,更新表account。????(3)這時(shí),雖然客戶端B的事務(wù)還沒提交,但是客戶端A就可以查詢到B已經(jīng)更新的數(shù)據(jù)。????(4)一旦客戶端B的事務(wù)因?yàn)槟撤N原因回滾,所有的操作都將會(huì)被撤銷,那客戶端A查詢到的數(shù)據(jù)其實(shí)就是臟數(shù)據(jù)。

????(5)在客戶端A執(zhí)行更新語句update account set balance = balance - 50 where id =1,lilei的balance沒有變成350,居然是400,是不是很奇怪,數(shù)據(jù)不一致啊,如果你這么想就太天真 了,在應(yīng)用程序中,我們會(huì)用400-50=350,并不知道其他會(huì)話回滾了,要想解決這個(gè)問題可以采用讀已提交的隔離級(jí)別。
2、讀已提交
????(1)打開一個(gè)客戶端A,并設(shè)置當(dāng)前事務(wù)模式為read committed(未提交讀),查詢表account的所有記錄。????(2)在客戶端A的事務(wù)提交之前,打開另一個(gè)客戶端B,更新表account。????(3)這時(shí),客戶端B的事務(wù)還沒提交,客戶端A不能查詢到B已經(jīng)更新的數(shù)據(jù),解決了臟讀問題。

????(4)客戶端B的事務(wù)提交

????(5)?客戶端A執(zhí)行與上一步相同的查詢,結(jié)果 與上一步不一致,即產(chǎn)生了不可重復(fù)讀的問題。

3、可重復(fù)讀
????(1)打開一個(gè)客戶端A,并設(shè)置當(dāng)前事務(wù)模式為repeatable read,查詢account表中id為4的記錄。

????(2)在客戶端A的事務(wù)提交之前,打開另一個(gè)客戶端B,向account表中插入一條記錄,并提交。

????(3)在客戶端B提交之后,同樣地,客戶端A向account表中插入id為4的記錄并再次查詢id為4的記錄,發(fā)現(xiàn)主鍵重復(fù)但又讀取不到數(shù)據(jù),感覺像自己讀過一樣,這就造成了幻讀。

更多信息請(qǐng)關(guān)注公眾號(hào):「軟件老王」,關(guān)注不迷路,軟件老王和他的IT朋友們,分享一些他們的技術(shù)見解和生活故事。