MySQL之事務、鎖

來源:互聯網
上載者:User

標籤:serial   更新記錄   顯示   next   阻塞   upd   序列   特點   機制   

鎖一、概念

  鎖是電腦協調多個進程或線程訪問某一個資源的機制。在資料庫中,除傳統的計算資源(CPU、RAM、IO)的爭用意外,資料也是一種許多使用者共用的資源。如何保證資料並發訪問的一致性、有效性是所有資料庫必須解決的一個問題,鎖衝突也是影響資料庫並發訪問的一個重要因素。從這個角度來說,鎖對資料庫尤其重要,也更加複雜。本文就以MyISAM和InnoDB兩個引擎來說明鎖的問題;

二、MySQL鎖概述

  相對其他資料庫而言,MySQL的鎖機制比較簡單,其中最為顯著的特點是不同的引擎具有不同的鎖。比如MyISAM和InnoDB分別採用表鎖、行鎖,但是InnoDB也支援表鎖,預設情況下採用行鎖;

三、MySQL表鎖和行鎖對比
鎖的名稱 開銷 加鎖速度 死結 粒度 並發效能
表鎖 不會出現 最低
行鎖 出現 最高

從鎖的角度來說表鎖適合查詢為主,只有少量按索引條件更新資料的應用;行鎖適合於大量按索引條件來並發更新少量的資料,同時又有並發查詢的應用;

MyISAM表鎖 一、查詢表級鎖的爭用情況

  可以通過table_locks_waited和table_locks_immediate來分析系統資料表鎖的爭奪;

如果table_locks_waited的值比較高的話,則說明存在較高的表鎖爭奪。

二、表鎖的鎖模式

  表級鎖有兩種方式:讀鎖和寫鎖,鎖模式的相容性如下:

 請求鎖模式是否相容當前鎖模式  讀鎖  寫鎖
 讀鎖  是  否
 寫鎖  否  否

由上表可見,MyISAM表的讀操作是不會阻塞其他使用者對同一張表的讀請求,但會阻塞表的寫請求;對於MyISAM的寫操作,則會阻塞其他使用者對同一張表的讀和寫操作。MyISAM的讀和寫操作之間,寫操作和寫操作之間是串列的。也就是說當一個線程獲得一個表的寫鎖之後,只有持有鎖的線程可以執行更新操作,其他線程的讀和寫都要等待,直到鎖被釋放為止。

三、如何加表鎖

  MyISAM在執行查詢語句(SELECT)前,會自動給涉及的所有表加讀鎖,在執行更新操作(UPDATE、DELETE、INSERT等)前,會自動給涉及的表加寫鎖,這個過程並不需要使用者幹預,因此,使用者一般不需要直接用LOCK TABLE命令給MyISAM表顯式加鎖。

  給MyISAM表加顯示鎖,一般是在一定程度上類比事務操作,實現某一時間點多個表大的一致性讀取。例如我們現在有一個庫存表和入庫單表,我們要判斷入庫單的總數是否和庫存表的一致,這個時候我們不給這兩個表加鎖,就可能產生錯誤的結果,比如在執行第一次查詢庫存的時候,入庫單表發生了改變。這個時候就造成資料錯誤。

  對以上的列子做出總結:

  1、上面的例子在LOCK TABLES時加了“local”選項,其作用就是在滿足MyISAM表並發插入條件的情況下,允許其他使用者在表尾並發插入記錄;

  2、在用LOCK TABLES給表顯式加表鎖時,必須同時取得所有涉及到表的鎖,並且MySQL不支援鎖定擴大。也就是說,在執行LOCK TABLES後,只能訪問顯式加鎖的這些表,不能訪問未加鎖的表;同時,如果加的是讀鎖,那麼只能執行查詢操作,而不能執行更新操作。其實,在自動加鎖的情況下也基本如此,MyISAM總是一次獲得SQL語句所需要的全部鎖。這也正是MyISAM表不會出現死結的原因。

四、並發插入 

  MyISAM的儲存引擎有一個系統的變數concurrent_insert,專門用於控制並發插入的行為,其值分別是0,1,2:

  1、 當concurrent_insert設定為0時,不允許並發插入。

  2、 當concurrent_insert設定為1時,如果MyISAM表中沒有空洞(即表的中間沒有被刪除的行),MyISAM允許在一個進程讀表的同時,另一個進程從表尾插入記錄。這也是MySQL的預設設定。

  3、當concurrent_insert設定為2時,無論MyISAM表中有沒有空洞,都允許在表尾並發插入記錄。

