1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        不知道MYSQL怎么控制并發(fā)數(shù)據(jù)的讀取,怎么辦?

        共 5198字,需瀏覽 11分鐘

         ·

        2020-09-27 17:56


        點擊上方「藍字」關(guān)注我們

        數(shù)據(jù)隔離是怎么實現(xiàn)的

        ?

        注意:本次數(shù)據(jù)隔離是建立在可重復讀的場景下

        ?

        在可重復讀的場景下,我們了解每次啟動事務的時候,會在當前啟動一個視圖,而這個視圖是整個數(shù)據(jù)庫的視圖快照。

        嘿嘿,是不是想數(shù)據(jù)庫那么大,為啥我們沒有感覺到創(chuàng)建快照時間的消耗呢?

        這是因為數(shù)據(jù)庫創(chuàng)建的視圖快照利用了「所有數(shù)據(jù)都有多個版本的特性,來實現(xiàn)快速創(chuàng)建視圖快照的能力」

        那數(shù)據(jù)多個版本是怎么回事呢?

        準備下數(shù)據(jù)

        先別急,我們準備下數(shù)據(jù)。

        現(xiàn)在創(chuàng)建一個表,并且插入三條數(shù)據(jù)。

        create?table?scores
        (
        ????id????int???not?null
        ????????primary?key,
        ????score?float?null
        );
        INSERT?INTO?scores?(id,?score)?VALUES?(1,?3.5);
        INSERT?INTO?scores?(id,?score)?VALUES?(2,?3.65);
        INSERT?INTO?scores?(id,?Score)?VALUES?(3,?4);

        在開始使用前我們要了解兩個小知識點。begin/start transaction 與 start transaction with consistent snapshot。

        • begin/start transaction 視圖的創(chuàng)建是建立在begin/ start transaction 之后SQL語句才會創(chuàng)建視圖, 比如 下面案例

          begin
          select?source?from?scores;??//視圖是在這里開始創(chuàng)建?而不是在begin那里創(chuàng)建
          commit
        • start transaction with consistent snapshot:則是該語句執(zhí)行后,就創(chuàng)建視圖。

        了解上面兩個創(chuàng)建事務的區(qū)別后,我們來看下視圖是怎么創(chuàng)建出來多個數(shù)據(jù)版本的. 以下SQL在兩個窗口打開。

        事務A事務B結(jié)果
        start transaction with consistent snapshot開啟事務,并創(chuàng)建視圖
        --start transaction with consistent snapshot開啟事務,并創(chuàng)建視圖
        select score from scors where id =2--事務A中的值為3.65
        --update scores set scores = 10 where id =2事務B修改為10
        --select score from scores where id =2事務B顯示為10
        select score from scores where id =2--事務A顯示為3.65
        select score from scores where id =2 for update--會被鎖住,等待事務B釋放鎖(間隙鎖)
        --commit提交事務B
        select score from scores where id =2 for update--這個語句可以看到變成了10(利用了當前讀)
        select score from scores where id =2--不加 for update 那么結(jié)果還是3.65
        commit------

        上述流程就是兩個不同的請求過來,對數(shù)據(jù)庫同一個表的不同操作。

        當事務A執(zhí)行start transaction with consistent snapshot之后,A的視圖就開始被創(chuàng)建了,這時候是看不到事務B對其中的修改,就算事務Bcommit之后,只要事務A不結(jié)束,它看到的結(jié)果就是它啟動時刻的值。

        「這就與不重復提交,執(zhí)行過程中看到的結(jié)果與啟動的時候看到的結(jié)果是一致的這句話對應上了」。

        快照多版本

        前面說了,快照是事務的啟動的時候是基于整個數(shù)據(jù)庫的,而整個數(shù)據(jù)庫是很大,那MYSQL是怎么讓我們無感并快速創(chuàng)建一個快照呢。

        快照多版本你可以認為是由以下兩部分構(gòu)成。

        • 事務id(transaction id):這個是由事務啟動的時候向InnoDB啟動時申請的。并且一定注意哦它是遞增的。
        • row trx_id:這個id其實就是事務ID,每次事務更新數(shù)據(jù)的時候回將事務ID賦值給這個數(shù)據(jù)版本的事務ID上,將這個數(shù)據(jù)版本的事務ID稱為 row trx_id.

        當一行記錄存在多個數(shù)據(jù)版本的時候,那么就有多個row trx_id 。舉個例子

        版本事務ID對應的語句操作
        v1score =389--
        v2score =590update scores set score = 5 where id =3; select score from scores where id =3;
        v3score = 691update scores set score = 6 where id =3;

        v1->v2->v3 這里面涉及了三個版本的迭代。中間是通過undo log 日志來保存更新的記錄的。

        注意啟動快照之后,可重復讀隔離情況下,獲取到v1的值,不是說MYSQL直接存儲的該值,而是利用現(xiàn)在這條記錄的最新版本與undo log日志計算出來的,比如通過v3 ->v2—>v1 計算出v1中score值。

        版本圖

        版本計算

        上面簡單說了下版本的計算規(guī)則,但是在MYSQL中,版本并不是那么簡單的計算的,我們現(xiàn)在來看下到底怎么計算的,

        這個兩點我們在注意一下:

        • 事務在啟動的時候會向InnoDB的事務系統(tǒng)申請事務ID,這個事務ID是嚴格遞增的。
        • 每行數(shù)據(jù)是多個版本,這個版本的id就是row trx_id,而事務「更新數(shù)據(jù)」(更新數(shù)據(jù)的時候才會生成一個新的版本)的時候會生成一個新的數(shù)據(jù)版本,并把事務ID賦值給這個數(shù)據(jù)的事務ID==row trx_id,
        1. 事務啟動的時候,能看到所有已經(jīng)提交事務的結(jié)果,但是他啟動之后,其他事務的變更是看不到的。

        2. 當事務啟動的瞬間,除了已經(jīng)提交的事務,創(chuàng)建的瞬間還會存在正在運行的事務,MYSQL是把這些正在運行的事務ID放入到一個數(shù)組中。「數(shù)組中最小的事務ID」記為低水位,當前系統(tǒng)中「創(chuàng)建過的事務ID最大值+1」記為高水位。

          ?

          舉個簡單的例子。a. 注意一點:獲取事務ID與創(chuàng)建數(shù)組不是一個原子操作,所以存在事務id為8,然后又存在當前MYSQL中存在活躍事務ID為9 10的事務。

          ?

          b. 事務ID低于低水位那么對于當前事務肯定是可見的,事務ID高于高水位的事務ID值,則對當前事務不可見. c. 事務ID 位于低水位與高水位之間分為兩種情況。

          • 如果事務id是在活躍的數(shù)組中表示這個版本是正在執(zhí)行,但是結(jié)果還沒有提交,所以這些事務的變更是不會讓當然事務看到的。
          • 事務id如果沒有在活躍數(shù)組中,代表這個事務是已經(jīng)提交了,所以可見。比如現(xiàn)在創(chuàng)建了90,91,92三個事務,91執(zhí)行的比較快,提交完畢,90和92還沒有提交.這時候創(chuàng)建了一個新的事務id為93,那么在活躍的數(shù)組中的事務就是90,92,93,你看91是已經(jīng)提交了,它的事務還在這個低水位與高水位之間,但結(jié)果對于93是可見。

        總的上面來說就是你在我創(chuàng)建的時候事務結(jié)果已經(jīng)提交,那么是可見的,之后提交那么就是不可見的。

        讀取流程

        上面簡單說了下老版本視圖中的數(shù)據(jù)是通過最新的版本與undo log 計算出來的,那到底怎么就算的呢?

        事務A事務B結(jié)果
        start transaction with consistent snapshot ?事務 id 89開啟事務,并創(chuàng)建視圖
        --start transaction with consistent snapshot ?事務id 92開啟事務,并創(chuàng)建視圖
        select score from scors where id =2--事務A中的值為3.65
        --update scores set scores = 10 where id =2事務B修改為10
        --select score from scores where id =2事務B顯示為10
        select score from scores where id =2--事務A顯示為3.65
        commit------

        還是看這個事務操作。下面是數(shù)據(jù)變動的流程。

        • 假設開始之前有兩個活躍的事務ID為 78,88.
        • 事務A啟動的時候會將78 88,包含它自己放入到活躍數(shù)組中。
        • 事務A 操作的語句select score from scors where id =2將其看到的結(jié)果認為是v1版本數(shù)據(jù)比如其現(xiàn)在row trx_id(**注意:**row trx_id是數(shù)據(jù)行被更新后事務id才會賦值給row trx id上)是86,并且保存好。
        • 事務B啟動時,會發(fā)現(xiàn)在活躍數(shù)組是78,88,89,自己的92.
        • 事務B 執(zhí)行更新語句語句后,會生成一個新的版本V2,數(shù)據(jù)變換就是V1-->V2。記錄中間變化的是「undo log」日志。這樣ID 89存儲的數(shù)據(jù)就變成了歷史數(shù)據(jù)。數(shù)據(jù)版本row trx_id則是92
        • 事務A 查詢score數(shù)據(jù),就會通過先查到現(xiàn)在的V2版本視圖,找到對應的row trx_id = 92,發(fā)現(xiàn)row trx_id 位于高水位上,則拋棄這個值,通過V2找到V1,row trx_id為86,而86大于「低水位」,而低于「高水位」89+1.但是由于86沒有在活躍數(shù)組中,而且屬于已經(jīng)提交的事務,則當前事務是能看到該結(jié)果的,所以事務A能拿到讀取的值。

        你看經(jīng)過簡單的幾步,我們就拿到了想要讀取的事務數(shù)據(jù),所以不論事務A什么時候查詢,它拿到的結(jié)果都是跟它讀取的數(shù)據(jù)是一致的。

        你看有了MVCC(多版本并發(fā)控制)計算別的事務更改了值也不會影響到當前事務讀取結(jié)果的過程。

        我們經(jīng)常說不要寫一個長事務,通過上面的讀取流程可以看到,長事務存在時間長的話,數(shù)據(jù)版本就會有很多,那么undo log日志就需要保存好久,這些回滾日志會占用大量的「內(nèi)存」存儲空間。

        當沒有事務需要讀取該日志與版本數(shù)據(jù)的時候,這個日志才可以刪除,從而釋放內(nèi)存空間。

        更新流程

        事務A事務B結(jié)果
        start transaction with consistent snapshot ?事務 id 89開啟事務,并創(chuàng)建視圖
        --start transaction with consistent snapshot ?事務id 92開啟事務,并創(chuàng)建視圖
        select score from scors where id =2--事務A中的值為3.65
        --update scores set scores = 10 where id =2事務B修改為10
        --select score from scores where id =2事務B顯示為10
        select score from scores where id =2--事務A顯示為3.65
        select score from scores where id =2 for update--會被鎖住,等待事務B釋放鎖(間隙鎖)
        --commit提交事務B
        select score from scores where id =2 for update--這個語句可以看到變成了10(利用了當前讀)
        select score from scores where id =2--不加 for update 那么結(jié)果還是3.65
        commit------

        上面說了讀取的過程,其實在事務中,我們還有更新流程,更新流程比較簡單,更新過程我們需要保證數(shù)據(jù)的一致性,不能說別人修改了,我們還看不到,那樣就會造成數(shù)據(jù)的不一致。

        為了保證看到最新的數(shù)據(jù),會對更新行的操作加鎖(行鎖),加鎖之后,其他事務對行進行更新操作,必須等待其他事務commit之后才能獲取到最新的值,這個過程被稱為「當前讀」。

        想要讀取過程中獲得最新的值可以使用 上面的語句select score from scores where id =2 for update ,就可以看到當前最新值。

        總結(jié)

        本小節(jié)主要梳理了事務的隔離級別,事務的MVCC多版本并發(fā)控制實現(xiàn)原理。

        事務在面試中是比較多的一個點,這樣的題目可以多種變換,面試官:說說MySQL的事務隔離?提到的三個問題已經(jīng)可以解答了。

        你來嘗試回答下?

        下期會說下數(shù)據(jù)庫中的幻讀,幻讀也是面試中經(jīng)常遇到的問題哦。

        你點的每個在看,我都認真當成了喜歡
        瀏覽 46
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

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

        手機掃一掃分享

        分享
        舉報
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            做爱免费在线观看 | 香蕉91视频在线观看大全 | 女人18毛片水真多免费 | 丁香五月色情综合激 | 国产无遮挡裸体免费视频在线观看 | 操逼的视频网站 | 国产成人aaaa | 五月丁香婷中文字幕 | 欧美午夜片欧美片在线观看 | 伊人导航 |