1. MySql系列:MySQL 到底是怎么解決幻讀的?

        共 3115字,需瀏覽 7分鐘

         ·

        2021-08-19 12:03

        前言

        尋找光的過程,就是向上爬的過程

        純真的【蘇醒】、海上的【告別】、傍晚的【遺憾】、山中的【約定】、平凡的【妥協(xié)】、自我的【同行】、美好的【相遇】、社恐的【停頓】、表達(dá)的【傾聽】、勇敢的【探索】,最終發(fā)現(xiàn),【愛】是霧中的燈塔,指引著出海時的彼岸,也是向往的歸宿。所以,“幼鳥”們,當(dāng)落定所有聲色,也別忘記哼著歌?!坐B指南

        什么是幻讀?在一次事務(wù)里面,多次查詢之后,結(jié)果集的個數(shù)不一致的情況叫做幻讀。而多出來或者少的哪一行被叫做幻行。

        為什么要解決幻讀?在高并發(fā)數(shù)據(jù)庫系統(tǒng)中,需要保證事務(wù)與事務(wù)之間的隔離性,還有事務(wù)本身的一致性。

        MySQL 是如何解決幻讀的?如果你看到了這篇文章,那么我會默認(rèn)你了解了臟讀 、不可重復(fù)讀與可重復(fù)讀。


        ??什么是幻讀,臟讀,不可重復(fù)讀呢?

        • 事務(wù) A、B 交替執(zhí)行,事務(wù) A 被事務(wù) B 干擾到了,因為事務(wù) A 讀取到事務(wù) B 未提交的數(shù)據(jù),這就是臟讀。

        • 在一個事務(wù)范圍內(nèi),兩個相同的查詢,讀取同一條記錄,卻返回了不同的數(shù)據(jù),這就是不可重復(fù)讀。

        • 事務(wù) A 查詢一個范圍的結(jié)果集,另一個并發(fā)事務(wù) B 往這個范圍中插入 / 刪除了數(shù)據(jù),并靜悄悄地提交,然后事務(wù) A 再次查詢相同的范圍,兩次讀取得到的結(jié)果集不一樣了,這就是幻讀。


        ??不可重復(fù)讀(鎖行即可解決)和幻讀的區(qū)別(需要鎖表解決)

        很多人容易搞混不可重復(fù)讀(不可重復(fù)讀重點是在 update,即事務(wù)前后對比特定數(shù)據(jù)內(nèi)容的修改;而幻讀是 insertdelete,即事務(wù)前后數(shù)據(jù)條數(shù)的對比)和幻讀,確實這兩者有些相似。

        所以說不可重復(fù)讀和幻讀最大的區(qū)別,就在于如何通過鎖機制來解決他們產(chǎn)生的問題。

        但是 MySQL、ORACLE、PostgreSQL 等成熟的數(shù)據(jù)庫,出于性能考慮,都是使用以樂觀鎖為理論基礎(chǔ)的 MVCC(多版本并發(fā)控制) 來避免這兩種問題。


        ??悲觀鎖和樂觀鎖

        傳送門:樂觀鎖和悲觀鎖

        • 悲觀鎖

        正如其名,它指的是對數(shù)據(jù)被外界(包括本系統(tǒng)當(dāng)前的其他事務(wù),以及來自外部系統(tǒng)的事務(wù)處理)修改持保守態(tài)度,因此,在整個數(shù)據(jù)處理過程中,將數(shù)據(jù)處 于鎖定狀態(tài)。悲觀鎖的實現(xiàn),往往依靠數(shù)據(jù)庫提供的鎖機制(也只有數(shù)據(jù)庫層提供的鎖機制才能真正保證數(shù)據(jù)訪問的排他性,否則,即使在本系統(tǒng)中實現(xiàn)了加鎖機 制,也無法保證外部系統(tǒng)不會修改數(shù)據(jù))。

        在悲觀鎖的情況下,為了保證事務(wù)的隔離性,就需要一致性鎖定讀。讀取數(shù)據(jù)時給加鎖,其它事務(wù)無法修改這些數(shù)據(jù)。修改刪除數(shù)據(jù)時也要加鎖,其它事務(wù)無法讀取這些數(shù)據(jù)。

        • 樂觀鎖

        相對悲觀鎖而言,樂觀鎖機制采取了更加寬松的加鎖機制。悲觀鎖大多數(shù)情況下依靠數(shù)據(jù)庫的鎖機制實現(xiàn),以保證操作最大程度的獨占性。但隨之而來的就是數(shù)據(jù)庫性能的大量開銷,特別是對長事務(wù)而言,這樣的開銷往往無法承受。

        而樂觀鎖機制在一定程度上解決了這個問題。樂觀鎖,大多是基于數(shù)據(jù)版本( Version )記錄機制實現(xiàn)。何謂數(shù)據(jù)版本?即為數(shù)據(jù)增加一個版本標(biāo)識,在基于數(shù)據(jù)庫表的版本解決方案中,一般是通過為數(shù)據(jù)庫表增加一個 “version” 字段來實現(xiàn)。讀取出數(shù)據(jù)時,將此版本號一同讀出,之后更新時,對此版本號加一。此時,將提交數(shù)據(jù)的版本數(shù)據(jù)與數(shù)據(jù)庫表對應(yīng)記錄的當(dāng)前版本信息進(jìn)行比對,如 果提交的數(shù)據(jù)版本號大于數(shù)據(jù)庫表當(dāng)前版本號,則予以更新,否則認(rèn)為是過期數(shù)據(jù)。

        要說明的是,MVCC的實現(xiàn)沒有固定的規(guī)范,每個數(shù)據(jù)庫都會有不同的實現(xiàn)方式,這里討論的是InnoDB的MVCC。


        ??1. 多版本并發(fā)控制(MVCC)(快照讀/一致性讀)

        多數(shù)數(shù)據(jù)庫都實現(xiàn)了多版本并發(fā)控制,并且都是靠保存數(shù)據(jù)快照來實現(xiàn)的。以 InnoDB 為例,每一行中都冗余了兩個字?jǐn)唷?/strong>

        一個是行的創(chuàng)建版本,一個是行的刪除(過期)版本。具體的版本號(trx_id)存在  information_schema.INNODB_TRX 表中。版本號(trx_id)隨著每次事務(wù)的開啟自增。

        事務(wù)每次取數(shù)據(jù)的時候都會取創(chuàng)建版本小于當(dāng)前事務(wù)版本的數(shù)據(jù),以及過期版本大于當(dāng)前版本的數(shù)據(jù)。

        普通的 select 就是快照讀。

        SELECT id FROM T WHERE number = 1;
        復(fù)制代碼

        原理:將歷史數(shù)據(jù)存一份快照,所以其他事務(wù)增加與刪除數(shù)據(jù),對于當(dāng)前事務(wù)來說是不可見的。


        ??2. next-key 鎖 (當(dāng)前讀)

        next-key 鎖包含兩部分:

        • 記錄鎖(行鎖)

        • 間隙鎖

        記錄鎖是加在索引上的鎖,間隙鎖是加在索引之間的。(思考:如果列上沒有索引會發(fā)生什么?)

        SELECT id FROM T WHERE number = 1 for update;
        SELECT id FROM T WHERE number = 1 lock in share mode;
        insert
        update
        delete
        復(fù)制代碼

        原理:將當(dāng)前數(shù)據(jù)行與上一條數(shù)據(jù)和下一條數(shù)據(jù)之間的間隙鎖定,保證此范圍內(nèi)讀取的數(shù)據(jù)是一致的。


        ??其他:MySQL InnoDB 引擎 RR 隔離級別是否解決了幻讀

        github 上面的評論 地址:

        Mysql官方給出的幻讀解釋是:只要在一個事務(wù)中,第二次select多出了row就算幻讀。
        a事務(wù)先select,b事務(wù)insert確實會加一個gap鎖,但是如果b事務(wù)commit,這個gap鎖就會釋放(釋放后a事務(wù)可以隨意dml操作),a事務(wù)再select出來的結(jié)果在MVCC下還和第一次select一樣,接著a事務(wù)不加條件地update,這個update會作用在所有行上(包括b事務(wù)新加的),a事務(wù)再次select就會出現(xiàn)b事務(wù)中的新行,并且這個新行已經(jīng)被update修改了,實測在RR級別下確實如此。
        如果這樣理解的話,Mysql的RR級別確實防不住幻讀

        有道友回復(fù) 地址:

        在快照讀讀情況下,mysql通過mvcc來避免幻讀。在當(dāng)前讀讀情況下,mysql通過next-key來避免幻讀。select * from t where a=1;屬于快照讀 select * from t where a=1 lock in share mode;屬于當(dāng)前讀 不能把快照讀和當(dāng)前讀得到的結(jié)果不一樣這種情況認(rèn)為是幻讀,這是兩種不同的使用。所以我認(rèn)為mysql的rr級別是解決了幻讀的。

        先說結(jié)論,MySQL 存儲引擎 InnoDB 隔離級別 RR 解決了幻讀問題。面試問爛的 MySQL 四種隔離級別,建議大家多了解多學(xué)習(xí)。

        如引用一問題所說,T1 select 之后 update,會將 T2insert 的數(shù)據(jù)一起更新,那么認(rèn)為多出來一行,所以防不住幻讀??粗f法無懈可擊,但是其實是錯誤的,InnoDB 中設(shè)置了快照讀和當(dāng)前讀兩種模式,如果只有快照讀,那么自然沒有幻讀問題,但是如果將語句提升到當(dāng)前讀,那么 T1select 的時候需要用如下語法:select * from t for update (lock in share mode)  進(jìn)入當(dāng)前讀,那么自然沒有 T2 可以插入數(shù)據(jù)這一回事兒了。


        ??注意

        next-key 固然很好的解決了幻讀問題,但是還是遵循一般的定律,隔離級別越高,并發(fā)越低。


        作者:Sunny_Chen
        鏈接:https://juejin.cn/post/6996081766037471239
        來源:掘金
        著作權(quán)歸作者所有。商業(yè)轉(zhuǎn)載請聯(lián)系作者獲得授權(quán),非商業(yè)轉(zhuǎn)載請注明出處。



        瀏覽 77
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

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

        手機掃一掃分享

        分享
        舉報
          
          

            1. www.黄色视频在线 | 超碰人妻无码 | 啊~嗯隔着内裤进去里做h男男 | AV偷拍 | 欧美日韩毛 |