<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ??碼云GVP開源項目 12k star Uniapp+ElementUI 功能強大 支持多語言、二開方便! 廣告
                [TOC] # 丟失數據 旺財是數據庫村的一個程序, 小強也是。 數據庫村有個特點, 很多數據支持共享操作,多個程序可以同時讀寫,他們倆經常會為了讀寫同一個數據, 爭奪的不可開交。 這一天,當旺財和小強對同一個銀行賬戶A進行寫操作時候, 出現了這么一個錯誤: ![](https://box.kancloud.cn/edb3a66651cb56d088833717f9385e75_233x292.png) 看看, 本來旺財要加上的20元就丟掉了。 同樣的事情發生的多了, 他倆給這種情況起了一個名字,叫“丟失修改”, 其實說白了就是倆人都去寫一個數據, 一個人的數據把另外一個給覆蓋了。 村里的MySQL老頭兒說: “你們兩個小家伙,寫數據的時候連加鎖都不做,肯定會出大亂子!" 旺財說:“加什么鎖?” “來來來, 我教你們一個排他鎖(Exclusive Lock) , 簡稱X鎖, 旺財你要寫數據了, 就把它用X鎖鎖住, 鎖住后,除非你釋放, 否則小強無法獲得X鎖。 這不就解決你們的問題了? ” 小強想了想, 就把上面的操作過程用X鎖改了一下: ![](https://box.kancloud.cn/62df3ac1828fac5810adc50b5410c972_236x453.png) 旺財說:“果然不錯, 確實可以解決兩個人同時修改導致的問題。” # 臟數據 小強說:“旺財, 我們約定,寫數據的時候都用X鎖吧?” 旺財說: “這沒問題, 可是X鎖只在寫數據的時候用, 我們讀數據是不用加鎖的, 我想起了一種情況, 你看看怎么辦?” ![](https://box.kancloud.cn/ac0a8fa1f09ca1ae089b9c1ecc6e230c_272x399.png) 小強在旺財執行的途中讀了A的值, 但是旺財把對A的修改給回滾(Rollback)了, 這下小強尷尬了, 他讀到了臟數據。 “要不我們在讀取數據的時候也加個X鎖 ? ” 小強說。 “那樣太嚴格了, 就是讀一個數據啊, 值得嗎?” “這樣吧, 我們再搞一個新的鎖出來, 專門用于共享數據的讀取, 就叫共享鎖(Share lock) ,簡稱S鎖, 這個鎖和之前的排他鎖X鎖有區別, 主要用于讀取數據, 如果一個數據加了X鎖, 就沒法加S鎖, 同樣加了S鎖, 就沒法加X鎖” 小強想出了一個點子。 “那如果我加了S鎖, 你還能加S鎖嗎? ” 旺財問。 “應該可以吧, 咱們倆都是讀數據, 互不影響啊。 還有為了防止長時間的鎖住, 我們可以約定一下,不管我們要做的事情有多少, 讀一個數據之前加S鎖, 讀完之后立刻釋放該S鎖 ! ” ![](https://box.kancloud.cn/03aa64e5d1299878da52bc8ae141cb86_264x475.png) 果然,這樣一來“臟數據”的問題就解決了 ! # 沒法重復讀? 旺財和小強兩個程序相安無事了很久, 但是S鎖在讀完數據后立刻釋放的約定, 導致出了一個新問題。 旺財在一次數據處理中, 先讀取了A和B的值, 相加得到了150 , 然后小強把B改成了30 旺財再次讀取A和B, 發現求和以后是130 , 剛才的不一樣了! 假定旺財的處理是在一個事務當中 ![](https://box.kancloud.cn/3f2888a815eb695a0483920555ad3266_266x730.png) 旺財說: “小強, 我在讀取數據的時候你不能改啊 , 要不然我這里會出現不一致, 你看剛開始是A+B是 150, 現在變成130了” 小強說: “我們之前的約定是讀數據時加S鎖, 讀完立馬釋放, 問題就出現在這里了。” “看來在讀數據的時候, 也需要一直鎖定了, 直到事務提交。” ![](https://box.kancloud.cn/48972d2f8d818a704862997a79159d6d_265x694.png) # 幻覺出現 旺財和小強現在已經能靈活的使用X鎖和S鎖了。 他們倆總結了一下, 分為了這么幾種情況: 1. 寫數據時加上X鎖,直到事務結束, 讀的時候不加鎖。 雖然能夠避免丟失數據, 但是可以讀到沒有提交或者回滾的內容 (臟數據), 這其實就是數據庫最低的事務隔離級別 --- Read uncommitted 2. 寫數據的時候加上X鎖, 直到事務結束, 讀的時候加上S鎖, 讀完數據立刻釋放。 這能避免“丟失數據”和“臟數據”, 但是會出現“不可重復讀”的問題 , 這是第二級的事務隔離級別 -- Read committed 3. 寫數據的時候加上X鎖, 直到事務結束, 讀數據的時候加S鎖, 也是直到事務結束。 這能避免“丟失數據”和“臟數據”, “不可重復讀”三個問題 , 這是數據庫常用的隔離級別 -- Repeatable read 整個世界似乎清凈了。 有一次旺財對一個“學生表”進行操作,選取了年齡是18歲的所有行, 用X鎖鎖住, 并且做了修改。 改完以后旺財再次選擇所有年齡是18歲的行, 想做一個確認, 沒想到有一行竟然沒有修改! 這是怎么回事? 出了幻覺嗎? 原來就在旺財查詢并修改的的時候, 小強也對學生表進行操作, 他插入了一個新的行,其中的年齡也是18歲! 雖然兩個人的修改都沒有問題, 互不影響, 但從最終效果看, 還是出了事。 正是小強的操作, 讓旺財出現了“幻讀” 旺財說: “沒轍了, 我們倆非得串行執行不可, 你必須得等我執行完。 ” 這就是數據庫事務隔離級別的終極大招:**Serializable (串行化)** 最后, 為了方便記憶, 他們倆倒騰了半天, 整出了一張表, 用于記錄各種情況: ![](https://box.kancloud.cn/e0119e2d110665f0febcda74c3068a17_443x93.png) 兩個人看著這張表, 感慨的說:“唉, 這數據庫村的事務隔離級別可真是不容易啊!” # MVCC 旺財和小強使用了一段時間的“串行化”隔離級別,雖然不會出錯,但是效率實在太低了。數據庫村的人都笑話他倆干活太慢, 于是倆人商量著退到“可重復讀”,雖然會出現幻讀,但是也能忍受。 “可重復讀”用了一段時間,他們又不滿意了。 旺財唉聲嘆氣地說:“為了實現可重復讀, 我們需要在事務中對讀操作加鎖,并且得持續到整個事務結束,這實在是不爽啊!” 小強說:“是啊,我修改數據的時候,還得等待你讀完成,效率就太低嘍。” 許久不見的MySQL聽到他倆的抱怨,插嘴道:“看來你們兩個已經開始思考了啊,我有一個辦法, 可以在讀的時候不用加鎖,也能實現可重復讀。” “你就吹吧!這怎么可能?” 旺財和小強根本不相信。 MySQL老頭兒說: “你們兩個太孤陋寡聞了,這個方法叫做MVCC(多版本并發控制)。” 頓了一下, MySQL老頭兒故意激他們:“可是有點難啊,你們倆不一定能弄明白。” 旺財和小強很不服氣:“說來聽聽!” “假設啊,數據庫中有一個叫做users的表,里邊有這么一行數據:” MySQL老頭兒開始畫圖: ![](https://box.kancloud.cn/99adf3e518756d9af7bcdbb6d4b234a0_194x99.png) “現在,我要給他加兩個隱藏的字段:” ![](https://box.kancloud.cn/a5ab275e581faae295ff7c8f6d0bad50_386x100.png) “事務ID? 是不是每次開始事務的時候分配的? ” “沒錯,這個事務ID就表明這一行數據是哪個事務操作的,注意啊,事務ID是一個遞增的數字,每次開始新事務,這個數字就會增加。” “這有什么用?” “別急,馬上就會講到,” MySQL老頭兒地說:“ 旺財,小強,假設你們倆的事務中SQL的執行次序如下: ” ![](https://box.kancloud.cn/370f814d1bedb9494d8139581fdf8ad0_467x640.png) 在標號為 (1) 的地方,數據是這樣的: ![](https://box.kancloud.cn/6b0d937067543190ac7c4858547440ab_387x100.png) 與此同時,需要建立一個叫做Read View的數據結構,它有三個部分: (1) 當前活躍的事務列表 ,即`[101,102]` (2) Tmin ,就是活躍事務的最小值, 在這里 Tmin = 101 (3) Tmax, 是系統中最大事務ID(不管事務是否提交)加上1。 在這里例子中,Tmax = 103 (注: 在可重復讀的隔離級別下,當第一個Read操作發生的時候,Read view就會建立。 在Read Committed隔離級別下,每次發出Read操作,都會建立新的Read view。) 旺財和小強還不知道有什么用處,只是死記硬背,生怕跟不上老頭兒的思路。 MySQL老頭兒接著說道: “在標號為(2)的地方,小強做了修改,數據是這樣的:” ![](https://box.kancloud.cn/91af08342b766ec72029131bd2d2f78e_499x249.png) “看到回滾指針沒有? 它指向了上一條記錄。” “怪不得叫做多版本并發控制,你這里維護了數據的多個版本啊。” 小強感慨道。 “按照可重復讀的要求,我開始了一個事務,無論我讀多少次,我總是能讀到age=20的那行記錄,即使小強修改了age,我也不受影響。你這個結構該怎么實現啊? ” 旺財問道。 “關鍵部分要到了,我這里有個算法,用來判斷這些數據版本記錄中哪些對你來說是可見的(可讀的)。 ” ![](https://box.kancloud.cn/91b3eb97fbf017d23b3646e4261b3e0f_546x772.png) 旺財只覺得覺得自己的頭嗡地一下就大了:“這....怎么這么麻煩!” MySQL老頭說:“這還麻煩? 已經很簡單的算法了,就是幾個if else ,加上幾個循環而已! 連這個都整不明白,別在我們數據庫村混了! 對于上面的例子,ReadView 中事務列表是`[101,102]`, Tmin= 101, Tmax = 103,你們想想,第一次讀和第二次讀是什么樣子。” 只聽到小強嘴里嘟囔著:“ 當旺財第一次讀的時候,只有一條記錄, tid = 100 ,小于Tmin,所以是可以讀的。 然后我做了修改, 當旺財第二次讀的時候,tid=102,程序走到了‘tid是否在Read View中這一分支,由于102確實在Read View的活動事務列表中,那就順著回滾指針找到下一行記錄,即tid為100那一行,再次判斷,這就和第一次讀一樣了。” MySQL老頭兒得意地說:“對嘍,這不就實現了可保證可重復讀嘛! 旺財你想想,你在讀數據的時候,需不需要加鎖操作?” 旺財搖頭:“不用加鎖, 我只要找到正確的版本就可以了。 ” (注: 但是在寫數據的時候,MySQL還是要加鎖的,防止寫-寫沖突) “這就是MVCC的好處啊,讀寫不互相等待,能極大地提高數據庫的并發能力啊。” 旺財還是有點不放心,覺得這種方式太復雜了,但是轉念一想,讀的時候不用加鎖,這個誘惑實在太大, 他說:“這樣吧,我和小強再合計合計。” MySQL老頭兒自信地說:“沒問題,你們來再想想,有問題再找我吧。” (注:本文講解了可重復讀的情況,對于Read Committed 這個可以適用同樣的算法,只是每次讀操作的時候,都要建立新的Read view,朋友們可自行分析下。)
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看