<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>

                ThinkChat2.0新版上線,更智能更精彩,支持會話、畫圖、視頻、閱讀、搜索等,送10W Token,即刻開啟你的AI之旅 廣告
                [TOC] ## 什么是MVCC 全稱Multi-Version Concurrency Control,即`多版本并發控制`,主要是為了提高數據庫的`并發性能`。以下文章都是圍繞InnoDB引擎來講,因為myIsam不支持事務。 同一行數據平時發生讀寫請求時,會`上鎖阻塞`住。但mvcc用更好的方式去處理讀—寫請求,做到在發生讀—寫請求沖突時`不用加鎖`。 這個讀是指的`快照讀`,而不是`當前讀`,當前讀是一種加鎖操作,是`悲觀鎖`。 那它到底是怎么做到讀—寫`不用加鎖`的,`快照讀`和`當前讀`又是什么鬼,跟著你們的`貼心老哥`,繼續往下看。 ![](https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/d05911f83aa34acea5ea2ff47cc42232~tplv-k3u1fbpfcp-zoom-1.image) ## 當前讀、快照讀都是什么鬼 什么是MySQL InnoDB下的當前讀和快照讀? ### 當前讀 它讀取的數據庫記錄,都是`當前最新`的`版本`,會對當前讀取的數據進行`加鎖`,防止其他事務修改數據。是`悲觀鎖`的一種操作。 如下操作都是當前讀: * select lock in share mode (共享鎖) * select for update (排他鎖) * update (排他鎖) * insert (排他鎖) * delete (排他鎖) * 串行化事務隔離級別 ### 快照讀 快照讀的實現是基于`多版本`并發控制,即MVCC,既然是多版本,那么快照讀讀到的數據不一定是當前最新的數據,有可能是之前`歷史版本`的數據。 如下操作是快照讀: * 不加鎖的select操作(注:事務級別不是串行化) ### 快照讀與mvcc的關系 `MVCCC`是“維持一個數據的多個版本,使讀寫操作沒有沖突”的一個`抽象概念`。 這個概念需要具體功能去實現,這個具體實現就是`快照讀`。(具體實現下面講) 聽完`貼心老哥`的講解,是不是瞬間`茅廁頓開`。 ![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/5c5fa9c5a396422294a508b7a009e0d5~tplv-k3u1fbpfcp-zoom-1.image) ## 數據庫并發場景 * `讀-讀`:不存在任何問題,也不需要并發控制 * `讀-寫`:有線程安全問題,可能會造成事務隔離性問題,可能遇到臟讀,幻讀,不可重復讀 * `寫-寫`:有線程安全問題,可能會存在更新丟失問題,比如第一類更新丟失,第二類更新丟失 ## MVCC解決并發哪些問題? mvcc用來解決讀—寫沖突的無鎖并發控制,就是為事務分配`單向增長`的`時間戳`。為每個數據修改保存一個`版本`,版本與事務時間戳`相關聯`。 讀操作`只讀取`該事務`開始前`的`數據庫快照`。 **解決問題如下:** * `并發讀-寫時`:可以做到讀操作不阻塞寫操作,同時寫操作也不會阻塞讀操作。 * 解決`臟讀`、`幻讀`、`不可重復讀`等事務隔離問題,但不能解決上面的`寫-寫 更新丟失`問題。 **因此有了下面提高并發性能的`組合拳`:** * `MVCC + 悲觀鎖`:MVCC解決讀寫沖突,悲觀鎖解決寫寫沖突 * `MVCC + 樂觀鎖`:MVCC解決讀寫沖突,樂觀鎖解決寫寫沖突 ## MVCC的實現原理 它的實現原理主要是`版本鏈`,`undo日志` ,`Read View` 來實現的 ### 版本鏈 我們數據庫中的每行數據,除了我們肉眼看見的數據,還有幾個`隱藏字段`,得開`天眼`才能看到。分別是`db_trx_id`、`db_roll_pointer`、`db_row_id`。 * db\_trx\_id 6byte,最近修改(修改/插入)`事務ID`:記錄`創建`這條記錄/`最后一次修改`該記錄的`事務ID`。 * db\_roll\_pointer(版本鏈關鍵) 7byte,`回滾指針`,指向`這條記錄`的`上一個版本`(存儲于rollback segment里) * db\_row\_id 6byte,隱含的`自增ID`(隱藏主鍵),如果數據表`沒有主鍵`,InnoDB會自動以db\_row\_id產生一個`聚簇索引`。 * 實際還有一個`刪除flag`隱藏字段, 記錄被`更新`或`刪除`并不代表真的刪除,而是`刪除flag`變了 ![](https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/4810faee12844b31ad9f44d8012fe188~tplv-k3u1fbpfcp-zoom-1.image) 如上圖,`db_row_id`是數據庫默認為該行記錄生成的`唯一隱式主鍵`,`db_trx_id`是當前操作該記錄的`事務ID`,而`db_roll_pointer`是一個`回滾指針`,用于配合`undo日志`,指向上一個`舊版本`。 每次對數據庫記錄進行改動,都會記錄一條`undo日志`,每條undo日志也都有一個`roll_pointer`屬性(INSERT操作對應的undo日志沒有該屬性,因為該記錄并沒有更早的版本),可以將這些`undo日志都連起來`,`串成一個鏈表`,所以現在的情況就像下圖一樣: ![](https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/d2fa1c7ffdf241a09a790a34b4b8c817~tplv-k3u1fbpfcp-zoom-1.image) 對該記錄每次更新后,都會將舊值放到一條undo日志中,就算是該記錄的一個舊版本,隨著更新次數的增多,所有的版本都會被`roll_pointer`屬性連接成一個`鏈表`,我們把這個鏈表稱之為`版本鏈`,版本鏈的頭節點就是當前記錄最新的值。另外,每個版本中還包含生成該版本時對應的事務id,這個信息很重要,在根據ReadView判斷版本可見性的時候會用到。 ### undo日志 Undo log 主要用于`記錄`數據被`修改之前`的日志,在表信息修改之前先會把數據拷貝到`undo log`里。 當`事務`進行`回滾時`可以通過undo log 里的日志進行`數據還原`。 **Undo log 的用途** * 保證`事務`進行`rollback`時的`原子性和一致性`,當事務進行`回滾`的時候可以用undo log的數據進行`恢復`。 * 用于MVCC`快照讀`的數據,在MVCC多版本控制中,通過讀取`undo log`的`歷史版本數據`可以實現`不同事務版本號`都擁有自己`獨立的快照數據版本`。 **undo log主要分為兩種:** * insert undo log 代表事務在insert新記錄時產生的undo log , 只在事務回滾時需要,并且在事務提交后可以被立即丟棄 * update undo log(主要) 事務在進行update或delete時產生的undo log ; 不僅在事務回滾時需要,在快照讀時也需要; 所以不能隨便刪除,只有在快速讀或事務回滾不涉及該日志時,對應的日志才會被purge線程統一清除 ### Read View(讀視圖) 事務進行`快照讀`操作的時候生產的`讀視圖`(Read View),在該事務執行的快照讀的那一刻,會生成數據庫系統當前的一個`快照`。 記錄并維護系統當前`活躍事務的ID`(沒有commit,當每個事務開啟時,都會被分配一個ID, 這個ID是遞增的,所以越新的事務,ID值越大),是系統中當前不應該被`本事務`看到的`其他事務id列表`。 Read View主要是用來做`可見性`判斷的, 即當我們`某個事務`執行`快照讀`的時候,對該記錄創建一個Read View讀視圖,把它比作條件用來判斷`當前事務`能夠看到`哪個版本`的數據,既可能是當前`最新`的數據,也有可能是該行記錄的undo log里面的`某個版本`的數據。 **Read View幾個屬性** * `trx_ids`: 當前系統活躍(`未提交`)事務版本號集合。 * `low_limit_id`: 創建當前read view 時“當前系統`最大事務版本號`+1”。 * `up_limit_id`: 創建當前read view 時“系統正處于活躍事務`最小版本號`” * `creator_trx_id`: 創建當前read view的事務版本號; ### Read View可見性判斷條件 ![](https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/5a55a3e31e644bc0ae3d8e73a7176e0d~tplv-k3u1fbpfcp-zoom-1.image) * `db_trx_id` < `up_limit_id` || `db_trx_id` == `creator_trx_id`(顯示) 如果數據事務ID小于read view中的`最小活躍事務ID`,則可以肯定該數據是在`當前事務啟之前`就已經`存在`了的,所以可以`顯示`。 或者數據的`事務ID`等于`creator_trx_id` ,那么說明這個數據就是當前事務`自己生成的`,自己生成的數據自己當然能看見,所以這種情況下此數據也是可以`顯示`的。 * `db_trx_id` >= `low_limit_id`(不顯示) 如果數據事務ID大于read view 中的當前系統的`最大事務ID`,則說明該數據是在當前read view 創建`之后才產生`的,所以數據`不顯示`。如果小于則進入下一個判斷 * `db_trx_id`是否在`活躍事務`(trx\_ids)中 * `不存在`:則說明read view產生的時候事務`已經commit`了,這種情況數據則可以`顯示`。 * `已存在`:則代表我Read View生成時刻,你這個事務還在活躍,還沒有Commit,你修改的數據,我當前事務也是看不見的。 ![](https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/14afe47c1add435c907682ca07babf74~tplv-k3u1fbpfcp-zoom-1.image) ## MVCC和事務隔離級別 上面所講的`Read View`用于支持`RC`(Read Committed,讀提交)和`RR`(Repeatable Read,可重復讀)`隔離級別`的`實現`。 ### RR、RC生成時機 * `RC`隔離級別下,是每個`快照讀`都會`生成并獲取最新`的`Read View`; * 而在`RR`隔離級別下,則是`同一個事務中`的`第一個快照讀`才會創建`Read View`, `之后的`快照讀獲取的都是`同一個Read View`,之后的查詢就`不會重復生成`了,所以一個事務的查詢結果每次`都是一樣的`。 ### 解決幻讀問題 * `快照讀`:通過MVCC來進行控制的,不用加鎖。按照MVCC中規定的“語法”進行增刪改查等操作,以避免幻讀。 * `當前讀`:通過next-key鎖(行鎖+gap鎖)來解決問題的。 ### RC、RR級別下的InnoDB快照讀區別 * 在RR級別下的某個事務的對某條記錄的第一次快照讀會創建一個快照及Read View, 將當前系統活躍的其他事務記錄起來,此后在調用快照讀的時候,還是使用的是同一個Read View,所以只要當前事務在其他事務提交更新之前使用過快照讀,那么之后的快照讀使用的都是同一個Read View,所以對之后的修改不可見; * 即RR級別下,快照讀生成Read View時,Read View會記錄此時所有其他活動事務的快照,這些事務的修改對于當前事務都是不可見的。而早于Read View創建的事務所做的修改均是可見 * 而在RC級別下的,事務中,每次快照讀都會新生成一個快照和Read View, 這就是我們在RC級別下的事務中可以看到別的事務提交的更新的原因 作者:IT老哥 鏈接:https://juejin.cn/post/6871046354018238472 來源:掘金 著作權歸作者所有。商業轉載請聯系作者獲得授權,非商業轉載請注明出處。
                  <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>

                              哎呀哎呀视频在线观看