五、MyISAM的鎖調度

  MyISAM的儲存引擎的讀鎖和寫鎖互斥的,讀寫操作都是串列的。比如現在有一個進程某一個表的讀鎖,這是同時有個進程來請求同一張表的寫鎖,這個時候MyISAM表會怎麼做呢?結果就是寫鎖先擷取,不僅如此,如果讀鎖先進入隊列,寫鎖後進入隊列,這個時候也是寫鎖現貨的。這是因為MySQL認為寫比讀重要。這也正是MyISAM表不太合適有大量更新操作和查詢操作的原因。因為大量的更新操作是的讀操作難以獲得鎖,從而可能永遠阻塞,這種情況可能會變的非常糟糕。但是我們可以設定MyISAM的調度行為

1、 通過指定啟動參數low-priority-updates,使MyISAM引擎預設給予讀請求以優先的權利。2、通過執行命令SET LOW_PRIORITY_UPDATES=1,使該串連發出的更新要求優先順序降低。3、通過指定INSERT、UPDATE、DELETE語句的LOW_PRIORITY屬性,降低該語句的優先順序。4、給系統參數 max_write_lock_count設定一個合適的值,當一個表的讀鎖達到這個值後,MySQL就暫時將寫請求的優先順序降低,給讀進程一定獲得鎖的機會。 5、一些需要長時間啟動並執行查詢操作,也會使寫進程阻塞。 六、什麼時候用表鎖

1、事務需要更新大部分或全部資料,表又比較大,如果使用預設的行鎖,不僅這個事務執行效率低,而且可能造成其他事務長時間鎖等待和鎖衝突,這種情況下可以考慮使用表鎖來提高該事務的執行速度。

2、事務涉及多個表,比較複雜,很可能引起死結,造成大量交易回復。這種情況也可以考慮一次性鎖定事務涉及的表,從而避免死結、減少資料庫因交易回復帶來的開銷。

InnoDB鎖

  InnoDB和MyISAM的區別是,InnoDB支援事務和行級鎖。行級鎖和表鎖有很多不同的地方,事務的引入也帶來了一些新的問題。

一、事務1、事務的特性

  原子性對資料的修改,要麼全部成功,要麼全部失敗。

  一致性:在事務開始的時候和完成的時候,資料都必須保持一致的狀態。

  隔離性:保證事務在不受外部並行作業影響的“獨立”環境執行。也就是交易處理過程的中間部分是不可見的。

  持久性:事務完成之後,他對資料的修改是永久的,即系統出現故障也可以保持。

2、並發事務帶來的問題

  更新丟失:最後的更新覆蓋了其他事務所做的更新。解決此問題的條件是,目前使用者在沒有提交事務的時候,其他使用者是不可以操作的。

  髒讀:一個事務正在對一條記錄做修改,這個事務在完成提交前,這個時候另一個事務來讀取這一行的資料,就有可能讀到髒資料。

  不可重複讀取:一個事務在讀取某些資料後的某個時間,再次讀取以前讀過的資料,卻發現其讀出的資料已經發生了改變、或某些記錄已經被刪除了!

  幻度:一個事務按相同的查詢條件重新讀取以前檢索過的資料,卻發現其他事務插入了滿足其查詢條件的新資料。

3、事務的隔離等級

  a、更新丟失通常應該完全可以避免,但防止更新丟失,不能單靠資料庫事務控制來解決,需要應用程式對更新的資料加鎖來解決;

  b、“髒讀”、“不可重複讀取”和“幻讀”,其實都是資料庫讀一致性問題,必須由資料庫提供一定的事務隔離機制來解決

隔離等級 讀資料一致性 髒讀 不可重複讀取 幻讀
未提交讀(Read uncommitted) 最低層級,只能保證不讀取物理上損壞的資料
已提交度(Read committed) 語句級
可重複讀(Repeatable read) 事務級
可序列化(Serializable) 最進階別,事務級
4、InnoDB的行鎖模式和加鎖方法

  

鎖類型 可讀(其他事務) 可寫(其他事務)
共用鎖定(S)
獨佔鎖定(x)

 

為了行鎖和表鎖共從,InnoDB又提供了兩個意圖鎖定(也就是表鎖)

  意圖共用鎖(IS):事務打算給資料行加行共用鎖定,事務在給一個資料行加共用鎖定前必須先取得該表的IS鎖。

  意向獨佔鎖定(IX):事務打算給資料行加行獨佔鎖定,事務在給一個資料行加獨佔鎖定前必須先取得該表的IX鎖。

這四種鎖之間的相容性如下:

鎖模式 S X IS IX
S 相容 衝突 相容 衝突
X 衝突 衝突 衝突 衝突
IS 相容 衝突  相容 相容
IX  衝突  衝突 相容   相容

如果一個事務請求的鎖模式與當前的鎖相容,InnoDB就將請求的鎖授予該事務;反之,如果兩者不相容,該事務就要等待鎖釋放。

