mysql鎖機制淺析(1)

mysql最初是但願設計出一種獨立於各類存儲引擎的鎖定機制,mysql存儲引擎的設計者是創建在「任何表在同一時刻只容許單個線程對其進行訪問(包括讀)」這樣的假設的基礎之上的!很明顯,如今的mysql並非這個樣子的,由於mysql現在已經發展成爲了一款多用戶、多線程的mysql關係型數據庫! 其顯著特色就是不一樣的存儲引擎支持不一樣的鎖機制!在咱們淺析鎖機制以前,須要先明白一些基礎的概念!mysql

一次性封鎖與兩段鎖

一次性封鎖:在sql語句的開始執行的時候,已經預先知道要涉及到那些數據,而後所有鎖住,在執行完畢以後,再所有解鎖!(myisam就是採用這樣的鎖協議)sql

兩段鎖: 是指每一個事務的執行能夠分爲兩個階段:生長階段(加鎖階段)和衰退階段(解鎖階段)。數據庫

加鎖階段:在該階段能夠進行加鎖操做。在對任何數據進行讀操做以前要申請並得到S鎖,在進行寫操做以前要申請並得到X鎖。加鎖不成功,則事務進入等待狀態,直到加鎖成功才繼續執行;服務器

解鎖階段:當事務釋放了一個封鎖之後,事務進入解鎖階段,在該階段只能進行解鎖操做不能再進行加鎖操做。session

兩段封鎖法能夠這樣來實現:事務開始後就處於加鎖階段,一直到執行ROLLBACK和COMMIT以前都是加鎖階段。ROLLBACK和COMMIT使事務進入解鎖階段,即在ROLLBACK和COMMIT模塊中DBMS釋放全部封鎖。(innodb就是採起這樣的鎖協議)多線程

mysql使用了三種級別的鎖定機制(依照鎖定範圍而言)

行級別的鎖定、頁級別的鎖定、表級別的鎖定!併發

行級鎖定:
行級鎖定最大的特色就是鎖定對象的顆粒度很小,因爲鎖定顆粒度很小,因此發生鎖定資源爭用的機率也最小,可以給予應用程序儘量大的併發處理能力而提升一些須要高併發應用系統的總體性能。
雖然可以在併發處理能力上面有較大的優點,可是行級鎖定也所以帶來了很多弊端。因爲鎖定資源的顆粒度很小,因此每次獲取鎖和釋放鎖須要作的事情也更多,帶來的消耗天然也就更大了。此外,行級鎖定也最容易發生死鎖。高併發

表級鎖定:
和行級鎖定相反,表級別的鎖定是MySQL各存儲引擎中最大顆粒度的鎖定機制。該鎖定機制最大的特色是實現邏輯很是簡單,帶來的系統負面影響最小。因此獲取鎖和釋放鎖的速度很快。固然,鎖定顆粒度大所帶來最大的負面影響就是出現鎖定資源爭用的機率也會最高,導致並大度大打折扣。此外,表級別的鎖定是不會產生死鎖問題的。性能

頁級鎖定:
頁級鎖定是MySQL中比較獨特的一種鎖定級別,在其餘數據庫管理軟件中也並非太常見。頁級鎖定的特色是鎖定顆粒度介於行級鎖定與表級鎖之間,因此獲取鎖定所須要的資源開銷,以及所能提供的併發處理能力也一樣是介於上面兩者之間。固然,也會產生死鎖問題。spa

常見的存儲引擎與三種級別的鎖機制

行級鎖定:innodb(innodb的鎖是創建在索引基礎上的,必要的時候會由行鎖升級爲表鎖,因此,innodb既支持表鎖也支持行鎖)

表級鎖定:myisam、memory,innodb,BDB

頁級鎖定: BDB存儲引擎採用的是頁面鎖(page-level locking),但也支持表級鎖

隱式鎖與顯式鎖

mysql鎖分爲隱式鎖和顯式鎖。當多個客戶端併發訪問同一個數據的時候,爲了保證數據的一致性,數據庫管理系統會自動的爲該數據加鎖、解鎖,這種被稱爲隱式鎖。隱式鎖無需開發人員維護(包括鎖粒度、加鎖時機、解鎖時機等)

當時在某些特殊的狀況下須要開發人員手動的進行加鎖、解鎖,這種鎖方式被稱爲顯式鎖。對於顯式鎖而言,開發人員不只要肯定鎖的粒度,還須要肯定加鎖的時機(什麼時候加鎖)、解鎖的時機(什麼時候解鎖)以及所的類型。

鎖的生命週期

鎖的生命週期是指在一個msql回話內,對數據進行加鎖到解鎖之間的時間間隔。鎖的聲明週期越長,併發性能就越低;鎖的聲明週期越短,併發性能就越高。另外鎖是數據庫管理系統的重要資源,須要佔據必定的服務器內存,鎖的週期越長,佔用的服務器內存時間就越長;相反若是鎖週期越短,佔用的內存也就越短。所以,總的來講,咱們應該儘量的縮短鎖的生命週期。

