《MySQL必懂系列》全局鎖、表級鎖、行鎖

      網友投稿 1529 2025-03-31

      MySQL提供了不同等級的鎖,按限制能力的劃分,分為全局鎖、表鎖、行鎖。本文會描述不同鎖的應用場景與實現原理。


      全局鎖

      全局鎖就是對整個MySql數據庫加鎖,MySQL中的命令是 Flush tables with read lock (FTWRL)。在執行這個命令之后,MySQL進入全局鎖的狀態,整個數據庫會拒絕掉增刪改這些請求。

      為什么需要全局鎖

      全局鎖的目標是為我們維護一個數據庫的邏輯一致性。如下場景中:在進行邏輯備份(即備份的數據是SQL語句)的時候,沒有開啟全局鎖,那么很可能會導致出現數據庫的邏輯一致性錯誤,例如兩個表,一個余額表、一個訂單表,在購物時(減余額、生成訂單)如果邏輯備份在這兩個操作之間,也就是說減完余額之后,邏輯備份,拒絕生成訂單,那么這個時候,我們進行的邏輯備份就是一個錯誤的邏輯一致性狀態。以后使用這個邏輯備份進行數據恢復的時候,就會出現用戶余額已經減少,但并沒有訂單這種問題。

      全局鎖的缺點

      對主庫使用全局鎖進行邏輯備份時,會造成業務的停擺

      對從庫使用全局鎖進行邏輯備份時,會造成主從延遲的問題

      FTWRL的替代方式

      全局鎖解決的就是上面的問題,我們可以結合數據庫中事務的隔離級別,使用可重復讀(各個事務之間沒有相互影響,基于mvcc)的隔離級別,獲取數據庫的邏輯一致性視圖。MySQL官方自帶的邏輯備份工具mysqldump,在備份數據之前,會啟動一個事務,以此來獲得一個邏輯一致性視圖。

      但需要注意的是,雖然事務的可重復能解決FTWRL影響性能的問題,但事務并不是萬能的,因為并不是所有的引擎都支持這個隔離級別,MyISAM這種不支持事務的引擎,如果備份過程中有更新,總是只能取到最新的數據,那么就破壞了備份的邏輯一致性。

      為什么不設置為全庫只讀?

      我們的目的是實現數據庫的邏輯一致性,那么為什么不建議直接把數據庫設置成只讀狀態呢? (set global readonly=true)

      主要有一下原因:

      FTWRL與readonly的異常機制不太一樣。客戶端(相對于MySQL)發生異常,FTWRL命令下會自動釋放MySQL的全局鎖。而readonly會一直停留在readonly狀態,數據庫長期處于不可寫狀態。

      readonly會被一些邏輯判斷使用,例如使用readonly判斷是主庫或者備庫。

      表級鎖

      表級鎖也分為兩類: 表鎖 、 元數據鎖(meta data lock,MDL)。

      業務的更新不只是增刪改數據(DML),還有可能是加字段等修改表結構的操作(DDL)。

      表鎖

      使用場景

      在還沒有更細粒度的行鎖的時候,表鎖是最長用的處理并發的解決方式。但是對于當前支持行鎖的引擎例如innodb,都優先使用行鎖來控制并發,以此來避免因為鎖住整個表的影響。

      表鎖的語法

      加鎖 lock tables … read/write、主動釋放鎖unlock tables 。同時表鎖也可以在客戶端斷開連接的時候自動釋放。

      讀鎖(共享鎖)

      事務A對數據d加上共享鎖S,那么事務A只能對d進行讀操作,并且后面的事務B、C、D都可以加鎖S進行只讀操作。在釋放完S鎖之前不能對數據d進行修改。

      寫鎖(排它鎖)

      事務A對數據d加上排它鎖X,那么事務A可以對數據d進行訪問、修改,并且拒絕其他事務對數據d的讀、寫。

      表鎖需要注意的地方

      lock tables語法不僅會限制別的線程(事務)讀寫操作,也限定了本線程(事務)的操作對象以及操作方式。即本線程只能按照加鎖語句中規定的方式(讀或者寫)訪問特定的資源(table1、table2)。例如:線程 Thread1 中執行 lock tables table1 write, table2 read;其他線程讀、寫 table1、寫 table2 的語句都會被 阻塞。同時,線程 Thread1 在執行 unlock tables 之前,也只能執行讀、寫 table1、讀 table2 的操作。連寫 table2 都不允許,并且也不能訪問其他表。

      元數據鎖 metadata lock MDL

      元數據在這里其實指的就是表結構,MDL鎖定的也就是我們表結構。防止出現一個線程A在執行表查詢操作時候,線程B刪除了一個字段,導致查詢的結果與表結構不符合這種情況的出現。

      所以為了解決上述問題,MDL分為了讀鎖與寫鎖。

      在進行表的增刪改查時候,會對表自動加上讀鎖,讀鎖之間不會互斥,所以多個線程可以對同一個表進行增刪改查。

      在進行表結構更改時候,會對表自動加上寫鎖,寫鎖是互斥,多個線程能依次對表結構進行修改,然后再加上讀鎖進行增刪改查。

      表鎖并不是現在優先考慮使用的鎖,應該盡量的使用行鎖,如果在項目中遇到lock table1這樣的SQL語句時,應該思考一下:

      是否使用了過老的引擎,例如MYISAM就不支持行鎖,可以考慮升級一下引擎,然后把業務代碼中的lock tables?unlock tables替換為begin commit就OK啦。

      行鎖

      行鎖顧名思義就是對每一行的數據加鎖,這是MySql數據庫中最細粒度的鎖,右innodb引擎支持。對于不能支持行鎖的引擎,對于并發操作的處理只能使用表鎖鎖定整個表,這也是MyISAM被innoDB所替代的重要原因之一。

      行鎖的使用過程

      使用行鎖過程中,若一個事務A正在更新某一行數據d,這時候如果事務B也想對d進行更新操作,那么只能等A更新完畢然后再加自己的行鎖對d進行更新操作。這其中就涉及到一個兩階段鎖這個概念。

      行鎖的兩階段鎖協議

      兩階段鎖協議:在 InnoDB事務中,行鎖是在需要的時候才加上的,但并不是不需要了就立刻釋放,而是要等到事務結束時才釋放。

      其實就是規定了加鎖與解鎖的時機,兩階段鎖協議不僅局限在行鎖中。

      上面的兩個事務AB執行時候就會使用到兩段鎖協議:事務A先開始執行,id=1時加鎖這一行,id=2時加鎖這一行,事務A的兩條語句執行完了但是還沒有commit,事務B開始執行,但是這個時候事務B的update id=1會被阻塞,因為id=1還被事務A加著行鎖,雖然事務A的update執行完了,但是事務A還沒有commit,意味著事務A所占據著的行鎖都沒有釋放,只有等A執行commit之后,事務B才能繼續獲得id=1的行鎖進行update。

      所以我們應該記住兩段鎖的特點:

      在行鎖的引擎中,行鎖是執行到具體某一行才加上的。

      行鎖在本本事務commit之后才會被釋放。

      所以根據兩段鎖協議的特點,我們在開發過程中,應該在事務中把并發大的表放到后面執行,讓它被行鎖鎖定的時間最短。

      例如在減庫存,生成訂單這樣的場景中,我們應該先在事務中生成訂單,在減庫存。因為庫存的update并發量會大于訂單insert的并發量,update需要使用行鎖,如果先update庫存,會使庫存中的這一行一直被行鎖鎖定,在事務提交時候才能被釋放,增加了許多無用的庫存行鎖鎖定時間。

      行鎖中的死鎖

      數據庫中死鎖的概念很清晰,和我們操作系統中的一致:

      資源必須互斥訪問

      請求并保持

      不可搶占資源

      形成一個環

      如果一個項目要新上線一個新功能,如果新功能剛開始的時候MySQL 就掛了。登上服務器一看,CPU 消耗接近 100%,但整個數據庫每秒就執行不到 100 個事務。原因很可能就是死鎖。

      解決MySQL死鎖策略

      出現死鎖以后,有兩種解決策略:

      設置等待的超時時間。innodb_lock_wait_timeout

      主動發起死鎖檢測,發現死鎖后,主動回滾死鎖鏈條中的某一個事務,讓其他事務得以繼續執行。innodb_deadlock_detect = on,表示開啟死鎖檢測。

      innodb_lock_wait_timeout在innoDB引擎中的的默認值是50s,意味著如果發生死鎖的情況,第一個被鎖住的線程等待50s才會超時退出,然后其他線程才有可能繼續執行。對于在線服務來說,這個等待時間往往是無法接受的。但是,我們又不可能直接把這個時間設置成一個很小的值,比如1s。這樣當出現死鎖的時候,確實很快就可以解開,但如果不是死鎖,而是簡單的鎖等待呢?所以,通過設置超時時間通常不是一個好辦法,這個更依賴經驗值,也依賴不同項目的環境(請求并不均勻)。

      所以通常情況下會采用主動死鎖檢測的策略,innodb_deadlock_detect默認值就是on的狀態。主動死鎖檢測能及時發現并解決死鎖,但主動死鎖檢測會消耗硬件資源。

      主動死鎖檢測 流程:每當一個事務被鎖的時候,就要看看它所依賴的線程有沒有被 別人鎖住,如此循環,最后判斷是否出現了循環等待,也就是死鎖。

      《MySQL必懂系列》全局鎖、表級鎖、行鎖

      主動死鎖檢測在熱點行更新時產生的問題

      上面我們提到更推薦使用主動死鎖檢測去解決死鎖問題,但在這樣的場景中:所有的事務都需要更新同一行的數據。使用主動死鎖檢測肯定能得出未死鎖,但是這期間要消耗大量的cpu,導致雖然占用了大量cpu卻實際沒能執行幾個事務。

      這種由這種熱點行更新導致的性能問題的原因在于:主動死鎖檢測要耗費大量的 CPU 資源。

      熱點行更新導致的性能問題的解決思路:

      如果能保證某個業務不會出現死鎖,可以臨時關閉死鎖檢測,但本身可能存在風險,如果發生死鎖,會發生事務等待超時時間。

      控制并發度。例如一行數據只能允許20個事務進行同時更新,那么可以極大的減緩死鎖檢測的壓力。如何去控制并發度,大體也有兩個思路一是通過業務代碼在客戶端進行訪問MySQL的控制,但是MySQL不一定只有這一個客戶端,所以這個思路優缺點;二是考慮使用中間間或者是修改MySQL源碼,對于相同行的update,在進入引擎之前排隊,里面只允許存在20個事務進行update,這樣update時候就不會有太大的死鎖檢測壓力。(死鎖檢測時間復雜度為O(n平方))。 但是這個需要數據庫方面的專家。。。

      可以考慮在業務層面減少對某一行的并發度。例如在收款這個場景中,我們把熱點的某一行拆分出來,保證拆分出來的幾行最后在收款的總數一致就可以了。如果分為20個,那么死鎖的肯能性就變為了原來的20粉之一,與此同時由于不是同一行也減少了主動死鎖檢測cpu的消耗。這種方式需要在代碼里做詳細、嚴謹的邏輯分析。

      綜上:減少死鎖的主要方向,就是控制訪問相同資源的并發事務量。

      MySQL 數據庫

      版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。

      版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。

      上一篇:家具工廠訂單排期表格優化家具生產計劃的利器
      下一篇:Excel2010如何制作施工進度圖表(用excel做施工進度表的教程步驟圖)
      相關文章
      亚洲AV女人18毛片水真多| 亚洲天堂中文字幕在线观看| 中文字幕 亚洲 有码 在线| 亚洲黄色免费网址| 亚洲国产精品自在在线观看| 亚洲国产精品无码专区影院| 亚洲一级特黄大片无码毛片| 亚洲av无码乱码在线观看野外| 国产精品亚洲综合天堂夜夜| 国产成人精品日本亚洲语音 | 久久亚洲精品视频| 亚洲大成色www永久网站| 国产亚洲综合久久系列| 亚洲国产精品无码av| 人人狠狠综合久久亚洲婷婷| 亚洲国产精品成人精品无码区在线| 国产成人精品日本亚洲| 久久亚洲AV无码精品色午夜麻| 亚洲AV无码专区国产乱码电影| 亚洲av无码一区二区三区乱子伦| 亚洲va中文字幕无码久久| 亚洲AV日韩AV天堂久久| 久久久久亚洲AV无码专区首JN| 亚洲综合男人的天堂色婷婷| 亚洲免费网站在线观看| 亚洲一区二区三区丝袜| 一本色道久久88亚洲精品综合| 亚洲国产精品18久久久久久| 国产成人高清亚洲一区久久| 亚洲欧洲一区二区三区| 国产亚洲AV无码AV男人的天堂| 久久综合九九亚洲一区| 亚洲综合在线成人一区| 亚洲AV综合色区无码二区偷拍| 亚洲人成网站色在线观看| 亚洲AV色欲色欲WWW| 久久激情亚洲精品无码?V| 亚洲精品乱码久久久久久久久久久久| 亚洲精品中文字幕乱码三区| 亚洲毛片在线观看| 亚洲人和日本人jizz|