<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Mysql的事務(wù)實(shí)現(xiàn)原理

          共 6481字,需瀏覽 13分鐘

           ·

          2020-04-05 23:28

          今天跟大家分享下Mysql的事務(wù)實(shí)現(xiàn)原理的知識(shí)。

          開(kāi)篇

          相信大家都用過(guò)事務(wù)以及了解他的特點(diǎn),如原子性(Atomicity),一致性(Consistency),隔離型(Isolation)以及持久性(Durability)等。今天想跟大家一起研究下事務(wù)內(nèi)部到底是怎么實(shí)現(xiàn)的,在講解前我想先拋出個(gè)問(wèn)題:

          事務(wù)想要做到什么效果?

          按我理解,無(wú)非是要做到可靠性以及并發(fā)處理。

          可靠性:數(shù)據(jù)庫(kù)要保證當(dāng)insert或update操作時(shí)拋異?;蛘邤?shù)據(jù)庫(kù)crash的時(shí)候需要保障數(shù)據(jù)的操作前后的一致,想要做到這個(gè),我需要知道我修改之前和修改之后的狀態(tài),所以就有了undo log和redo log。

          并發(fā)處理:也就是說(shuō)當(dāng)多個(gè)并發(fā)請(qǐng)求過(guò)來(lái),并且其中有一個(gè)請(qǐng)求是對(duì)數(shù)據(jù)修改操作的時(shí)候會(huì)有影響,為了避免讀到臟數(shù)據(jù),所以需要對(duì)事務(wù)之間的讀寫(xiě)進(jìn)行隔離,至于隔離到啥程度得看業(yè)務(wù)系統(tǒng)的場(chǎng)景了,實(shí)現(xiàn)這個(gè)就得用MySQL 的隔離級(jí)別。

          下面我首先講實(shí)現(xiàn)事務(wù)功能的三個(gè)技術(shù),分別是日志文件(redo log 和 undo log),鎖技術(shù)以及MVCC,然后再講事務(wù)的實(shí)現(xiàn)原理,包括原子性是怎么實(shí)現(xiàn)的,隔離型是怎么實(shí)現(xiàn)的等等。最后在做一個(gè)總結(jié),希望大家能夠耐心看完

          • redo log與undo log介紹

          • mysql鎖技術(shù)以及MVCC基礎(chǔ)

          • 事務(wù)的實(shí)現(xiàn)原理

          • 總結(jié)


          redo log 與 undo log介紹

          1. redo log

          什么是redo log ?

          redo log叫做重做日志,是用來(lái)實(shí)現(xiàn)事務(wù)的持久性。該日志文件由兩部分組成:重做日志緩沖(redo log buffer)以及重做日志文件(redo log),前者是在內(nèi)存中,后者在磁盤中。當(dāng)事務(wù)提交之后會(huì)把所有修改信息都會(huì)存到該日志中。假設(shè)有個(gè)表叫做tb1(id,username) 現(xiàn)在要插入數(shù)據(jù)(3,ceshi)

          246da39840ce1cf7409895d43ee813ca.webp

          start transaction;select balance from bank where name="zhangsan";// 生成 重做日志 balance=600update bank set balance = balance - 400; // 生成 重做日志 amount=400update finance set amount = amount + 400;commit;

          c7160437518d18327d2c84574e407d25.webp

          redo log 有什么作用?

          mysql 為了提升性能不會(huì)把每次的修改都實(shí)時(shí)同步到磁盤,而是會(huì)先存到Boffer Pool(緩沖池)里頭,把這個(gè)當(dāng)作緩存來(lái)用。然后使用后臺(tái)線程去做緩沖池和磁盤之間的同步。

          那么問(wèn)題來(lái)了,如果還沒(méi)來(lái)的同步的時(shí)候宕機(jī)或斷電了怎么辦?還沒(méi)來(lái)得及執(zhí)行上面圖中紅色的操作。這樣會(huì)導(dǎo)致丟部分已提交事務(wù)的修改信息!

          所以引入了redo log來(lái)記錄已成功提交事務(wù)的修改信息,并且會(huì)把redo log持久化到磁盤,系統(tǒng)重啟之后在讀取redo log恢復(fù)最新數(shù)據(jù)。

          總結(jié):

          redo log是用來(lái)恢復(fù)數(shù)據(jù)的 用于保障,已提交事務(wù)的持久化特性

          2.undo log

          什么是 undo log ?

          undo log 叫做回滾日志,用于記錄數(shù)據(jù)被修改前的信息。他正好跟前面所說(shuō)的重做日志所記錄的相反,重做日志記錄數(shù)據(jù)被修改后的信息。undo log主要記錄的是數(shù)據(jù)的邏輯變化,為了在發(fā)生錯(cuò)誤時(shí)回滾之前的操作,需要將之前的操作都記錄下來(lái),然后在發(fā)生錯(cuò)誤時(shí)才可以回滾。

          還用上面那兩張表

          0abd9b3333519b71a37288e6914102b3.webp

          每次寫(xiě)入數(shù)據(jù)或者修改數(shù)據(jù)之前都會(huì)把修改前的信息記錄到 undo log。

          undo log 有什么作用?

          undo log 記錄事務(wù)修改之前版本的數(shù)據(jù)信息,因此假如由于系統(tǒng)錯(cuò)誤或者rollback操作而回滾的話可以根據(jù)undo log的信息來(lái)進(jìn)行回滾到?jīng)]被修改前的狀態(tài)。

          總結(jié):

          undo log是用來(lái)回滾數(shù)據(jù)的用于保障 未提交事務(wù)的原子性

          mysql鎖技術(shù)以及MVCC基礎(chǔ)

          1. mysql鎖技術(shù)

          當(dāng)有多個(gè)請(qǐng)求來(lái)讀取表中的數(shù)據(jù)時(shí)可以不采取任何操作,但是多個(gè)請(qǐng)求里有讀請(qǐng)求,又有修改請(qǐng)求時(shí)必須有一種措施來(lái)進(jìn)行并發(fā)控制。不然很有可能會(huì)造成不一致。

          讀寫(xiě)鎖

          解決上述問(wèn)題很簡(jiǎn)單,只需用兩種鎖的組合來(lái)對(duì)讀寫(xiě)請(qǐng)求進(jìn)行控制即可,這兩種鎖被稱為:

          共享鎖(shared lock),又叫做"讀鎖"

          讀鎖是可以共享的,或者說(shuō)多個(gè)讀請(qǐng)求可以共享一把鎖讀數(shù)據(jù),不會(huì)造成阻塞。

          排他鎖(exclusive lock),又叫做"寫(xiě)鎖"

          寫(xiě)鎖會(huì)排斥其他所有獲取鎖的請(qǐng)求,一直阻塞,直到寫(xiě)入完成釋放鎖。

          112454517ff82cabc295b6af034aabe9.webp

          總結(jié):

          通過(guò)讀寫(xiě)鎖,可以做到讀讀可以并行,但是不能做到寫(xiě)讀,寫(xiě)寫(xiě)并行

          事務(wù)的隔離性就是根據(jù)讀寫(xiě)鎖來(lái)實(shí)現(xiàn)的?。?!這個(gè)后面再說(shuō)。

          2. MVCC基礎(chǔ)

          MVCC (MultiVersion Concurrency Control) 叫做多版本并發(fā)控制。

          InnoDB的 MVCC ,是通過(guò)在每行記錄的后面保存兩個(gè)隱藏的列來(lái)實(shí)現(xiàn)的。這兩個(gè)列,一個(gè)保存了行的創(chuàng)建時(shí)間,一個(gè)保存了行的過(guò)期時(shí)間,當(dāng)然存儲(chǔ)的并不是實(shí)際的時(shí)間值,而是系統(tǒng)版本號(hào)

          以上片段摘自《高性能Mysql》這本書(shū)對(duì)MVCC的定義。他的主要實(shí)現(xiàn)思想是通過(guò)數(shù)據(jù)多版本來(lái)做到讀寫(xiě)分離。從而實(shí)現(xiàn)不加鎖讀進(jìn)而做到讀寫(xiě)并行。

          MVCC在mysql中的實(shí)現(xiàn)依賴的是undo log與read view

          • undo log :undo log 中記錄某行數(shù)據(jù)的多個(gè)版本的數(shù)據(jù)。

          • read view :用來(lái)判斷當(dāng)前版本數(shù)據(jù)的可見(jiàn)性

          2188e347001bf7251a38b485a263d12e.webp

          事務(wù)的實(shí)現(xiàn)

          前面講的重做日志,回滾日志以及鎖技術(shù)就是實(shí)現(xiàn)事務(wù)的基礎(chǔ)。

          事務(wù)的原子性是通過(guò) undo log 來(lái)實(shí)現(xiàn)的事務(wù)的持久性性是通過(guò) redo log 來(lái)實(shí)現(xiàn)的事務(wù)的隔離性是通過(guò) (讀寫(xiě)鎖+MVCC)來(lái)實(shí)現(xiàn)的而事務(wù)的終極大 boss 一致性是通過(guò)原子性,持久性,隔離性來(lái)實(shí)現(xiàn)的!??!

          原子性,持久性,隔離性折騰半天的目的也是為了保障數(shù)據(jù)的一致性!

          總之,ACID只是個(gè)概念,事務(wù)最終目的是要保障數(shù)據(jù)的可靠性,一致性。

          1.原子性的實(shí)現(xiàn)

          什么是原子性:

          一個(gè)事務(wù)必須被視為不可分割的最小工作單位,一個(gè)事務(wù)中的所有操作要么全部成功提交,要么全部失敗回滾,對(duì)于一個(gè)事務(wù)來(lái)說(shuō)不可能只執(zhí)行其中的部分操作,這就是事務(wù)的原子性。

          上面這段話取自《高性能MySQL》這本書(shū)對(duì)原子性的定義,原子性可以概括為就是要實(shí)現(xiàn)要么全部失敗,要么全部成功。

          以上概念相信大家伙兒都了解,那么數(shù)據(jù)庫(kù)是怎么實(shí)現(xiàn)的呢?就是通過(guò)回滾操作。所謂回滾操作就是當(dāng)發(fā)生錯(cuò)誤異?;蛘唢@式的執(zhí)行rollback語(yǔ)句時(shí)需要把數(shù)據(jù)還原到原先的模樣,所以這時(shí)候就需要用到undo log來(lái)進(jìn)行回滾,接下來(lái)看一下undo log在實(shí)現(xiàn)事務(wù)原子性時(shí)怎么發(fā)揮作用的

          1.1  undo log 的生成

          假設(shè)有兩個(gè)表 bank和finance,表中原始數(shù)據(jù)如圖所示,當(dāng)進(jìn)行插入,刪除以及更新操作時(shí)生成的undo log如下面圖所示:

          0abd9b3333519b71a37288e6914102b3.webp

          3242ff59967afc5273077274b25b46bf.webp

          從上圖可以了解到數(shù)據(jù)的變更都伴隨著回滾日志的產(chǎn)生:

          (1) 產(chǎn)生了被修改前數(shù)據(jù)(zhangsan,1000) 的回滾日志

          (2) 產(chǎn)生了被修改前數(shù)據(jù)(zhangsan,0) 的回滾日志

          根據(jù)上面流程可以得出如下結(jié)論:

          1.每條數(shù)據(jù)變更(insert/update/delete)操作都伴隨一條undo log的生成,并且回滾日志必須先于數(shù)據(jù)持久化到磁盤上

          2.所謂的回滾就是根據(jù)回滾日志做逆向操作,比如delete的逆向操作為insert,insert的逆向操作為delete,update的逆向?yàn)閡pdate等。

          思考:為什么先寫(xiě)日志后寫(xiě)數(shù)據(jù)庫(kù)? ---稍后做解釋

          1.2  根據(jù)undo log 進(jìn)行回滾

          為了做到同時(shí)成功或者失敗,當(dāng)系統(tǒng)發(fā)生錯(cuò)誤或者執(zhí)行rollback操作時(shí)需要根據(jù)undo log 進(jìn)行回滾

          f8dcc004c2837e22f294db953eb10b07.webp

          回滾操作就是要還原到原來(lái)的狀態(tài),undo log記錄了數(shù)據(jù)被修改前的信息以及新增和被刪除的數(shù)據(jù)信息,根據(jù)undo log生成回滾語(yǔ)句,比如:

          (1) 如果在回滾日志里有新增數(shù)據(jù)記錄,則生成刪除該條的語(yǔ)句

          (2) 如果在回滾日志里有刪除數(shù)據(jù)記錄,則生成生成該條的語(yǔ)句

          (3) 如果在回滾日志里有修改數(shù)據(jù)記錄,則生成修改到原先數(shù)據(jù)的語(yǔ)句

          2.持久性的實(shí)現(xiàn)

          事務(wù)一旦提交,其所作做的修改會(huì)永久保存到數(shù)據(jù)庫(kù)中,此時(shí)即使系統(tǒng)崩潰修改的數(shù)據(jù)也不會(huì)丟失。

          先了解一下MySQL的數(shù)據(jù)存儲(chǔ)機(jī)制,MySQL的表數(shù)據(jù)是存放在磁盤上的,因此想要存取的時(shí)候都要經(jīng)歷磁盤IO,然而即使是使用SSD磁盤IO也是非常消耗性能的。為此,為了提升性能InnoDB提供了緩沖池(Buffer Pool),Buffer Pool中包含了磁盤數(shù)據(jù)頁(yè)的映射,可以當(dāng)做緩存來(lái)使用:

          讀數(shù)據(jù)會(huì)首先從緩沖池中讀取,如果緩沖池中沒(méi)有,則從磁盤讀取在放入緩沖池;

          寫(xiě)數(shù)據(jù)會(huì)首先寫(xiě)入緩沖池,緩沖池中的數(shù)據(jù)會(huì)定期同步到磁盤中;

          上面這種緩沖池的措施雖然在性能方面帶來(lái)了質(zhì)的飛躍,但是它也帶來(lái)了新的問(wèn)題,當(dāng)MySQL系統(tǒng)宕機(jī),斷電的時(shí)候可能會(huì)丟數(shù)據(jù)?。?!

          因?yàn)槲覀兊臄?shù)據(jù)已經(jīng)提交了,但此時(shí)是在緩沖池里頭,還沒(méi)來(lái)得及在磁盤持久化,所以我們急需一種機(jī)制需要存一下已提交事務(wù)的數(shù)據(jù),為恢復(fù)數(shù)據(jù)使用。

          于是 redo log就派上用場(chǎng)了。下面看下redo log是什么時(shí)候產(chǎn)生的

          95e2bee6c11a290fff086f2943bf8f3d.webp

          既然redo log也需要存儲(chǔ),也涉及磁盤IO為啥還用它?

          (1)redo log 的存儲(chǔ)是順序存儲(chǔ),而緩存同步是隨機(jī)操作。

          (2)緩存同步是以數(shù)據(jù)頁(yè)為單位的,每次傳輸?shù)臄?shù)據(jù)大小大于redo log。

          3.隔離性實(shí)現(xiàn)

          隔離性是事務(wù)ACID特性里最復(fù)雜的一個(gè)。在SQL標(biāo)準(zhǔn)里定義了四種隔離級(jí)別,每一種級(jí)別都規(guī)定一個(gè)事務(wù)中的修改,哪些是事務(wù)之間可見(jiàn)的,哪些是不可見(jiàn)的。

          級(jí)別越低的隔離級(jí)別可以執(zhí)行越高的并發(fā),但同時(shí)實(shí)現(xiàn)復(fù)雜度以及開(kāi)銷也越大。

          Mysql 隔離級(jí)別有以下四種(級(jí)別由低到高):

          READ UNCOMMITED (未提交讀)READ COMMITED (提交讀)REPEATABLE READ (可重復(fù)讀)SERIALIZABLE (可重復(fù)讀)

          只要徹底理解了隔離級(jí)別以及他的實(shí)現(xiàn)原理就相當(dāng)于理解了ACID里的隔離型。前面說(shuō)過(guò)原子性,隔離性,持久性的目的都是為了要做到一致性,但隔離型跟其他兩個(gè)有所區(qū)別,原子性和持久性是為了要實(shí)現(xiàn)數(shù)據(jù)的可性保障靠,比如要做到宕機(jī)后的恢復(fù),以及錯(cuò)誤后的回滾。

          那么隔離性是要做到什么呢? 隔離性是要管理多個(gè)并發(fā)讀寫(xiě)請(qǐng)求的訪問(wèn)順序。 這種順序包括串行或者是并行

          說(shuō)明一點(diǎn),寫(xiě)請(qǐng)求不僅僅是指insert操作,又包括update操作。

          12e0e2b80d4791c2ad6c1970fd122d10.webp

          總之,從隔離性的實(shí)現(xiàn)可以看出這是一場(chǎng)數(shù)據(jù)的可靠性與性能之間的權(quán)衡。

          可靠性性高的,并發(fā)性能低(比如 Serializable)可靠性低的,并發(fā)性能高(比如 Read Uncommited)

          READ UNCOMMITTED

          在READ UNCOMMITTED隔離級(jí)別下,事務(wù)中的修改即使還沒(méi)提交,對(duì)其他事務(wù)是可見(jiàn)的。事務(wù)可以讀取未提交的數(shù)據(jù),造成臟讀。

          因?yàn)樽x不會(huì)加任何鎖,所以寫(xiě)操作在讀的過(guò)程中修改數(shù)據(jù),所以會(huì)造成臟讀。好處是可以提升并發(fā)處理性能,能做到讀寫(xiě)并行。

          換句話說(shuō),讀的操作不能排斥寫(xiě)請(qǐng)求。

          7edff640d6ae3ff983e7a64db6592608.webp

          優(yōu)點(diǎn):讀寫(xiě)并行,性能高

          缺點(diǎn):造成臟讀

          READ COMMITTED

          一個(gè)事務(wù)的修改在他提交之前的所有修改,對(duì)其他事務(wù)都是不可見(jiàn)的。其他事務(wù)能讀到已提交的修改變化。在很多場(chǎng)景下這種邏輯是可以接受的。

          InnoDB在 READ COMMITTED,使用排它鎖,讀取數(shù)據(jù)不加鎖而是使用了MVCC機(jī)制?;蛘邠Q句話說(shuō)他采用了讀寫(xiě)分離機(jī)制。

          但是該級(jí)別會(huì)產(chǎn)生不可重讀以及幻讀問(wèn)題。

          什么是不可重讀?

          在一個(gè)事務(wù)內(nèi)多次讀取的結(jié)果不一樣。

          為什么會(huì)產(chǎn)生不可重復(fù)讀?

          這跟 READ COMMITTED 級(jí)別下的MVCC機(jī)制有關(guān)系,在該隔離級(jí)別下每次 select的時(shí)候新生成一個(gè)版本號(hào),所以每次select的時(shí)候讀的不是一個(gè)副本而是不同的副本。

          在每次select之間有其他事務(wù)更新了我們讀取的數(shù)據(jù)并提交了,那就出現(xiàn)了不可重復(fù)讀

          REPEATABLE READ(Mysql默認(rèn)隔離級(jí)別)

          在一個(gè)事務(wù)內(nèi)的多次讀取的結(jié)果是一樣的。這種級(jí)別下可以避免,臟讀,不可重復(fù)讀等查詢問(wèn)題。mysql 有兩種機(jī)制可以達(dá)到這種隔離級(jí)別的效果,分別是采用讀寫(xiě)鎖以及MVCC。

          采用讀寫(xiě)鎖實(shí)現(xiàn)

          d4d85e134026c4fd21192e12f8f9dcb1.webp

          為什么能可重復(fù)度?只要沒(méi)釋放讀鎖,在次讀的時(shí)候還是可以讀到第一次讀的數(shù)據(jù)。

          優(yōu)點(diǎn):實(shí)現(xiàn)起來(lái)簡(jiǎn)單

          缺點(diǎn):無(wú)法做到讀寫(xiě)并行

          采用MVCC實(shí)現(xiàn)

          3d2cb1ce7898856956a70dce14dea9be.webp

          為什么能可重復(fù)度?因?yàn)槎啻巫x取只生成一個(gè)版本,讀到的自然是相同數(shù)據(jù)。

          優(yōu)點(diǎn):讀寫(xiě)并行  

          缺點(diǎn):實(shí)現(xiàn)的復(fù)雜度高

          但是在該隔離級(jí)別下仍會(huì)存在幻讀的問(wèn)題,關(guān)于幻讀的解決我打算另開(kāi)一篇來(lái)介紹。

          SERIALIZABLE

          該隔離級(jí)別理解起來(lái)最簡(jiǎn)單,實(shí)現(xiàn)也最單。在隔離級(jí)別下除了不會(huì)造成數(shù)據(jù)不一致問(wèn)題,沒(méi)其他優(yōu)點(diǎn)。

          f0333cf658ef8b3a273649bf0f8f512b.webp

          c130bafee896ad2f0b12347cbd21746c.webp

          --摘自《高性能Mysql》

          4.一致性的實(shí)現(xiàn)

          數(shù)據(jù)庫(kù)總是從一個(gè)一致性的狀態(tài)轉(zhuǎn)移到另一個(gè)一致性的狀態(tài)。

          下面舉個(gè)例子:zhangsan 從銀行卡轉(zhuǎn)400到理財(cái)賬戶

          start transaction;select balance from bank where name="zhangsan";// 生成 重做日志 balance=600update bank set balance = balance - 400; // 生成 重做日志 amount=400update finance set amount = amount + 400;commit;

          1.假如執(zhí)行完 update bank set balance = balance - 400;之發(fā)生異常了,銀行卡的錢也不能平白無(wú)辜的減少,而是回滾到最初狀態(tài)。

          2.又或者事務(wù)提交之后,緩沖池還沒(méi)同步到磁盤的時(shí)候宕機(jī)了,這也是不能接受的,應(yīng)該在重啟的時(shí)候恢復(fù)并持久化。

          3.假如有并發(fā)事務(wù)請(qǐng)求的時(shí)候也應(yīng)該做好事務(wù)之間的可見(jiàn)性問(wèn)題,避免造成臟讀,不可重復(fù)讀,幻讀等。在涉及并發(fā)的情況下往往在性能和一致性之間做平衡,做一定的取舍,所以隔離性也是對(duì)一致性的一種破壞。

          總結(jié)

          本文出發(fā)點(diǎn)是想講一下Mysql的事務(wù)的實(shí)現(xiàn)原理。

          實(shí)現(xiàn)事務(wù)采取了哪些技術(shù)以及思想?

          • 原子性:使用 undo log ,從而達(dá)到回滾

          • 持久性:使用 redo log,從而達(dá)到故障后恢復(fù)

          • 隔離性:使用鎖以及MVCC,運(yùn)用的優(yōu)化思想有讀寫(xiě)分離,讀讀并行,讀寫(xiě)并行

          • 一致性:通過(guò)回滾,以及恢復(fù),和在并發(fā)環(huán)境下的隔離做到一致性。

          參考文獻(xiàn):https://juejin.im/post/5cb2e3b46fb9a0686e40c5cb

          - END -


          文末福利我準(zhǔn)備了100份經(jīng)典的電子書(shū)。部分截圖如下:
          eae67fbfba4151dffd6d13deac44b7e4.webp
          領(lǐng)取方式:掃描下方二維碼,關(guān)注Java開(kāi)發(fā)寶典,并回復(fù)“100即可領(lǐng)??!武哥分享無(wú)任何套路~


          29e0c6ed5995ec9264f14a3baf00a8ea.webp
          長(zhǎng)按二維碼關(guān)注我每天一篇Java干貨


          點(diǎn)贊是最大的支持  b800fe8efb58435996be342fb3242763.webp
          瀏覽 44
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  免费播放黄色片网站 | 中国福利视频黄色一级片 | 白峰美羽一区二区三区 | 中文人妻无码一区二区三区久久 | 人妻无码操逼 |