鎖類型

clipboard.png

讀鎖(read lock,也叫共享鎖): 不會阻塞其餘用戶對鎖定數據的讀請求,但會阻塞對鎖定數據的寫請求。

clipboard.png

寫鎖(x lock,也叫排它鎖): 會阻塞其餘用戶對鎖定數據的讀和寫操做。

clipboard.png

Myisam存儲引擎的鎖機制

MyISAM存儲引擎只支持表鎖,這也是MySQL開始幾個版本中惟一支持的鎖類型。隨着應用對事務完整性和 併發性要求的不斷提升,MySQL纔開始開發基於事務的存儲引擎,後來慢慢出現了支持頁鎖的BDB存儲引擎和支持行鎖的InnoDB存儲引擎(實際 InnoDB是單獨的一個公司,如今已經被Oracle公司收購)。可是MyISAM的表鎖依然是使用最爲普遍的鎖類型。

一、如何維護讀鎖和寫鎖

在MySQL 中,主要經過四個隊列來維護這兩種鎖定:兩個存放當前正在鎖定中的讀和寫鎖定信息,另外兩個存放等待中的讀寫鎖定信息,以下:
• Current read-lock queue (lock->read)
• Pending read-lock queue (lock->read_wait)
• Current write-lock queue (lock->write)
• Pending write-lock queue (lock->write_wait)
當前持有讀鎖的全部線程的相關信息都可以在Current read-lock queue 中找到,隊列中的信息按照獲取到鎖的時間依序存放。而正在等待鎖定資源的信息則存放在Pending read-lock queue 裏面,另外兩個存放寫鎖信息的隊列也按照上面相同規則來存放信息。

二、如何加鎖

如何加讀鎖?

  • 請求的資源當前沒有被寫鎖定,就是沒有在Current write-lock queue 隊列中出現;

  • 寫鎖定的等待隊列中(Pending write-lock queue )隊列中並無優先級更高的寫鎖定等待;

知足上面的兩個條件以後,請求會被當即經過,並將相關信息存入到Current read-lock queue隊列中,而若是有一個條件沒有知足,就會被迫進入到Pending-read-lock-queue隊列中進行等待。

如何加寫鎖?

  • 在Current write-lock queue中是否有鎖定相同的資源;

  • 在Pending write-lock queue中是否有鎖定相同的資源;

  • 在Current read-lock queue中是否有鎖定相同的資源;

知足上面的三個條件後,請求會被當即經過,將相關信息存入到Current write-lock queue隊列中,而若是有一個條件沒有知足,就會被迫進入到Pending-write-lock-queue隊列中進行等待!

隱式加鎖?
myisam存儲引擎在執行sql語句以前會自動爲涉及到的表加鎖。
MyISAM在執行查詢語句(SELECT)前,會自動給涉及的全部表加讀鎖,在執行更新操做 (UPDATE、DELETE、INSERT等)前,會自動給涉及的表加寫鎖,這個過程並不須要用戶干預,所以,用戶通常不須要直接用LOCK TABLE命令給MyISAM表顯式加鎖。在示例中,顯式加鎖基本上都是爲了模擬而已。

顯式加鎖?
在用LOCK TABLES給表顯式加表鎖時,必須同時取得全部涉及到表的鎖,而且MySQL不支持鎖升級。也就是說,在執行LOCK TABLES後,只能訪問顯式加鎖的這些表,不能訪問未加鎖的表;同時,若是加的是讀鎖,那麼只能執行查詢操做,而不能執行更新操做。其實,在自動加鎖的 狀況下也基本如此,MyISAM老是一次得到SQL語句所須要的所有鎖。這也正是MyISAM表不會出現死鎖(Deadlock Free)的緣由。

lock table 表名 【redad|write】;加鎖
unlock tables; 解鎖

三、myisam的併發插入

上文提到過MyISAM表的讀和寫是串行的,但這是就整體而言的。在必定條件下,MyISAM表也支持查詢和插入操做的併發進行。
MyISAM存儲引擎有一個系統變量concurrent_insert,專門用以控制其併發插入的行爲,其值分別能夠爲0、1或2。

當concurrent_insert設置爲0時,不容許併發插入。
   當concurrent_insert設置爲1時,若是MyISAM表中沒有空洞(即表的中間沒有被刪除的行),MyISAM容許在一個進程讀表的同時,
   另外一個進程從表尾插入記錄。這也是MySQL的默認設置。
   當concurrent_insert設置爲2時,不管MyISAM表中有沒有空洞,都容許在表尾併發插入記錄

四、myisam的鎖調度

