<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ù)總結(jié)篇,對線面試官

          共 4294字,需瀏覽 9分鐘

           ·

          2021-10-19 20:44

          此篇文章算是對mysql事務(wù)的一個總結(jié),基本把mysql事務(wù)相關(guān)的知識點都涵蓋到了,面試問來問去無非也就是這些,接下來咱們逐一總結(jié)


          事務(wù)的特點:ACID

          原子性(Atomicity)

          「定義」

          原子性的意思是指一個事務(wù)是一個無法分割的單位,事務(wù)中的操作要么都做,要么都不做;萬一在事務(wù)中一個sql語句執(zhí)行失敗了,那么已執(zhí)行的語句也必須回滾,數(shù)據(jù)庫退回到執(zhí)行事務(wù)之前的狀態(tài)。其實簡單來說,就是

          「實現(xiàn)原理」

          原子性實現(xiàn)的關(guān)鍵是依據(jù)日志undo log,它是實現(xiàn)原子性的最關(guān)鍵的部分,是當(dāng)事務(wù)回滾時能夠撤銷所有已經(jīng)成功執(zhí)行的sql語句。InnoDB實現(xiàn)回滾,靠的是undo log:當(dāng)事務(wù)對數(shù)據(jù)庫進行修改時,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)容做與之前相反的工作:對于每個insert,回滾時會執(zhí)行delete;對于每個delete,回滾時會執(zhí)行insert;對于每個update,回滾時會執(zhí)行一個相反的update,把數(shù)據(jù)改回去。

          以update操作為例:當(dāng)事務(wù)執(zhí)行update時,其生成的undo log中會包含被修改行的主鍵(以便知道修改了哪些行)、修改了哪些列、這些列在修改前后的值等信息,回滾時便可以使用這些信息將數(shù)據(jù)還原到update之前的狀態(tài)。

          從上圖可以了解到數(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的逆向為update等。回滾過程如圖

          tips:undo log也可以這么理解

          當(dāng)delete一條記錄時,undo?log中會記錄一條對應(yīng)的insert記錄?
          當(dāng)insert一條記錄時,undo?log中會記錄一條對應(yīng)的delete記錄
          當(dāng)update一條記錄時,它記錄一條對應(yīng)相反的update記錄

          tips:邏輯日志和物理日志的區(qū)別 看記日志的時候 是針對一行記錄,就是邏輯日志 如果是一個數(shù)據(jù)頁,就是物理日志

          持久性(Durability)

          「定義」

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

          「實現(xiàn)原理:Redo log(WAL ?write ahead log)」

          先了解一下MySQL的數(shù)據(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)宕機,斷電的時候可能會丟數(shù)據(jù)?。?!

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

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

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

          (1)刷臟是隨機IO,因為每次修改的數(shù)據(jù)位置隨機,但寫redo log是追加操作,屬于順序IO。

          (2)刷臟是以數(shù)據(jù)頁(Page)為單位的,MySQL默認(rèn)頁大小是16KB,一個Page上一個小修改都要整頁寫入;而redo log中只包含真正需要寫入的部分,無效IO大大減少。

          「redo log與binlog」

          我們知道,在MySQL中還存在binlog(二進制日志)也可以記錄寫操作并用于數(shù)據(jù)的恢復(fù),但二者是有著根本的不同的:

          (1)作用不同:redo log是用于crash recovery的,保證MySQL宕機也不會影響持久性;binlog是用于point-in-time recovery的,保證服務(wù)器可以基于時間點恢復(fù)數(shù)據(jù),此外binlog還用于主從復(fù)制。

          (2)層次不同:redo log是InnoDB存儲引擎實現(xiàn)的,而binlog是MySQL的服務(wù)器層(可以參考文章前面對MySQL邏輯架構(gòu)的介紹)實現(xiàn)的,同時支持InnoDB和其他存儲引擎。

          (3)內(nèi)容不同:redo log是物理日志,內(nèi)容基于磁盤的Page;binlog的內(nèi)容是二進制的,根據(jù)binlog_format參數(shù)的不同,可能基于sql語句、基于數(shù)據(jù)本身或者二者的混合。

          (4)寫入時機不同:binlog在事務(wù)提交時寫入;redo log的寫入時機相對多元:

          前面曾提到:當(dāng)事務(wù)提交時會調(diào)用fsync對redo log進行刷盤;這是默認(rèn)情況下的策略,修改innodb_flush_log_at_trx_commit參數(shù)可以改變該策略,但事務(wù)的持久性將無法保證。除了事務(wù)提交時,還有其他刷盤時機:如master thread每秒刷盤一次redo log等,這樣的好處是不一定要等到commit時刷盤,commit速度大大加快。

          隔離性(Isolation)

          「定義」

          與原子性、持久性側(cè)重于研究事務(wù)本身不同,隔離性研究的是不同事務(wù)之間的相互影響。隔離性是指,事務(wù)內(nèi)部的操作與其他事務(wù)是隔離的,并發(fā)執(zhí)行的各個事務(wù)之間不能互相干擾。嚴(yán)格的隔離性,對應(yīng)了事務(wù)隔離級別中的Serializable (可串行化),但實際應(yīng)用中出于性能方面的考慮很少會使用可串行化。

          「實現(xiàn)原理」

          隔離性追求的是并發(fā)情形下事務(wù)之間互不干擾。簡單起見,我們僅考慮最簡單的讀操作和寫操作(暫時不考慮帶鎖讀等特殊操作),那么隔離性的探討,主要可以分為兩個方面:

          (一個事務(wù))寫操作對(另一個事務(wù))寫操作的影響:鎖機制保證隔離性 (一個事務(wù))寫操作對(另一個事務(wù))讀操作的影響:MVCC保證隔離性

          「臟讀、不可重復(fù)讀和幻讀」

          首先來看并發(fā)情況下,讀操作可能存在的三類問題:

          • 臟讀:當(dāng)前事務(wù)(A)中可以讀到其他事務(wù)(B)未提交的數(shù)據(jù)(臟數(shù)據(jù)),這種現(xiàn)象是臟讀。舉例如下(以賬戶余額表為例)

          • 不可重復(fù)讀:在事務(wù)A中先后兩次讀取同一個數(shù)據(jù),兩次讀取的結(jié)果不一樣,這種現(xiàn)象稱為不可重復(fù)讀。臟讀與不可重復(fù)讀的區(qū)別在于:前者讀到的是其他事務(wù)未提交的數(shù)據(jù),后者讀到的是其他事務(wù)已提交的數(shù)據(jù)。舉例如下:

          • 幻讀:在事務(wù)A中按照某個條件先后兩次查詢數(shù)據(jù)庫,兩次查詢結(jié)果的條數(shù)不同,這種現(xiàn)象稱為幻讀。不可重復(fù)讀與幻讀的區(qū)別可以通俗的理解為:前者是數(shù)據(jù)變了,后者是數(shù)據(jù)的行數(shù)變了。舉例如下

          「事務(wù)隔離級別」

          在實際應(yīng)用中,讀未提交在并發(fā)時會導(dǎo)致很多問題,而性能相對于其他隔離級別提高卻很有限,因此使用較少??纱谢瘡娭剖聞?wù)串行,并發(fā)效率很低,只有當(dāng)對數(shù)據(jù)一致性要求極高且可以接受沒有并發(fā)時使用,因此使用也較少。因此在大多數(shù)數(shù)據(jù)庫系統(tǒng)中,默認(rèn)的隔離級別是讀已提交(如Oracle)或可重復(fù)讀(后文簡稱RR)。可以通過如下兩個命令分別查看隔離級別:

          select?@@tx_isolation;
          +-----------------+
          |?@@tx_isolation??|
          +-----------------+
          |?REPEATABLE-READ?|
          +-----------------+
          1?row?in?set?(0.00?sec)

          「MVCC」

          RR解決臟讀、不可重復(fù)讀、幻讀等問題,使用的是MVCC:MVCC全稱Multi-Version Concurrency Control,即多版本的并發(fā)控制協(xié)議。下面的例子很好的體現(xiàn)了MVCC的特點:在同一時刻,不同的事務(wù)讀取到的數(shù)據(jù)可能是不同的(即多版本)——在T5時刻,事務(wù)A和事務(wù)C可以讀取到不同版本的數(shù)據(jù)。

          MVCC最大的優(yōu)點是讀不加鎖,因此讀寫不沖突,并發(fā)性能好。InnoDB實現(xiàn)MVCC,多個版本的數(shù)據(jù)可以共存,主要是依靠數(shù)據(jù)的隱藏列(也可以稱之為標(biāo)記位)和undo log。其中數(shù)據(jù)的隱藏列包括了該行數(shù)據(jù)的版本號、刪除時間、指向undo log的指針等等;當(dāng)讀取數(shù)據(jù)時,MySQL可以通過隱藏列判斷是否需要回滾并找到回滾需要的undo log,從而實現(xiàn)MVCC;隱藏列的詳細(xì)格式不再展開。

          下面結(jié)合前文提到的幾個問題分別說明「臟讀」

          當(dāng)事務(wù)A在T3時間節(jié)點讀取zhangsan的余額時,會發(fā)現(xiàn)數(shù)據(jù)已被其他事務(wù)修改,且狀態(tài)為未提交。此時事務(wù)A讀取最新數(shù)據(jù)后,根據(jù)數(shù)據(jù)的undo log執(zhí)行回滾操作,得到事務(wù)B修改前的數(shù)據(jù),從而避免了臟讀。

          「不可重復(fù)讀」

          當(dāng)事務(wù)A在T2節(jié)點第一次讀取數(shù)據(jù)時,會記錄該數(shù)據(jù)的版本號(數(shù)據(jù)的版本號是以row為單位記錄的),假設(shè)版本號為1;當(dāng)事務(wù)B提交時,該行記錄的版本號增加,假設(shè)版本號為2;當(dāng)事務(wù)A在T5再一次讀取數(shù)據(jù)時,發(fā)現(xiàn)數(shù)據(jù)的版本號(2)大于第一次讀取時記錄的版本號(1),因此會根據(jù)undo log執(zhí)行回滾操作,得到版本號為1時的數(shù)據(jù),從而實現(xiàn)了可重復(fù)讀。

          「幻讀」

          InnoDB實現(xiàn)的RR通過next-key lock機制避免了幻讀現(xiàn)象。

          next-key lock是行鎖的一種,實現(xiàn)相當(dāng)于record lock(記錄鎖) + gap lock(間隙鎖);其特點是不僅會鎖住記錄本身(record lock的功能),還會鎖定一個范圍(gap lock的功能)。當(dāng)然,這里我們討論的是不加鎖讀:此時的next-key lock并不是真的加鎖,只是為讀取的數(shù)據(jù)增加了標(biāo)記(標(biāo)記內(nèi)容包括數(shù)據(jù)的版本號等);準(zhǔn)確起見姑且稱之為類next-key lock機制。還是以前面的例子來說明:

          當(dāng)事務(wù)A在T2節(jié)點第一次讀取0

          總結(jié)

          概括來說,InnoDB實現(xiàn)的RR,通過鎖機制、數(shù)據(jù)的隱藏列、undo log和類next-key lock,實現(xiàn)了一定程度的隔離性,可以滿足大多數(shù)場景的需要。不過需要說明的是,RR雖然避免了幻讀問題,但是畢竟不是Serializable,不能保證完全的隔離,下面是一個例子,大家可以自己驗證一下。

          一致性

          基本概念

          一致性是指事務(wù)執(zhí)行結(jié)束后,數(shù)據(jù)庫的完整性約束沒有被破壞,事務(wù)執(zhí)行的前后都是合法的數(shù)據(jù)狀態(tài)。數(shù)據(jù)庫的完整性約束包括但不限于:實體完整性(如行的主鍵存在且唯一)、列完整性(如字段的類型、大小、長度要符合要求)、外鍵約束、用戶自定義完整性(如轉(zhuǎn)賬前后,兩個賬戶余額的和應(yīng)該不變)。

          實現(xiàn)

          可以說,一致性是事務(wù)追求的最終目標(biāo):前面提到的原子性、持久性和隔離性,都是為了保證數(shù)據(jù)庫狀態(tài)的一致性。此外,除了數(shù)據(jù)庫層面的保障,一致性的實現(xiàn)也需要應(yīng)用層面進行保障。

          實現(xiàn)一致性的措施包括:

          • 保證原子性、持久性和隔離性,如果這些特性無法保證,事務(wù)的一致性也無法保證
          • 數(shù)據(jù)庫本身提供保障,例如不允許向整形列插入字符串值、字符串長度不能超過列的限制等
          • 應(yīng)用層面進行保障,例如如果轉(zhuǎn)賬操作只扣除轉(zhuǎn)賬者的余額,而沒有增加接收者的余額,無論數(shù)據(jù)庫實現(xiàn)的多么完美,也無法保證狀態(tài)的一致


          ??


          瀏覽 31
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <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>
                  国产又爽 又黄 免费观看视频 | 亚洲色亭亭亭 | 无码影院在线观看 | 欧美日韩国产免费观看 | 亚洲成人影片 |