<kbd id="5sdj3"></kbd>
<th id="5sdj3"></th>

  • <dd id="5sdj3"><form id="5sdj3"></form></dd>
    <td id="5sdj3"><form id="5sdj3"><big id="5sdj3"></big></form></td><del id="5sdj3"></del>

  • <dd id="5sdj3"></dd>
    <dfn id="5sdj3"></dfn>
  • <th id="5sdj3"></th>
    <tfoot id="5sdj3"><menuitem id="5sdj3"></menuitem></tfoot>

  • <td id="5sdj3"><form id="5sdj3"><menu id="5sdj3"></menu></form></td>
  • <kbd id="5sdj3"><form id="5sdj3"></form></kbd>

    面試官:你說熟悉MySQL事務(wù),那來談?wù)勈聞?wù)的實(shí)現(xiàn)原理吧!

    共 6124字,需瀏覽 13分鐘

     ·

    2021-02-24 17:45

    閱讀本文大概需要 10?分鐘。

    作者:小小木
    cnblogs.com/wyc1994666/p/11367051.html

    開篇

    相信大家都用過事務(wù)以及了解他的特點(diǎn),如原子性(Atomicity),一致性(Consistency),隔離型(Isolation)以及持久性(Durability)等。今天想跟大家一起研究下事務(wù)內(nèi)部到底是怎么實(shí)現(xiàn)的,在講解前我想先拋出個問題:
    事務(wù)想要做到什么效果?
    按我理解,無非是要做到可靠性以及并發(fā)處理
    • 可靠性:數(shù)據(jù)庫要保證當(dāng)insert或update操作時拋異常或者數(shù)據(jù)庫crash的時候需要保障數(shù)據(jù)的操作前后的一致,想要做到這個,我需要知道我修改之前和修改之后的狀態(tài),所以就有了undo log和redo log。

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

    下面我首先講實(shí)現(xiàn)事務(wù)功能的三個技術(shù),分別是日志文件(redo log 和 undo log),鎖技術(shù)以及MVCC,然后再講事務(wù)的實(shí)現(xiàn)原理,包括原子性是怎么實(shí)現(xiàn)的,隔離型是怎么實(shí)現(xiàn)的等等。最后在做一個總結(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叫做重做日志,是用來實(shí)現(xiàn)事務(wù)的持久性。該日志文件由兩部分組成:重做日志緩沖(redo log buffer)以及重做日志文件(redo log),前者是在內(nèi)存中,后者在磁盤中。
    當(dāng)事務(wù)提交之后會把所有修改信息都會存到該日志中。假設(shè)有個表叫做tb1(id,username) 現(xiàn)在要插入數(shù)據(jù)(3,ceshi)

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

    redo log 有什么作用?

    mysql 為了提升性能不會把每次的修改都實(shí)時同步到磁盤,而是會先存到Boffer Pool(緩沖池)里頭,把這個當(dāng)作緩存來用。然后使用后臺線程去做緩沖池和磁盤之間的同步。
    那么問題來了,如果還沒來的同步的時候宕機(jī)或斷電了怎么辦?還沒來得及執(zhí)行上面圖中紅色的操作。這樣會導(dǎo)致丟部分已提交事務(wù)的修改信息!
    所以引入了redo log來記錄已成功提交事務(wù)的修改信息,并且會把redo log持久化到磁盤,系統(tǒng)重啟之后在讀取redo log恢復(fù)最新數(shù)據(jù)。
    總結(jié):
    redo log是用來恢復(fù)數(shù)據(jù)的 用于保障,已提交事務(wù)的持久化特性

    2.undo log

    什么是 undo log ?

    undo log 叫做回滾日志,用于記錄數(shù)據(jù)被修改前的信息。他正好跟前面所說的重做日志所記錄的相反,重做日志記錄數(shù)據(jù)被修改后的信息。undo log主要記錄的是數(shù)據(jù)的邏輯變化,為了在發(fā)生錯誤時回滾之前的操作,需要將之前的操作都記錄下來,然后在發(fā)生錯誤時才可以回滾。
    還用上面那兩張表
    每次寫入數(shù)據(jù)或者修改數(shù)據(jù)之前都會把修改前的信息記錄到 undo log。

    undo log 有什么作用?

    undo log 記錄事務(wù)修改之前版本的數(shù)據(jù)信息,因此假如由于系統(tǒng)錯誤或者rollback操作而回滾的話可以根據(jù)undo log的信息來進(jìn)行回滾到?jīng)]被修改前的狀態(tài)。
    總結(jié):
    undo log是用來回滾數(shù)據(jù)的用于保障 未提交事務(wù)的原子性

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

    1. mysql鎖技術(shù)

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

    讀寫鎖

    解決上述問題很簡單,只需用兩種鎖的組合來對讀寫請求進(jìn)行控制即可,這兩種鎖被稱為:
    共享鎖(shared lock),又叫做"讀鎖"
    讀鎖是可以共享的,或者說多個讀請求可以共享一把鎖讀數(shù)據(jù),不會造成阻塞。
    排他鎖(exclusive lock),又叫做"寫鎖"
    寫鎖會排斥其他所有獲取鎖的請求,一直阻塞,直到寫入完成釋放鎖。
    總結(jié):通過讀寫鎖,可以做到讀讀可以并行,但是不能做到寫讀,寫寫并行 事務(wù)的隔離性就是根據(jù)讀寫鎖來實(shí)現(xiàn)的?。?!這個后面再說。

    2. MVCC基礎(chǔ)

    MVCC (MultiVersion Concurrency Control) 叫做多版本并發(fā)控制。
    InnoDB的 MVCC ,是通過在每行記錄的后面保存兩個隱藏的列來實(shí)現(xiàn)的。這兩個列, 一個保存了行的創(chuàng)建時間,一個保存了行的過期時間,當(dāng)然存儲的并不是實(shí)際的時間值,而是系統(tǒng)版本號。
    以上片段摘自《高性能Mysql》這本書對MVCC的定義。他的主要實(shí)現(xiàn)思想是通過數(shù)據(jù)多版本來做到讀寫分離。從而實(shí)現(xiàn)不加鎖讀進(jìn)而做到讀寫并行。
    MVCC在mysql中的實(shí)現(xiàn)依賴的是undo log與read view
    • undo log :undo log 中記錄某行數(shù)據(jù)的多個版本的數(shù)據(jù)。
    • read view :用來判斷當(dāng)前版本數(shù)據(jù)的可見性

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

    前面講的重做日志,回滾日志以及鎖技術(shù)就是實(shí)現(xiàn)事務(wù)的基礎(chǔ)。
    • 事務(wù)的原子性是通過 undo log 來實(shí)現(xiàn)的
    • 事務(wù)的持久性性是通過 redo log 來實(shí)現(xiàn)的
    • 事務(wù)的隔離性是通過 (讀寫鎖+MVCC)來實(shí)現(xiàn)的
    • 而事務(wù)的終極大 boss 一致性是通過原子性,持久性,隔離性來實(shí)現(xiàn)的?。?!
    原子性,持久性,隔離性折騰半天的目的也是為了保障數(shù)據(jù)的一致性!
    總之,ACID只是個概念,事務(wù)最終目的是要保障數(shù)據(jù)的可靠性,一致性。

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

    什么是原子性:
    一個事務(wù)必須被視為不可分割的最小工作單位,一個事務(wù)中的所有操作要么全部成功提交,要么全部失敗回滾,對于一個事務(wù)來說不可能只執(zhí)行其中的部分操作,這就是事務(wù)的原子性。
    上面這段話取自《高性能MySQL》這本書對原子性的定義,原子性可以概括為就是要實(shí)現(xiàn)要么全部失敗,要么全部成功。
    以上概念相信大家伙兒都了解,那么數(shù)據(jù)庫是怎么實(shí)現(xiàn)的呢?就是通過回滾操作。
    所謂回滾操作就是當(dāng)發(fā)生錯誤異常或者顯式的執(zhí)行rollback語句時需要把數(shù)據(jù)還原到原先的模樣,所以這時候就需要用到undo log來進(jìn)行回滾,接下來看一下undo log在實(shí)現(xiàn)事務(wù)原子性時怎么發(fā)揮作用的

    1.1 undo log 的生成

    假設(shè)有兩個表 bank和finance,表中原始數(shù)據(jù)如圖所示,當(dāng)進(jìn)行插入,刪除以及更新操作時生成的undo log如下面圖所示:
    從上圖可以了解到數(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等。
    思考:為什么先寫日志后寫數(shù)據(jù)庫?---稍后做解釋

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

    為了做到同時成功或者失敗,當(dāng)系統(tǒng)發(fā)生錯誤或者執(zhí)行rollback操作時需要根據(jù)undo log 進(jìn)行回滾
    回滾操作就是要還原到原來的狀態(tài),undo log記錄了數(shù)據(jù)被修改前的信息以及新增和被刪除的數(shù)據(jù)信息,根據(jù)undo log生成回滾語句,比如:
    • 如果在回滾日志里有新增數(shù)據(jù)記錄,則生成刪除該條的語句
    • 如果在回滾日志里有刪除數(shù)據(jù)記錄,則生成生成該條的語句
    • 如果在回滾日志里有修改數(shù)據(jù)記錄,則生成修改到原先數(shù)據(jù)的語句

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

    事務(wù)一旦提交,其所作做的修改會永久保存到數(shù)據(jù)庫中,此時即使系統(tǒng)崩潰修改的數(shù)據(jù)也不會丟失。
    先了解一下MySQL的數(shù)據(jù)存儲機(jī)制,MySQL的表數(shù)據(jù)是存放在磁盤上的,因此想要存取的時候都要經(jīng)歷磁盤IO,然而即使是使用SSD磁盤IO也是非常消耗性能的。
    為此,為了提升性能InnoDB提供了緩沖池(Buffer Pool),Buffer Pool中包含了磁盤數(shù)據(jù)頁的映射,可以當(dāng)做緩存來使用:
    • 讀數(shù)據(jù):會首先從緩沖池中讀取,如果緩沖池中沒有,則從磁盤讀取在放入緩沖池;
    • 寫數(shù)據(jù):會首先寫入緩沖池,緩沖池中的數(shù)據(jù)會定期同步到磁盤中;
    上面這種緩沖池的措施雖然在性能方面帶來了質(zhì)的飛躍,但是它也帶來了新的問題,當(dāng)MySQL系統(tǒng)宕機(jī),斷電的時候可能會丟數(shù)據(jù)?。?!
    因?yàn)槲覀兊臄?shù)據(jù)已經(jīng)提交了,但此時是在緩沖池里頭,還沒來得及在磁盤持久化,所以我們急需一種機(jī)制需要存一下已提交事務(wù)的數(shù)據(jù),為恢復(fù)數(shù)據(jù)使用。
    于是 redo log就派上用場了。下面看下redo log是什么時候產(chǎn)生的。
    事務(wù)開始之后就產(chǎn)生redo log,redo log的落盤并不是隨著事務(wù)的提交才寫入的,而是在事務(wù)的執(zhí)行過程中,便開始寫入redo log文件中。
    既然redo log也需要存儲,也涉及磁盤IO為啥還用它?
    • redo log 的存儲是順序存儲,而緩存同步是隨機(jī)操作。
    • 緩存同步是以數(shù)據(jù)頁為單位的,每次傳輸?shù)臄?shù)據(jù)大小大于redo log。

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

    隔離性是事務(wù)ACID特性里最復(fù)雜的一個。在SQL標(biāo)準(zhǔn)里定義了四種隔離級別,每一種級別都規(guī)定一個事務(wù)中的修改,哪些是事務(wù)之間可見的,哪些是不可見的。
    級別越低的隔離級別可以執(zhí)行越高的并發(fā),但同時實(shí)現(xiàn)復(fù)雜度以及開銷也越大。
    Mysql 隔離級別有以下四種(級別由低到高):
    • READ UNCOMMITED (未提交讀)
    • READ COMMITED (提交讀)
    • REPEATABLE READ (可重復(fù)讀)
    • SERIALIZABLE (可重復(fù)讀)
    只要徹底理解了隔離級別以及他的實(shí)現(xiàn)原理就相當(dāng)于理解了ACID里的隔離型。前面說過原子性,隔離性,持久性的目的都是為了要做到一致性,但隔離型跟其他兩個有所區(qū)別,原子性和持久性是為了要實(shí)現(xiàn)數(shù)據(jù)的可性保障靠,比如要做到宕機(jī)后的恢復(fù),以及錯誤后的回滾。
    那么隔離性是要做到什么呢?隔離性是要管理多個并發(fā)讀寫請求的訪問順序。這種順序包括串行或者是并行
    說明一點(diǎn),寫請求不僅僅是指insert操作,又包括update操作。
    總之,從隔離性的實(shí)現(xiàn)可以看出這是一場數(shù)據(jù)的可靠性與性能之間的權(quán)衡。
    • 可靠性性高的,并發(fā)性能低(比如 Serializable)
    • 可靠性低的,并發(fā)性能高(比如 Read Uncommited)

    READ UNCOMMITTED

    在READ UNCOMMITTED隔離級別下,事務(wù)中的修改即使還沒提交,對其他事務(wù)是可見的。事務(wù)可以讀取未提交的數(shù)據(jù),造成臟讀。
    因?yàn)樽x不會加任何鎖,所以寫操作在讀的過程中修改數(shù)據(jù),所以會造成臟讀。好處是可以提升并發(fā)處理性能,能做到讀寫并行。
    換句話說,讀的操作不能排斥寫請求。
    • 優(yōu)點(diǎn):讀寫并行,性能高
    • 缺點(diǎn):造成臟讀

    READ COMMITTED

    一個事務(wù)的修改在他提交之前的所有修改,對其他事務(wù)都是不可見的。其他事務(wù)能讀到已提交的修改變化。在很多場景下這種邏輯是可以接受的。
    InnoDB在 READ COMMITTED,使用排它鎖,讀取數(shù)據(jù)不加鎖而是使用了MVCC機(jī)制。或者換句話說他采用了讀寫分離機(jī)制。
    但是該級別會產(chǎn)生不可重讀以及幻讀問題。
    什么是不可重讀?
    在一個事務(wù)內(nèi)多次讀取的結(jié)果不一樣。
    為什么會產(chǎn)生不可重復(fù)讀?
    這跟 READ COMMITTED 級別下的MVCC機(jī)制有關(guān)系,在該隔離級別下每次 select的時候新生成一個版本號,所以每次select的時候讀的不是一個副本而是不同的副本。
    在每次select之間有其他事務(wù)更新了我們讀取的數(shù)據(jù)并提交了,那就出現(xiàn)了不可重復(fù)讀

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

    在一個事務(wù)內(nèi)的多次讀取的結(jié)果是一樣的。這種級別下可以避免,臟讀,不可重復(fù)讀等查詢問題。mysql 有兩種機(jī)制可以達(dá)到這種隔離級別的效果,分別是采用讀寫鎖以及MVCC。
    采用讀寫鎖實(shí)現(xiàn):
    為什么能可重復(fù)度?只要沒釋放讀鎖,在次讀的時候還是可以讀到第一次讀的數(shù)據(jù)。
    • 優(yōu)點(diǎn):實(shí)現(xiàn)起來簡單

    • 缺點(diǎn):無法做到讀寫并行

    采用MVCC實(shí)現(xiàn):
    為什么能可重復(fù)度?因?yàn)槎啻巫x取只生成一個版本,讀到的自然是相同數(shù)據(jù)。
    • 優(yōu)點(diǎn):讀寫并行
    • 缺點(diǎn):實(shí)現(xiàn)的復(fù)雜度高
    但是在該隔離級別下仍會存在幻讀的問題,關(guān)于幻讀的解決我打算另開一篇來介紹。

    SERIALIZABLE

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

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

    數(shù)據(jù)庫總是從一個一致性的狀態(tài)轉(zhuǎn)移到另一個一致性的狀態(tài).
    下面舉個例子:zhangsan 從銀行卡轉(zhuǎn)400到理財賬戶

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

    1.假如執(zhí)行完 update bank set balance = balance - 400;之發(fā)生異常了,銀行卡的錢也不能平白無辜的減少,而是回滾到最初狀態(tài)。
    2.又或者事務(wù)提交之后,緩沖池還沒同步到磁盤的時候宕機(jī)了,這也是不能接受的,應(yīng)該在重啟的時候恢復(fù)并持久化。
    3.假如有并發(fā)事務(wù)請求的時候也應(yīng)該做好事務(wù)之間的可見性問題,避免造成臟讀,不可重復(fù)讀,幻讀等。在涉及并發(fā)的情況下往往在性能和一致性之間做平衡,做一定的取舍,所以隔離性也是對一致性的一種破壞。

    總結(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)化思想有讀寫分離,讀讀并行,讀寫并行
    • 一致性:通過回滾,以及恢復(fù),和在并發(fā)環(huán)境下的隔離做到一致性。

    推薦閱讀:

    完全整理 | 365篇高質(zhì)技術(shù)文章目錄整理

    算法之美 : 棧和隊(duì)列

    主宰這個世界的10大算法

    徹底理解cookie、session、token

    淺談什么是遞歸算法

    專注服務(wù)器后臺技術(shù)棧知識總結(jié)分享

    歡迎關(guān)注交流共同進(jìn)步

    瀏覽 68
    點(diǎn)贊
    評論
    收藏
    分享

    手機(jī)掃一掃分享

    分享
    舉報
    評論
    圖片
    表情
    推薦
    點(diǎn)贊
    評論
    收藏
    分享

    手機(jī)掃一掃分享

    分享
    舉報

    <kbd id="5sdj3"></kbd>
    <th id="5sdj3"></th>

  • <dd id="5sdj3"><form id="5sdj3"></form></dd>
    <td id="5sdj3"><form id="5sdj3"><big id="5sdj3"></big></form></td><del id="5sdj3"></del>

  • <dd id="5sdj3"></dd>
    <dfn id="5sdj3"></dfn>
  • <th id="5sdj3"></th>
    <tfoot id="5sdj3"><menuitem id="5sdj3"></menuitem></tfoot>

  • <td id="5sdj3"><form id="5sdj3"><menu id="5sdj3"></menu></form></td>
  • <kbd id="5sdj3"><form id="5sdj3"></form></kbd>
    欧美三级电影在线 | 亚洲热视频 | 国产一级a毛一级a毛视频在线网站 | 操逼国产网站 | 自拍视频国产 |