前面講過,MyISAM存儲引擎的讀鎖和寫鎖是互斥的,讀寫操做是串行的。那麼,一個進程請求某個 MyISAM表的讀鎖,同時另外一個進程也請求同一表的寫鎖,MySQL如何處理呢?答案是寫進程先得到鎖。
不只如此,即便讀請求先到鎖等待隊列,寫請求後到,寫鎖也會插到讀鎖請求以前!這是由於MySQL認爲寫請求通常比讀請求要重要。這也正是MyISAM表不太適合於有大量更新操做和查詢操做應用的緣由,由於,大量的更新操做會形成查詢操做很難得到讀鎖,從而可能永遠阻塞。這種狀況有時可能會變得很是糟糕!幸虧咱們能夠經過一些設置來調MyISAM 的調度行爲。

  • 經過指定啓動參數low-priority-updates,使全部的更新操做優先級比select語句的優先級低。

  • 向特定的insert、delete、update語句添加 low_priority選項,下降這些操做的優先級。 這種調度修改暗示着,可能存在LOW_PRIORITY寫入操做永遠被阻塞的狀況。若是前面的讀取操做在進行的過程當中一直有其它的讀取操做到達,那麼新的請求都會插入到LOW_PRIORITY寫入操做以前。

  • 向特定的select語句添加high_priority選項,提升檢索操做的優先級。 它容許SELECT插入正在等待的寫入操做以前,即便在正常狀況下寫入操做的優先級更高。另一種影響是,高優先級的SELECT在正常的SELECT語句以前執行,由於這些語句會被寫入操做阻塞。

雖然上面3種方法都是要麼更新優先,要麼查詢優先的方法,但仍是能夠用其來解決查詢相對重要的應用(如用戶登陸系統)中,讀鎖等待嚴重的問題。(可是我的建議不要使用,除非你真的肯定要這麼幹)。

另外,MySQL也提供了一種折中的辦法來調節讀寫衝突,即給系統參數max_write_lock_count設置一個合適的值,當一個表的讀鎖達到這個值後,MySQL就暫時將寫請求的優先級下降,給讀進程必定得到鎖的機會。上面已經討論了寫優先調度機制帶來的問題和解決辦法。這 裏還要強調一點:一些須要長時間運行的查詢操做,也會使寫進程「餓死」!所以,應用中應儘可能避免出現長時間運行的查詢操做,不要總想用一條SELECT語 句來解決問題,由於這種看似巧妙的SQL語句,每每比較複雜,執行時間較長,在可能的狀況下能夠經過使用中間表等措施對SQL語句作必定的「分解」,使每 一步查詢都能在較短期完成,從而減小鎖衝突。若是複雜查詢不可避免,應儘可能安排在數據庫空閒時段執行,好比一些按期統計能夠安排在夜間執行。

五、Myisam存儲引擎中如何查看錶級鎖的爭用狀態

clipboard.png

這裏有兩個狀態變量記錄MySQL 內部表級鎖定的狀況,兩個變量說明以下:
Table_locks_immediate:使用表級鎖後當即釋放表級鎖的次數。
Table_locks_wait:出現表級鎖爭用而發生等待的次數;
兩個狀態值都是從系統開啓後開始記錄的,每出現一次對應的事件數量就會加1。

六、舉例

create table film_text
(

film_id int not null auto_increment,
 title varchar(64) not null default '',
 primary key (film_id)

)engine=myisam charset=utf8;

insert into film_text (film_id, title) values ('1001', 'Update Test');

=================================================================================================

create table film
(

film_id int not null auto_increment,
 title varchar(64) not null default '',
 primary key (film_id)

)engine=myisam charset=utf8;

insert into film (film_id, title) values ('1001', 'Update Test');

session1 session2
首先獲取film_text表的write鎖定: clipboard.png
在當前會話中對鎖定表的查詢、更新、插入操做都是能夠的: clipboard.pngclipboard.png 在其餘的會話中對鎖定表的任何操做都會被阻塞(串行化): clipboard.png 這裏一直處於等待的狀態……
在當前會話中,釋放對錶的鎖定: clipboard.png 執行獲得結果: clipboard.png
對film_text表加讀鎖 clipboard.png
在當前會話中,只能對鎖定到的表進行查詢操做,成功的對鎖定的表進行了查詢: clipboard.png 對沒有鎖定的表進行查詢,出現報錯: clipboard.png 對鎖定的表進行更新和刪除操做,出現了報錯 clipboard.png 在其餘會話中,能夠對鎖定的表進行查詢操做 clipboard.png 一直處於等待的狀態: clipboard.png
釋放對該表的鎖定: clipboard.png 更新操做完成: clipboard.pngclipboard.png
對film_text表加上read local鎖定: clipboard.png
當前會話不可以對鎖定表進行更新、插入、刪除的操做,可是查詢操做是能夠的 : clipboard.png 其餘的會話,能夠對進行查詢、和插入操做: clipboard.pngclipboard.png 刪除操做一直處於等待狀態…… clipboard.png
釋放鎖: clipboard.png 執行了刪除操做: clipboard.png

ps:READ LOCAL和READ之間的區別是,READ LOCAL容許在鎖定被保持時,執行非衝突性INSERT語句(同時插入)。 對於InnoDB表,READ LOCAL與READ相同。

以上是對我的對mysql鎖機制的一些理解,若是有理解錯誤或者不到位的地方,但願高手不吝賜教!

相關文章
相關標籤/搜索