下文主要給大家?guī)鞰ySQL鎖實現(xiàn)機制原理,希望這些文字能夠帶給大家實際用處,這也是我編輯mysql鎖實現(xiàn)機制原理這篇文章的主要目的。好了,廢話不多說,大家直接看下文吧。
創(chuàng)新互聯(lián)是一家集網(wǎng)站建設(shè),金水企業(yè)網(wǎng)站建設(shè),金水品牌網(wǎng)站建設(shè),網(wǎng)站定制,金水網(wǎng)站建設(shè)報價,網(wǎng)絡(luò)營銷,網(wǎng)絡(luò)優(yōu)化,金水網(wǎng)站推廣為一體的創(chuàng)新建站企業(yè),幫助傳統(tǒng)企業(yè)提升企業(yè)形象加強企業(yè)競爭力??沙浞譂M足這一群體相比中小企業(yè)更為豐富、高端、多元的互聯(lián)網(wǎng)需求。同時我們時刻保持專業(yè)、時尚、前沿,時刻以成就客戶成長自我,堅持不斷學(xué)習(xí)、思考、沉淀、凈化自己,讓我們?yōu)楦嗟钠髽I(yè)打造出實用型網(wǎng)站。
鎖是計算機協(xié)調(diào)多個進程或線程并發(fā)訪問某一資源的機制。在數(shù)據(jù)庫中,除傳統(tǒng)的 計算資源(如CPU、RAM、I/O等)的爭用以外,數(shù)據(jù)也是一種供許多用戶共享的資源。如何保證數(shù)據(jù)并發(fā)訪問的一致性、有效性是所有數(shù)據(jù)庫必須解決的一 個問題,鎖沖突也是影響數(shù)據(jù)庫并發(fā)訪問性能的一個重要因素。從這個角度來說,鎖對數(shù)據(jù)庫而言顯得尤其重要,也更加復(fù)雜。本章我們著重討論MySQL鎖機制 的特點,常見的鎖問題,以及解決MySQL鎖問題的一些方法或建議。
Mysql用到了很多這種鎖機制,比如行鎖,表鎖等,讀鎖,寫鎖等,都是在做操作之前先上鎖。這些鎖統(tǒng)稱為悲觀鎖(Pessimistic Lock)。
MySQL鎖概述
相對其他數(shù)據(jù)庫而言,MySQL的鎖機制比較簡單,其最 顯著的特點是不同的存儲引擎支持不同的鎖機制。比如,MyISAM和MEMORY存儲引擎采用的是表級鎖(table-level locking);BDB存儲引擎采用的是頁面鎖(page-level locking),但也支持表級鎖;InnoDB存儲引擎既支持行級鎖(row-level locking),也支持表級鎖,但默認情況下是采用行級鎖。
表級鎖:開銷小,加鎖快;不會出現(xiàn)死鎖;鎖定粒度大,發(fā)生鎖沖突的概率最高,并發(fā)度最低。
行級鎖:開銷大,加鎖慢;會出現(xiàn)死鎖;鎖定粒度最小,發(fā)生鎖沖突的概率最低,并發(fā)度也最高。
頁面鎖:開銷和加鎖時間界于表鎖和行鎖之間;會出現(xiàn)死鎖;鎖定粒度界于表鎖和行鎖之間,并發(fā)度一般
從上述特點可見,很難籠統(tǒng)地說哪種鎖更好,只能就具體應(yīng)用的特點來說哪種鎖更合適!僅從鎖的角度 來說:表級鎖更適合于以查詢?yōu)橹?,只有少量按索引條件更新數(shù)據(jù)的應(yīng)用,如Web應(yīng)用;而行級鎖則更適合于有大量按索引條件并發(fā)更新少量不同數(shù)據(jù),同時又有 并發(fā)查詢的應(yīng)用,如一些在線事務(wù)處理(OLTP)系統(tǒng)。
MyISAM表鎖
MySQL的表級鎖有兩種模式:表共享讀鎖(Table Read Lock)和表獨占寫鎖(Table Write Lock)。
對MyISAM表的讀操作,不會阻塞其他用戶對同一表的讀請求,但會阻塞對同一表的寫請求;對 MyISAM表的寫操作,則會阻塞其他用戶對同一表的讀和寫操作;MyISAM表的讀操作與寫操作之間,以及寫操作之間是串行的!根據(jù)如表20-2所示的 例子可以知道,當(dāng)一個線程獲得對一個表的寫鎖后,只有持有鎖的線程可以對表進行更新操作。其他線程的讀、寫操作都會等待,直到鎖被釋放為止。
MyISAM存儲引擎的寫鎖阻塞讀例子:
當(dāng)一個線程獲得對一個表的寫鎖后,只有持有鎖的線程可以對表進行更新操作。其他線程的讀、寫操作都會等待,直到鎖被釋放為止。
MyISAM存儲引擎的讀鎖阻塞寫例子:
一個session使用LOCK TABLE命令給表film_text加了讀鎖,這個session可以查詢鎖定表中的記錄,但更新或訪問其他表都會提示錯誤;同時,另外一個session可以查詢表中的記錄,但更新就會出現(xiàn)鎖等待。
如何加表鎖
MyISAM在執(zhí)行查詢語句(SELECT)前,會自動給涉及的所有表加讀鎖,在執(zhí)行更新操作 (UPDATE、DELETE、INSERT等)前,會自動給涉及的表加寫鎖,這個過程并不需要用戶干預(yù),因此,用戶一般不需要直接用LOCK TABLE命令給MyISAM表顯式加鎖。在示例中,顯式加鎖基本上都是為了演示而已,并非必須如此。
給MyISAM表顯示加鎖,一般是為了在一定程度模擬事務(wù)操作,實現(xiàn)對某一時間點多個表的一致性讀取。例如, 有一個訂單表orders,其中記錄有各訂單的總金額total,同時還有一個訂單明細表order_detail,其中記錄有各訂單每一產(chǎn)品的金額小計 subtotal,假設(shè)我們需要檢查這兩個表的金額合計是否相符,可能就需要執(zhí)行如下兩條SQL:
Select sum(total) from orders; Select sum(subtotal) from order_detail;
這時,如果不先給兩個表加鎖,就可能產(chǎn)生錯誤的結(jié)果,因為第一條語句執(zhí)行過程中,order_detail表可能已經(jīng)發(fā)生了改變。因此,正確的方法應(yīng)該是:
Lock tables orders read local, order_detail read local; Select sum(total) from orders; Select sum(subtotal) from order_detail; Unlock tables;
要特別說明以下兩點內(nèi)容:
1、上面的例子在LOCK TABLES時加了“l(fā)ocal”選項,其作用就是在滿足MyISAM表并發(fā)插入條件的情況下,允許其他用戶在表尾并發(fā)插入記錄,有關(guān)MyISAM表的并發(fā)插入問題,在后面還會進一步介紹。
2、在用LOCK TABLES給表顯式加表鎖時,必須同時取得所有涉及到表的鎖,并且MySQL不支持鎖升級。也就是說,在執(zhí)行LOCK TABLES后,只能訪問顯式加鎖的這些表,不能訪問未加鎖的表;同時,如果加的是讀鎖,那么只能執(zhí)行查詢操作,而不能執(zhí)行更新操作。其實,在自動加鎖的 情況下也基本如此,MyISAM總是一次獲得SQL語句所需要的全部鎖。這也正是MyISAM表不會出現(xiàn)死鎖(Deadlock Free)的原因。
當(dāng)使用LOCK TABLES時,不僅需要一次鎖定用到的所有表,而且,同一個表在SQL語句中出現(xiàn)多少次,就要通過與SQL語句中相同的別名鎖定多少次,否則也會出錯!舉例說明如下。
(1)對actor表獲得讀鎖:
mysql> lock table actor read; Query OK, 0 rows affected (0.00 sec)
(2)但是通過別名訪問會提示錯誤:
mysql> select a.first_name,a.last_name,b.first_name,b.last_name from actor a,actor b where a.first_name = b.first_name and a.first_name = 'Lisa' and a.last_name = 'Tom' and a.last_name <> b.last_name; ERROR 1100 (HY000): Table ‘a(chǎn)’ was not locked with LOCK TABLES
(3)需要對別名分別鎖定:
mysql> lock table actor as a read,actor as b read; Query OK, 0 rows affected (0.00 sec)
(4)按照別名的查詢可以正確執(zhí)行:
mysql> select a.first_name,a.last_name,b.first_name,b.last_name from actor a,actor b where a.first_name = b.first_name and a.first_name = 'Lisa' and a.last_name = 'Tom' and a.last_name <> b.last_name; +————+———–+————+———–+ | first_name | last_name | first_name | last_name | +————+———–+————+———–+ | Lisa | Tom | LISA | MONROE | +————+———–+————+———–+ 1 row in set (0.00 sec)
查詢表級鎖爭用情況
可以通過檢查table_locks_waited和table_locks_immediate狀態(tài)變量來分析系統(tǒng)上的表鎖定爭奪:
mysql> show status like 'table%'; 1Variable_name | Value Table_locks_immediate | 2979 Table_locks_waited | 0 2 rows in set (0.00 sec))
如果Table_locks_waited的值比較高,則說明存在著較嚴(yán)重的表級鎖爭用情況。
并發(fā)插入(Concurrent Inserts)
上文提到過MyISAM表的讀和寫是串行的,但這是就總體而言的。在一定條件下,MyISAM表也支持查詢和插入操作的并發(fā)進行。
MyISAM存儲引擎有一個系統(tǒng)變量concurrent_insert,專門用以控制其并發(fā)插入的行為,其值分別可以為0、1或2。
1、當(dāng)concurrent_insert設(shè)置為0時,不允許并發(fā)插入。
2、當(dāng)concurrent_insert設(shè)置為1時,如果MyISAM表中沒有空洞(即表的中間沒有被刪除的行),MyISAM允許在一個進程讀表的同時,另一個進程從表尾插入記錄。這也是MySQL的默認設(shè)置。
3、當(dāng)concurrent_insert設(shè)置為2時,無論MyISAM表中有沒有空洞,都允許在表尾并發(fā)插入記錄。
在下面的例子中,session_1獲得了一個表的READ LOCAL鎖,該線程可以對表進行查詢操作,但不能對表進行更新操作;其他的線程(session_2),雖然不能對表進行刪除和更新操作,但卻可以對該表進行并發(fā)插入操作,這里假設(shè)該表中間不存在空洞。
上文提到過MyISAM表的讀和寫是串行的,但這是就總體而言的。在一定條件下,MyISAM表也支持查詢和插入操作的并發(fā)進行。
MyISAM存儲引擎有一個系統(tǒng)變量concurrent_insert,專門用以控制其并發(fā)插入的行為,其值分別可以為0、1或2。
當(dāng)concurrent_insert設(shè)置為0時,不允許并發(fā)插入。當(dāng)concurrent_insert設(shè)置為1時,如果MyISAM表中沒有空洞(即表的中間沒有被刪除的行),MyISAM允許在一個進程讀表的同時,另一個進程從表尾插入記錄。這也是MySQL的默認設(shè)置。當(dāng)concurrent_insert設(shè)置為2時,無論MyISAM表中有沒有空洞,都允許在表尾并發(fā)插入記錄。
在下面的例子中,session_1獲得了一個表的READ LOCAL鎖,該線程可以對表進行查詢操作,但不能對表進行更新操作;其他的線程(session_2),雖然不能對表進行刪除和更新操作,但卻可以對該表進行并發(fā)插入操作,這里假設(shè)該表中間不存在空洞。
MyISAM存儲引擎的讀寫(INSERT)并發(fā)例子:
可以利用MyISAM存儲引擎的并發(fā)插入特性,來解決應(yīng) 用中對同一表查詢和插入的鎖爭用。例如,將concurrent_insert系統(tǒng)變量設(shè)為2,總是允許并發(fā)插入;同時,通過定期在系統(tǒng)空閑時段執(zhí)行 OPTIMIZE TABLE語句來整理空間碎片,收回因刪除記錄而產(chǎn)生的中間空洞。
MyISAM的鎖調(diào)度
前面講過,MyISAM存儲引擎的讀鎖和寫鎖是互斥的,讀寫操作是串行的。那么,一個進程請求某個 MyISAM表的讀鎖,同時另一個進程也請求同一表的寫鎖,MySQL如何處理呢?答案是寫進程先獲得鎖。不僅如此,即使讀請求先到鎖等待隊列,寫請求后 到,寫鎖也會插到讀鎖請求之前!這是因為MySQL認為寫請求一般比讀請求要重要。這也正是MyISAM表不太適合于有大量更新操作和查詢操作應(yīng)用的原 因,因為,大量的更新操作會造成查詢操作很難獲得讀鎖,從而可能永遠阻塞。這種情況有時可能會變得非常糟糕!幸好我們可以通過一些設(shè)置來調(diào)節(jié)MyISAM 的調(diào)度行為。
1、通過指定啟動參數(shù)low-priority-updates,使MyISAM引擎默認給予讀請求以優(yōu)先的權(quán)利。
2、通過執(zhí)行命令SET LOW_PRIORITY_UPDATES=1,使該連接發(fā)出的更新請求優(yōu)先級降低。
3、通過指定INSERT、UPDATE、DELETE語句的LOW_PRIORITY屬性,降低該語句的優(yōu)先級。
雖然上面3種方法都是要么更新優(yōu)先,要么查詢優(yōu)先的方法,但還是可以用其來解決查詢相對重要的應(yīng)用(如用戶登錄系統(tǒng))中,讀鎖等待嚴(yán)重的問題。
另外,MySQL也提供了一種折中的辦法來調(diào)節(jié)讀寫沖突,即給系統(tǒng)參數(shù)max_write_lock_count設(shè)置一個合適的值,當(dāng)一個表的讀鎖達到這個值后,MySQL就暫時將寫請求的優(yōu)先級降低,給讀進程一定獲得鎖的機會。
上面已經(jīng)討論了寫優(yōu)先調(diào)度機制帶來的問題和解決辦法。這 里還要強調(diào)一點:一些需要長時間運行的查詢操作,也會使寫進程“餓死”!因此,應(yīng)用中應(yīng)盡量避免出現(xiàn)長時間運行的查詢操作,不要總想用一條SELECT語 句來解決問題,因為這種看似巧妙的SQL語句,往往比較復(fù)雜,執(zhí)行時間較長,在可能的情況下可以通過使用中間表等措施對SQL語句做一定的“分解”,使每 一步查詢都能在較短時間完成,從而減少鎖沖突。如果復(fù)雜查詢不可避免,應(yīng)盡量安排在數(shù)據(jù)庫空閑時段執(zhí)行,比如一些定期統(tǒng)計可以安排在夜間執(zhí)行。
后續(xù)會為大家講解InnoDB鎖。
對于以上關(guān)于mysql鎖實現(xiàn)機制原理,大家是不是覺得非常有幫助。如果需要了解更多內(nèi)容,請繼續(xù)關(guān)注我們的行業(yè)資訊,相信你會喜歡上這些內(nèi)容的。