點(diǎn)擊關(guān)注公眾號,,Java干貨及時送達(dá) 來源:https:///131.html
ACIDMySQL 作為一個關(guān)系型數(shù)據(jù)庫,,以最常見的 InnoDB 引擎來說,,是如何保證 ACID 的。
隔離性先說說隔離性,,首先是四種隔離級別。
不同的隔離級別是為了解決不同的問題,。也就是臟讀、幻讀,、不可重復(fù)讀,。
那么不同的隔離級別,隔離性是如何實(shí)現(xiàn)的,,為什么不同事物間能夠互不干擾,?答案是 鎖 和 MVCC。 鎖先來說說鎖,, MySQL 有多少鎖,。 粒度從粒度上來說就是表鎖、頁鎖,、行鎖,。表鎖有意向共享鎖、意向排他鎖,、自增鎖等,。行鎖是在引擎層由各個引擎自己實(shí)現(xiàn)的,。但并不是所有的引擎都支持行鎖,比如 MyISAM 引擎就不支持行鎖,。 行鎖的種類在 InnoDB 事務(wù)中,,行鎖通過給索引上的索引項加鎖來實(shí)現(xiàn)。這意味著只有通過索引條件檢索數(shù)據(jù),,InnoDB才使用行級鎖,,否則將使用表鎖。行級鎖定同樣分為兩種類型:共享鎖和排他鎖,,以及加鎖前需要先獲得的意向共享鎖和意向排他鎖,。
行鎖是在需要的時候才加上的,,但并不是不需要了就立刻釋放,而是要等到事務(wù)結(jié)束時才釋放,。這個就是兩階段鎖協(xié)議,。 行鎖的實(shí)現(xiàn)算法Record Lock單個行記錄上的鎖,總是會去鎖住索引記錄,。 Gap Lock間隙鎖,,想一下幻讀的原因,其實(shí)就是行鎖只能鎖住行,,但新插入記錄這個動作,,要更新的是記錄之間的“間隙”。所以加入間隙鎖來解決幻讀,。 Next-Key LockGap Lock + Record Lock, 左開又閉,。 鎖之于隔離性大致介紹了下鎖,可以看到,。有了鎖,,當(dāng)某事務(wù)正在寫數(shù)據(jù)時,其他事務(wù)獲取不到寫鎖,,就無法寫數(shù)據(jù),,一定程度上保證了事務(wù)間的隔離,。但前面說,加了寫鎖,,為什么其他事務(wù)也能讀數(shù)據(jù)呢,,不是獲取不到讀鎖嗎? MVCC前面說到,,有了鎖,,當(dāng)前事務(wù)沒有寫鎖就不能修改數(shù)據(jù),但還是能讀的,,而且讀的時候,,即使該行數(shù)據(jù)其他事務(wù)已修改且提交,還是可以重復(fù)讀到同樣的值,。這就是MVCC,,多版本的并發(fā)控制,Multi-Version Concurrency Control,。 版本鏈Innodb 中行記錄的存儲格式,,有一些額外的字段:DATA_TRX_ID和DATA_ROLL_PTR。
ReadView在每一條 SQL 開始的時候被創(chuàng)建,有幾個重要屬性:
開始查詢現(xiàn)在開始查詢,,一個 select 過來了,,找到了一行數(shù)據(jù)。
點(diǎn)擊關(guān)注公眾號,,Java干貨及時送達(dá) RR 級別的幻讀有了鎖和 MVCC , 事務(wù)的隔離性得到解決,。這里要引申一下,,默認(rèn)的 RR 的級別,解決了幻讀嗎,?幻讀通常針對的是 INSERT, 不可重復(fù)度則針對 UPDATE ,。
我們期望是
實(shí)際卻是
其實(shí)在 MySQL 可重復(fù)讀的隔離級別中并不是完全解決了幻讀的問題,而是解決了讀數(shù)據(jù)情況下的幻讀問題,。而對于修改的操作依舊存在幻讀問題,,就是說 MVCC 對于幻讀的解決時不徹底的。另外,,MySQL 系列面試題和答案全部整理好了,,微信搜索Java技術(shù)棧,在后臺發(fā)送:面試,,可以在線閱讀,。 原子性接著說說原子性。前文有提到 undo log ,,回滾日志,。隔離性的MVCC其實(shí)就是依靠它來實(shí)現(xiàn)的,原子性也是,。實(shí)現(xiàn)原子性的關(guān)鍵,,是當(dāng)事務(wù)回滾時能夠撤銷所有已經(jīng)成功執(zhí)行的sql語句。 最新 Java 核心技術(shù)推薦:https://github.com/javastacks/javastack 當(dāng)事務(wù)對數(shù)據(jù)庫進(jìn)行修改時,InnoDB會生成對應(yīng)的 undo log,;如果事務(wù)執(zhí)行失敗或調(diào)用了 rollback,,導(dǎo)致事務(wù)需要回滾,便可以利用 undo log 中的信息將數(shù)據(jù)回滾到修改之前的樣子,。undo log 屬于邏輯日志,,它記錄的是sql執(zhí)行相關(guān)的信息。當(dāng)發(fā)生回滾時,,InnoDB 會根據(jù) undo log 的內(nèi)容做與之前相反的工作:
以update操作為例:當(dāng)事務(wù)執(zhí)行update時,其生成的undo log中會包含被修改行的主鍵(以便知道修改了哪些行),、修改了哪些列,、這些列在修改前后的值等信息,回滾時便可以使用這些信息將數(shù)據(jù)還原到update之前的狀態(tài),。 持久性Innnodb有很多 log,,持久性靠的是 redo log。 一條SQL更新語句怎么運(yùn)行持久性肯定和寫有關(guān),,MySQL 里經(jīng)常說到的 WAL 技術(shù),,WAL 的全稱是 Write-Ahead Logging,它的關(guān)鍵點(diǎn)就是先寫日志,,再寫磁盤,。就像小店做生意,有個粉板,,有個賬本,,來客了先寫粉板,,等不忙的時候再寫賬本,。點(diǎn)擊在線刷題,看看你可以打多少分了,。 redo logredo log 就是這個粉板,,當(dāng)有一條記錄要更新時,InnoDB 引擎就會先把記錄寫到 redo log(并更新內(nèi)存),,這個時候更新就算完成了,。在適當(dāng)?shù)臅r候,將這個操作記錄更新到磁盤里面,而這個更新往往是在系統(tǒng)比較空閑的時候做,,這就像打烊以后掌柜做的事,。 redo log 有兩個特點(diǎn):
對于redo log 是有兩階段的:commit 和 prepare 如果不使用“兩階段提交”,,數(shù)據(jù)庫的狀態(tài)就有可能和用它的日志恢復(fù)出來的庫的狀態(tài)不一致. 好了,,先到這里,看看另一個,。 Buffer PoolInnoDB還提供了緩存,,Buffer Pool 中包含了磁盤中部分?jǐn)?shù)據(jù)頁的映射,作為訪問數(shù)據(jù)庫的緩沖:
Buffer Pool 的使用大大提高了讀寫數(shù)據(jù)的效率,,但是也帶了新的問題:如果MySQL宕機(jī),而此時 Buffer Pool 中修改的數(shù)據(jù)還沒有刷新到磁盤,,就會導(dǎo)致數(shù)據(jù)的丟失,,事務(wù)的持久性無法保證。 所以加入了 redo log,。當(dāng)數(shù)據(jù)修改時,,除了修改Buffer Pool中的數(shù)據(jù),還會在redo log記錄這次操作,; 當(dāng)事務(wù)提交時,,會調(diào)用fsync接口對redo log進(jìn)行刷盤。 如果MySQL宕機(jī),,重啟時可以讀取redo log中的數(shù)據(jù),,對數(shù)據(jù)庫進(jìn)行恢復(fù)。 redo log采用的是WAL(Write-ahead logging,,預(yù)寫式日志),,所有修改先寫入日志,再更新到Buffer Pool,,保證了數(shù)據(jù)不會因MySQL宕機(jī)而丟失,,從而滿足了持久性要求。而且這樣做還有兩個優(yōu)點(diǎn):
binlog說到這,,可能會疑問還有個 bin log 也是寫操作并用于數(shù)據(jù)的恢復(fù),有啥區(qū)別呢,。
binlog 和 redo log對于語句
為什么先寫 redo log 呢 ,?
一致性一致性是事務(wù)追求的最終目標(biāo),,前問所訴的原子性,、持久性和隔離性,其實(shí)都是為了保證數(shù)據(jù)庫狀態(tài)的一致性,。當(dāng)然,,上文都是數(shù)據(jù)庫層面的保障,一致性的實(shí)現(xiàn)也需要應(yīng)用層面進(jìn)行保障,。 也就是你的業(yè)務(wù),,比如購買操作只扣除用戶的余額,不減庫存,,肯定無法保證狀態(tài)的一致,。 總結(jié)MySQL 都很熟, ACID 也知道是個啥,,但 MySQL 的 ACID 怎么實(shí)現(xiàn)的,? 有時候,就像你知道了有 undo log,、redo log 但可能并不太清楚為什么有,,當(dāng)知道了設(shè)計的目的,了解起來就會更加清晰了,。另外,,關(guān)注公眾號Java技術(shù)棧,在后臺回復(fù):面試,可以獲取我整理的 Java/ MySQL 系列面試題和答案,,非常齊全,。 參考: https://zhuanlan.zhihu.com/p/52977862 |
|
來自: 昵稱10087950 > 《JAVA》