意圖鎖定是InnoDB自動加的,不需使用者幹預。對於UPDATE、DELETE和INSERT語句,InnoDB會自動給涉及資料集加獨佔鎖定(X);對於普通SELECT語句,InnoDB不會加任何鎖;5、InnoDB行鎖的實現方式

  InnoDB的行鎖實現是通過給索引上的索引項目加鎖來實現的;也就說只有通過索引檢索資料,InnoDB才使用行級鎖,否則InnoDB將使用表鎖。

注意點:

  a、在不通過索引條件查詢的時候,InnoDB確實使用的是表鎖,而不是行鎖。

  b、由於MySQL的行鎖是針對索引加的鎖,不是針對記錄加的鎖,所以雖然是訪問不同行的記錄,但是如果是使用相同的索引鍵,是會出現鎖衝突的。

  c、當表有多個索引的時候,不同的事務可以使用不同的索引鎖定不同的行,另外,不論是使用主鍵索引、唯一索引或普通索引,InnoDB都會使用行鎖來對資料加鎖。

  d、即便在條件中使用了索引欄位,但是否使用索引來檢索資料是由MySQL通過判斷不同執行計畫的代價來決定的,如果MySQL認為全表掃描效率更高,比如對一些很小的表,它就不會使用索引,這種情況下InnoDB將使用表鎖,而不是行鎖。因此,在分析鎖衝突時,別忘了檢查SQL的執行計畫,以確認是否真正使用了索引。

6、間隙鎖

  當我們用範圍條件而不是相等條件檢索資料,並請求共用或獨佔鎖定時,InnoDB會給合格已有資料記錄的索引項目加鎖;對於索引值在條件範圍內但並不存在的記錄,叫做“間隙(GAP)”,InnoDB也會對這個“間隙”加鎖,這種鎖機制就是所謂的間隙鎖(Next-Key鎖)。

目的

  InnoDB使用間隙鎖的目的,一方面是為了防止幻讀,以滿足相關隔離等級的要求,如果不使用間隙鎖,如果其他事務插入了超過範圍的任何記錄,那麼本事務如果再次執行上述語句,就會發生幻讀;另外一方面,是為了滿足其恢複和複製的需要。

  很顯然,在使用範圍條件檢索並鎖定記錄時,InnoDB這種加鎖機制會阻塞符合條件範圍內索引值的並發插入,這往往會造成嚴重的鎖等待。因此,在實際應用開發中,尤其是並發插入比較多的應用,我們要盡量最佳化商務邏輯,盡量使用相等條件來訪問更新資料,避免使用範圍條件。

死結

  發生死結後,InnoDB一般都能自動檢測到,並使一個事務釋放鎖並回退,另一個事務獲得鎖,繼續完成事務。但在涉及外部鎖,或涉及表鎖的情況下,InnoDB並不能完全自動檢測到死結,這需要通過設定鎖等待逾時參數 innodb_lock_wait_timeout來解決。需要說明的是,這個參數並不是只用來解決死結問題,在並發訪問比較高的情況下,如果大量事務因無法立即獲得所需的鎖而掛起,會佔用大量電腦資源,造成嚴重性能問題,甚至拖跨資料庫。我們通過設定合適的鎖等待逾時閾值,可以避免這種情況發生。

  通常來說,死結都是應用設計的問題,通過調整商務程序、資料庫物件設計、事務大小,以及訪問資料庫的SQL語句,絕大部分死結都可以避免。下面就通過執行個體來介紹幾種避免死結的常用方法。

  1、在應用中,如果不同的程式會並發存取多個表,應盡量約定以相同的順序來訪問表,這樣可以大大降低產生死結的機會。

  2、在程式以批量方式處理資料的時候,如果事先對資料排序,保證每個線程按固定的順序來處理記錄,也可以大大降低出現死結的可能。

  3、在事務中,如果要更新記錄,應該直接申請足夠層級的鎖,即獨佔鎖定,而不應先申請共用鎖定,更新時再申請獨佔鎖定,因為當使用者申請獨佔鎖定時,其他事務可能又已經獲得了相同記錄的共用鎖定,從而造成鎖衝突,甚至死結。

  4、在REPEATABLE-READ隔離等級下,如果兩個線程同時對相同條件記錄用SELECT...FOR UPDATE加獨佔鎖定,在沒有符合該條件記錄情況下,兩個線程都會加鎖成功。程式發現記錄尚不存在,就試圖插入一條新記錄,如果兩個線程都這麼做,就會出現死結。這種情況下,將隔離等級改成READ COMMITTED,就可避免問題

  5、當隔離等級為READ COMMITTED時,如果兩個線程都先執行SELECT...FOR UPDATE,判斷是否存在合格記錄,如果沒有,就插入記錄。此時,只有一個線程能插入成功,另一個線程會出現鎖等待,當第1個線程提交後,第2個線程會因主鍵重出錯,但雖然這個線程出錯了,卻會獲得一個獨佔鎖定!這時如果有第3個線程又來申請獨佔鎖定,也會出現死結。

MySQL之事務、鎖

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.