国产成人精品18p,天天干成人网,无码专区狠狠躁天天躁,美女脱精光隐私扒开免费观看

MYsql鎖與索引的關(guān)系

發(fā)布時(shí)間:2021-09-27 17:43 來(lái)源:億速云 閱讀:0 作者:chen 欄目: Mysql 歡迎投稿:712375056

這篇文章主要講解了“鎖與索引的關(guān)系”,文中的講解內容簡(jiǎn)單清晰,易于學(xué)習與理解,下面請大家跟著(zhù)小編的思路慢慢深入,一起來(lái)研究和學(xué)習“MYsql鎖與索引的關(guān)系”吧!



mysql innodb的鎖是通過(guò)鎖索引來(lái)實(shí)現的。

select for update . 排它鎖舉例。

如果字段沒(méi)有索引,即使使用wehre條件也會(huì )進(jìn)行表級鎖

如果有索引,會(huì )鎖定對應where條件中索引值的所有行,可理解為對該索引值進(jìn)行了索引(所以即使另一事務(wù)查詢(xún)的是其他行,但因為索引值形同們也會(huì )被鎖住。)

有索引,而且使用了不同的索引值查數據,但是查詢(xún) 的結果是同一行,可以理解為真正的數據行鎖。

小結

本章重點(diǎn)介紹了MySQL中MyISAM表級鎖和InnoDB行級鎖的實(shí)現特點(diǎn),并討論了兩種存儲引擎經(jīng)常遇到的鎖問(wèn)題和解決辦法。

對于MyISAM的表鎖,主要討論了以下幾點(diǎn):

(1)共享讀鎖(S)之間是兼容的,但共享讀鎖(S)與排他寫(xiě)鎖(X)之間,以及排他寫(xiě)鎖(X)之間是互斥的,也就是說(shuō)讀和寫(xiě)是串行的。

(2)在一定條件下,MyISAM允許查詢(xún)和插入并發(fā)執行,我們可以利用這一點(diǎn)來(lái)解決應用中對同一表查詢(xún)和插入的鎖爭用問(wèn)題。

(3)MyISAM默認的鎖調度機制是寫(xiě)優(yōu)先,這并不一定適合所有應用,用戶(hù)可以通過(guò)設置LOW_PRIORITY_UPDATES參數,或在INSERT、UPDATE、DELETE語(yǔ)句中指定LOW_PRIORITY選項來(lái)調節讀寫(xiě)鎖的爭用。

(4)由于表鎖的鎖定粒度大,讀寫(xiě)之間又是串行的,因此,如果更新操作較多,MyISAM表可能會(huì )出現嚴重的鎖等待,可以考慮采用InnoDB表來(lái)減少鎖沖突。

對于InnoDB表,本章主要討論了以下幾項內容。

l         InnoDB的行鎖是基于鎖引實(shí)現的,如果不通過(guò)索引訪(fǎng)問(wèn)數據,InnoDB會(huì )使用表鎖。

l         介紹了InnoDB間隙鎖(Next-key)機制,以及InnoDB使用間隙鎖的原因。

l         在不同的隔離級別下,InnoDB的鎖機制和一致性讀策略不同。

l         MySQL的恢復和復制對InnoDB鎖機制和一致性讀策略也有較大影響。

l         鎖沖突甚至死鎖很難完全避免。

在了解InnoDB鎖特性后,用戶(hù)可以通過(guò)設計和SQL調整等措施減少鎖沖突和死鎖,包括:

l         盡量使用較低的隔離級別;

l         精心設計索引,并盡量使用索引訪(fǎng)問(wèn)數據,使加鎖更精確,從而減少鎖沖突的機會(huì );

l         選擇合理的事務(wù)大小,小事務(wù)發(fā)生鎖沖突的幾率也更??;

l         給記錄集顯示加鎖時(shí),最好一次性請求足夠級別的鎖。比如要修改數據的話(huà),最好直接申請排他鎖,而不是先申請共享鎖,修改時(shí)再請求排他鎖,這樣容易產(chǎn)生死鎖;

l         不同的程序訪(fǎng)問(wèn)一組表時(shí),應盡量約定以相同的順序訪(fǎng)問(wèn)各表,對一個(gè)表而言,盡可能以固定的順序存取表中的行。這樣可以大大減少死鎖的機會(huì );

l         盡量用相等條件訪(fǎng)問(wèn)數據,這樣可以避免間隙鎖對并發(fā)插入的影響;

l         不要申請超過(guò)實(shí)際需要的鎖級別;除非必須,查詢(xún)時(shí)不要顯示加鎖;

l         對于一些特定的事務(wù),可以使用表鎖來(lái)提高處理速度或減少死鎖的可能。

鎖是計算機協(xié)調多個(gè)進(jìn)程或線(xiàn)程并發(fā)訪(fǎng)問(wèn)某一資源的機制。在數據庫中,除傳統的計算資源(如CPU、RAM、I/O等)的爭用以外,數據也是一種供許多用戶(hù)共享的資源。如何保證數據并發(fā)訪(fǎng)問(wèn)的一致性、有效性是所有數據庫必須解決的一個(gè)問(wèn)題,鎖沖突也是影響數據庫并發(fā)訪(fǎng)問(wèn)性能的一個(gè)重要因素。從這個(gè)角度來(lái)說(shuō),鎖對數據庫而言顯得尤其重要,也更加復雜。本章我們著(zhù)重討論MySQL鎖機制的特點(diǎn),常見(jiàn)的鎖問(wèn)題,以及解決MySQL鎖問(wèn)題的一些方法或建議。

MySQL鎖概述

相對其他數據庫而言,MySQL的鎖機制比較簡(jiǎn)單,其最顯著(zhù)的特點(diǎn)是不同的存儲引擎支持不同的鎖機制。比如,MyISAM和MEMORY存儲引擎采用的是表級鎖(table-level locking);BDB存儲引擎采用的是頁(yè)面鎖(page-level locking),但也支持表級鎖;InnoDB存儲引擎既支持行級鎖(row-level locking),也支持表級鎖,但默認情況下是采用行級鎖。

MySQL這3種鎖的特性可大致歸納如下。

開(kāi)銷(xiāo)、加鎖速度、死鎖、粒度、并發(fā)性能

l         表級鎖:開(kāi)銷(xiāo)小,加鎖快;不會(huì )出現死鎖;鎖定粒度大,發(fā)生鎖沖突的概率最高,并發(fā)度最低。

l         行級鎖:開(kāi)銷(xiāo)大,加鎖慢;會(huì )出現死鎖;鎖定粒度最小,發(fā)生鎖沖突的概率最低,并發(fā)度也最高。

l         頁(yè)面鎖:開(kāi)銷(xiāo)和加鎖時(shí)間界于表鎖和行鎖之間;會(huì )出現死鎖;鎖定粒度界于表鎖和行鎖之間,并發(fā)度一般。

從上述特點(diǎn)可見(jiàn),很難籠統地說(shuō)哪種鎖更好,只能就具體應用的特點(diǎn)來(lái)說(shuō)哪種鎖更合適!僅從鎖的角度來(lái)說(shuō):表級鎖更適合于以查詢(xún)?yōu)橹?,只有少量按索引條件更新數據的應用,如Web應用;而行級鎖則更適合于有大量按索引條件并發(fā)更新少量不同數據,同時(shí)又有并發(fā)查詢(xún)的應用,如一些在線(xiàn)事務(wù)處理(OLTP)系統。這一點(diǎn)在本書(shū)的“開(kāi)發(fā)篇”介紹表類(lèi)型的選擇時(shí),也曾提到過(guò)。下面幾節我們重點(diǎn)介紹MySQL表鎖和 InnoDB行鎖的問(wèn)題,由于BDB已經(jīng)被InnoDB取代,即將成為歷史,在此就不做進(jìn)一步的討論了。


 

MyISAM表鎖

MyISAM存儲引擎只支持表鎖,這也是MySQL開(kāi)始幾個(gè)版本中唯一支持的鎖類(lèi)型。隨著(zhù)應用對事務(wù)完整性和并發(fā)性要求的不斷提高,MySQL才開(kāi)始開(kāi)發(fā)基于事務(wù)的存儲引擎,后來(lái)慢慢出現了支持頁(yè)鎖的BDB存儲引擎和支持行鎖的InnoDB存儲引擎(實(shí)際 InnoDB是單獨的一個(gè)公司,現在已經(jīng)被Oracle公司收購)。但是MyISAM的表鎖依然是使用最為廣泛的鎖類(lèi)型。本節將詳細介紹MyISAM表鎖的使用。

查詢(xún)表級鎖爭用情況

可以通過(guò)檢查table_locks_waited和table_locks_immediate狀態(tài)變量來(lái)分析系統上的表鎖定爭奪:

mysql> show status like 'table%';

+-----------------------+-------+

| Variable_name         | Value |

+-----------------------+-------+

| Table_locks_immediate | 2979  |

| Table_locks_waited    | 0     |

+-----------------------+-------+

2 rows in set (0.00 sec))

如果Table_locks_waited的值比較高,則說(shuō)明存在著(zhù)較嚴重的表級鎖爭用情況。

MySQL表級鎖的鎖模式

MySQL的表級鎖有兩種模式:表共享讀鎖(Table Read Lock)和表獨占寫(xiě)鎖(Table Write Lock)。鎖模式的兼容性如表20-1所示。

表20-1                                            MySQL中的表鎖兼容性                

可見(jiàn),對MyISAM表的讀操作,不會(huì )阻塞其他用戶(hù)對同一表的讀請求,但會(huì )阻塞對同一表的寫(xiě)請求;對 MyISAM表的寫(xiě)操作,則會(huì )阻塞其他用戶(hù)對同一表的讀和寫(xiě)操作;MyISAM表的讀操作與寫(xiě)操作之間,以及寫(xiě)操作之間是串行的!根據如表20-2所示的例子可以知道,當一個(gè)線(xiàn)程獲得對一個(gè)表的寫(xiě)鎖后,只有持有鎖的線(xiàn)程可以對表進(jìn)行更新操作。其他線(xiàn)程的讀、寫(xiě)操作都會(huì )等待,直到鎖被釋放為止。

表20-2                          MyISAM存儲引擎的寫(xiě)阻塞讀例子

如何加表鎖

MyISAM在執行查詢(xún)語(yǔ)句(SELECT)前,會(huì )自動(dòng)給涉及的所有表加讀鎖,在執行更新操作(UPDATE、DELETE、INSERT等)前,會(huì )自動(dòng)給涉及的表加寫(xiě)鎖,這個(gè)過(guò)程并不需要用戶(hù)干預,因此,用戶(hù)一般不需要直接用LOCK TABLE命令給MyISAM表顯式加鎖。在本書(shū)的示例中,顯式加鎖基本上都是為了方便而已,并非必須如此。

給MyISAM表顯示加鎖,一般是為了在一定程度模擬事務(wù)操作,實(shí)現對某一時(shí)間點(diǎn)多個(gè)表的一致性讀取。例如,有一個(gè)訂單表orders,其中記錄有各訂單的總金額total,同時(shí)還有一個(gè)訂單明細表order_detail,其中記錄有各訂單每一產(chǎn)品的金額小計 subtotal,假設我們需要檢查這兩個(gè)表的金額合計是否相符,可能就需要執行如下兩條SQL:

Select sum(total) from orders;

Select sum(subtotal) from order_detail;

這時(shí),如果不先給兩個(gè)表加鎖,就可能產(chǎn)生錯誤的結果,因為第一條語(yǔ)句執行過(guò)程中,order_detail表可能已經(jīng)發(fā)生了改變。因此,正確的方法應該是:

Lock tables orders read local, order_detail read local;

Select sum(total) from orders;

Select sum(subtotal) from order_detail;

Unlock tables;

要特別說(shuō)明以下兩點(diǎn)內容。

?  上面的例子在LOCK TABLES時(shí)加了“l(fā)ocal”選項,其作用就是在滿(mǎn)足MyISAM表并發(fā)插入條件的情況下,允許其他用戶(hù)在表尾并發(fā)插入記錄,有關(guān)MyISAM表的并發(fā)插入問(wèn)題,在后面的章節中還會(huì )進(jìn)一步介紹。

?  在用LOCK TABLES給表顯式加表鎖時(shí),必須同時(shí)取得所有涉及到表的鎖,并且MySQL不支持鎖升級。也就是說(shuō),在執行LOCK TABLES后,只能訪(fǎng)問(wèn)顯式加鎖的這些表,不能訪(fǎng)問(wèn)未加鎖的表;同時(shí),如果加的是讀鎖,那么只能執行查詢(xún)操作,而不能執行更新操作。其實(shí),在自動(dòng)加鎖的情況下也基本如此,MyISAM總是一次獲得SQL語(yǔ)句所需要的全部鎖。這也正是MyISAM表不會(huì )出現死鎖(Deadlock Free)的原因。

在如表20-3所示的例子中,一個(gè)session使用LOCK TABLE命令給表film_text加了讀鎖,這個(gè)session可以查詢(xún)鎖定表中的記錄,但更新或訪(fǎng)問(wèn)其他表都會(huì )提示錯誤;同時(shí),另外一個(gè)session可以查詢(xún)表中的記錄,但更新就會(huì )出現鎖等待。

表20-3                     MyISAM存儲引擎的讀阻塞寫(xiě)例子

當使用LOCK TABLES時(shí),不僅需要一次鎖定用到的所有表,而且,同一個(gè)表在SQL語(yǔ)句中出現多少次,就要通過(guò)與SQL語(yǔ)句中相同的別名鎖定多少次,否則也會(huì )出錯!舉例說(shuō)明如下。

(1)對actor表獲得讀鎖:

mysql> lock table actor read;

Query OK, 0 rows affected (0.00 sec)

(2)但是通過(guò)別名訪(fǎng)問(wèn)會(huì )提示錯誤:

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' 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)按照別名的查詢(xún)可以正確執行:

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)

并發(fā)插入(Concurrent Inserts)

上文提到過(guò)MyISAM表的讀和寫(xiě)是串行的,但這是就總體而言的。在一定條件下,MyISAM表也支持查詢(xún)和插入操作的并發(fā)進(jìn)行。

MyISAM存儲引擎有一個(gè)系統變量concurrent_insert,專(zhuān)門(mén)用以控制其并發(fā)插入的行為,其值分別可以為0、1或2。

l         當concurrent_insert設置為0時(shí),不允許并發(fā)插入。

l         當concurrent_insert設置為1時(shí),如果MyISAM表中沒(méi)有空洞(即表的中間沒(méi)有被刪除的行),MyISAM允許在一個(gè)進(jìn)程讀表的同時(shí),另一個(gè)進(jìn)程從表尾插入記錄。這也是MySQL的默認設置。

l         當concurrent_insert設置為2時(shí),無(wú)論MyISAM表中有沒(méi)有空洞,都允許在表尾并發(fā)插入記錄。

在如表20-4所示的例子中,session_1獲得了一個(gè)表的READ LOCAL鎖,該線(xiàn)程可以對表進(jìn)行查詢(xún)操作,但不能對表進(jìn)行更新操作;其他的線(xiàn)程(session_2),雖然不能對表進(jìn)行刪除和更新操作,但卻可以對該表進(jìn)行并發(fā)插入操作,這里假設該表中間不存在空洞。

表20-4              MyISAM存儲引擎的讀寫(xiě)(INSERT)并發(fā)例子

可以利用MyISAM存儲引擎的并發(fā)插入特性,來(lái)解決應用中對同一表查詢(xún)和插入的鎖爭用。例如,將concurrent_insert系統變量設為2,總是允許并發(fā)插入;同時(shí),通過(guò)定期在系統空閑時(shí)段執行 OPTIMIZE TABLE語(yǔ)句來(lái)整理空間碎片,收回因刪除記錄而產(chǎn)生的中間空洞。有關(guān)OPTIMIZE TABLE語(yǔ)句的詳細介紹,可以參見(jiàn)第18章中“兩個(gè)簡(jiǎn)單實(shí)用的優(yōu)化方法”一節的內容。

MyISAM的鎖調度

前面講過(guò),MyISAM存儲引擎的讀鎖和寫(xiě)鎖是互斥的,讀寫(xiě)操作是串行的。那么,一個(gè)進(jìn)程請求某個(gè) MyISAM表的讀鎖,同時(shí)另一個(gè)進(jìn)程也請求同一表的寫(xiě)鎖,MySQL如何處理呢?答案是寫(xiě)進(jìn)程先獲得鎖。不僅如此,即使讀請求先到鎖等待隊列,寫(xiě)請求后到,寫(xiě)鎖也會(huì )插到讀鎖請求之前!這是因為MySQL認為寫(xiě)請求一般比讀請求要重要。這也正是MyISAM表不太適合于有大量更新操作和查詢(xún)操作應用的原因,因為,大量的更新操作會(huì )造成查詢(xún)操作很難獲得讀鎖,從而可能永遠阻塞。這種情況有時(shí)可能會(huì )變得非常糟糕!幸好我們可以通過(guò)一些設置來(lái)調節M(mǎn)yISAM 的調度行為。

?  通過(guò)指定啟動(dòng)參數low-priority-updates,使MyISAM引擎默認給予讀請求以?xún)?yōu)先的權利。

?  通過(guò)執行命令SET LOW_PRIORITY_UPDATES=1,使該連接發(fā)出的更新請求優(yōu)先級降低。

?  通過(guò)指定INSERT、UPDATE、DELETE語(yǔ)句的LOW_PRIORITY屬性,降低該語(yǔ)句的優(yōu)先級。

雖然上面3種方法都是要么更新優(yōu)先,要么查詢(xún)優(yōu)先的方法,但還是可以用其來(lái)解決查詢(xún)相對重要的應用(如用戶(hù)登錄系統)中,讀鎖等待嚴重的問(wèn)題。

另外,MySQL也提供了一種折中的辦法來(lái)調節讀寫(xiě)沖突,即給系統參數max_write_lock_count設置一個(gè)合適的值,當一個(gè)表的讀鎖達到這個(gè)值后,MySQL就暫時(shí)將寫(xiě)請求的優(yōu)先級降低,給讀進(jìn)程一定獲得鎖的機會(huì )。

上面已經(jīng)討論了寫(xiě)優(yōu)先調度機制帶來(lái)的問(wèn)題和解決辦法。這里還要強調一點(diǎn):一些需要長(cháng)時(shí)間運行的查詢(xún)操作,也會(huì )使寫(xiě)進(jìn)程“餓死”!因此,應用中應盡量避免出現長(cháng)時(shí)間運行的查詢(xún)操作,不要總想用一條SELECT語(yǔ)句來(lái)解決問(wèn)題,因為這種看似巧妙的SQL語(yǔ)句,往往比較復雜,執行時(shí)間較長(cháng),在可能的情況下可以通過(guò)使用中間表等措施對SQL語(yǔ)句做一定的“分解”,使每一步查詢(xún)都能在較短時(shí)間完成,從而減少鎖沖突。如果復雜查詢(xún)不可避免,應盡量安排在數據庫空閑時(shí)段執行,比如一些定期統計可以安排在夜間執行。

InnoDB鎖問(wèn)題

InnoDB與MyISAM的最大不同有兩點(diǎn):一是支持事務(wù)(TRANSACTION);二是采用了行級鎖。行級鎖與表級鎖本來(lái)就有許多不同之處,另外,事務(wù)的引入也帶來(lái)了一些新問(wèn)題。下面我們先介紹一點(diǎn)背景知識,然后詳細討論InnoDB的鎖問(wèn)題。

背景知識

1.事務(wù)(Transaction)及其ACID屬性

事務(wù)是由一組SQL語(yǔ)句組成的邏輯處理單元,事務(wù)具有以下4個(gè)屬性,通常簡(jiǎn)稱(chēng)為事務(wù)的ACID屬性。

l         原子性(Atomicity):事務(wù)是一個(gè)原子操作單元,其對數據的修改,要么全都執行,要么全都不執行。

l         一致性(Consistent):在事務(wù)開(kāi)始和完成時(shí),數據都必須保持一致?tīng)顟B(tài)。這意味著(zhù)所有相關(guān)的數據規則都必須應用于事務(wù)的修改,以保持數據的完整性;事務(wù)結束時(shí),所有的內部數據結構(如B樹(shù)索引或雙向鏈表)也都必須是正確的。

l         隔離性(Isolation):數據庫系統提供一定的隔離機制,保證事務(wù)在不受外部并發(fā)操作影響的“獨立”環(huán)境執行。這意味著(zhù)事務(wù)處理過(guò)程中的中間狀態(tài)對外部是不可見(jiàn)的,反之亦然。

l         持久性(Durable):事務(wù)完成之后,它對于數據的修改是永久性的,即使出現系統故障也能夠保持。

銀行轉帳就是事務(wù)的一個(gè)典型例子。

2.并發(fā)事務(wù)處理帶來(lái)的問(wèn)題

相對于串行處理來(lái)說(shuō),并發(fā)事務(wù)處理能大大增加數據庫資源的利用率,提高數據庫系統的事務(wù)吞吐量,從而可以支持更多的用戶(hù)。但并發(fā)事務(wù)處理也會(huì )帶來(lái)一些問(wèn)題,主要包括以下幾種情況。

l  更新丟失(Lost Update):當兩個(gè)或多個(gè)事務(wù)選擇同一行,然后基于最初選定的值更新該行時(shí),由于每個(gè)事務(wù)都不知道其他事務(wù)的存在,就會(huì )發(fā)生丟失更新問(wèn)題--最后的更新覆蓋了由其他事務(wù)所做的更新。例如,兩個(gè)編輯人員制作了同一文檔的電子副本。每個(gè)編輯人員獨立地更改其副本,然后保存更改后的副本,這樣就覆蓋了原始文檔。最后保存其更改副本的編輯人員覆蓋另一個(gè)編輯人員所做的更改。如果在一個(gè)編輯人員完成并提交事務(wù)之前,另一個(gè)編輯人員不能訪(fǎng)問(wèn)同一文件,則可避免此問(wèn)題。

l  臟讀(Dirty Reads):一個(gè)事務(wù)正在對一條記錄做修改,在這個(gè)事務(wù)完成并提交前,這條記錄的數據就處于不一致?tīng)顟B(tài);這時(shí),另一個(gè)事務(wù)也來(lái)讀取同一條記錄,如果不加控制,第二個(gè)事務(wù)讀取了這些“臟”數據,并據此做進(jìn)一步的處理,就會(huì )產(chǎn)生未提交的數據依賴(lài)關(guān)系。這種現象被形象地叫做"臟讀"。

l  不可重復讀(Non-Repeatable Reads):一個(gè)事務(wù)在讀取某些數據后的某個(gè)時(shí)間,再次讀取以前讀過(guò)的數據,卻發(fā)現其讀出的數據已經(jīng)發(fā)生了改變、或某些記錄已經(jīng)被刪除了!這種現象就叫做“不可重復讀”。

l  幻讀(Phantom Reads):一個(gè)事務(wù)按相同的查詢(xún)條件重新讀取以前檢索過(guò)的數據,卻發(fā)現其他事務(wù)插入了滿(mǎn)足其查詢(xún)條件的新數據,這種現象就稱(chēng)為“幻讀”。

3.事務(wù)隔離級別

在上面講到的并發(fā)事務(wù)處理帶來(lái)的問(wèn)題中,“更新丟失”通常是應該完全避免的。但防止更新丟失,并不能單靠數據庫事務(wù)控制器來(lái)解決,需要應用程序對要更新的數據加必要的鎖來(lái)解決,因此,防止更新丟失應該是應用的責任。

“臟讀”、“不可重復讀”和“幻讀”,其實(shí)都是數據庫讀一致性問(wèn)題,必須由數據庫提供一定的事務(wù)隔離機制來(lái)解決。數據庫實(shí)現事務(wù)隔離的方式,基本上可分為以下兩種。

l  一種是在讀取數據前,對其加鎖,阻止其他事務(wù)對數據進(jìn)行修改。

l  另一種是不用加任何鎖,通過(guò)一定機制生成一個(gè)數據請求時(shí)間點(diǎn)的一致性數據快照(Snapshot),并用這個(gè)快照來(lái)提供一定級別(語(yǔ)句級或事務(wù)級)的一致性讀取。從用戶(hù)的角度來(lái)看,好像是數據庫可以提供同一數據的多個(gè)版本,因此,這種技術(shù)叫做數據多版本并發(fā)控制(MultiVersion Concurrency Control,簡(jiǎn)稱(chēng)MVCC或MCC),也經(jīng)常稱(chēng)為多版本數據庫。

數據庫的事務(wù)隔離越嚴格,并發(fā)副作用越小,但付出的代價(jià)也就越大,因為事務(wù)隔離實(shí)質(zhì)上就是使事務(wù)在一定程度上 “串行化”進(jìn)行,這顯然與“并發(fā)”是矛盾的。同時(shí),不同的應用對讀一致性和事務(wù)隔離程度的要求也是不同的,比如許多應用對“不可重復讀”和“幻讀”并不敏感,可能更關(guān)心數據并發(fā)訪(fǎng)問(wèn)的能力。

為了解決“隔離”與“并發(fā)”的矛盾,ISO/ANSI SQL92定義了4個(gè)事務(wù)隔離級別,每個(gè)級別的隔離程度不同,允許出現的副作用也不同,應用可以根據自己的業(yè)務(wù)邏輯要求,通過(guò)選擇不同的隔離級別來(lái)平衡 “隔離”與“并發(fā)”的矛盾。表20-5很好地概括了這4個(gè)隔離級別的特性。

表20-5                                             4種隔離級別比較

最后要說(shuō)明的是:各具體數據庫并不一定完全實(shí)現了上述4個(gè)隔離級別,例如,Oracle只提供Read committed和Serializable兩個(gè)標準隔離級別,另外還提供自己定義的Read only隔離級別;SQL Server除支持上述ISO/ANSI SQL92定義的4個(gè)隔離級別外,還支持一個(gè)叫做“快照”的隔離級別,但嚴格來(lái)說(shuō)它是一個(gè)用MVCC實(shí)現的Serializable隔離級別。MySQL 支持全部4個(gè)隔離級別,但在具體實(shí)現時(shí),有一些特點(diǎn),比如在一些隔離級別下是采用MVCC一致性讀,但某些情況下又不是,這些內容在后面的章節中將會(huì )做進(jìn)一步介紹。

獲取InnoDB行鎖爭用情況    

可以通過(guò)檢查InnoDB_row_lock狀態(tài)變量來(lái)分析系統上的行鎖的爭奪情況:

mysql> show status like 'innodb_row_lock%';

+-------------------------------+-------+

| Variable_name                 | Value |

+-------------------------------+-------+

| InnoDB_row_lock_current_waits | 0     |

| InnoDB_row_lock_time          | 0     |

| InnoDB_row_lock_time_avg      | 0     |

| InnoDB_row_lock_time_max      | 0     |

| InnoDB_row_lock_waits         | 0     |

+-------------------------------+-------+

5 rows in set (0.01 sec)

如果發(fā)現鎖爭用比較嚴重,如InnoDB_row_lock_waits和InnoDB_row_lock_time_avg的值比較高,還可以通過(guò)設置InnoDB Monitors來(lái)進(jìn)一步觀(guān)察發(fā)生鎖沖突的表、數據行等,并分析鎖爭用的原因。

具體方法如下:

mysql> CREATE TABLE innodb_monitor(a INT) ENGINE=INNODB;

Query OK, 0 rows affected (0.14 sec)

然后就可以用下面的語(yǔ)句來(lái)進(jìn)行查看:

mysql> Show innodb status\G;

*************************** 1. row ***************************

  Type: InnoDB

  Name:

Status:

------------

TRANSACTIONS

------------

Trx id counter 0 117472192

Purge done for trx's n:o < 0 117472190 undo n:o < 0 0

History list length 17

Total number of lock structs in row lock hash table 0

LIST OF TRANSACTIONS FOR EACH SESSION:

---TRANSACTION 0 117472185, not started, process no 11052, OS thread id 1158191456

MySQL thread id 200610, query id 291197 localhost root

---TRANSACTION 0 117472183, not started, process no 11052, OS thread id 1158723936

MySQL thread id 199285, query id 291199 localhost root

Show innodb status

監視器可以通過(guò)發(fā)出下列語(yǔ)句來(lái)停止查看:

mysql> DROP TABLE innodb_monitor;

Query OK, 0 rows affected (0.05 sec)

設置監視器后,在SHOW INNODB STATUS的顯示內容中,會(huì )有詳細的當前鎖等待的信息,包括表名、鎖類(lèi)型、鎖定記錄的情況等,便于進(jìn)行進(jìn)一步的分析和問(wèn)題的確定。打開(kāi)監視器以后,默認情況下每15秒會(huì )向日志中記錄監控的內容,如果長(cháng)時(shí)間打開(kāi)會(huì )導致.err文件變得非常的巨大,所以用戶(hù)在確認問(wèn)題原因之后,要記得刪除監控表以關(guān)閉監視器,或者通過(guò)使用“--console”選項來(lái)啟動(dòng)以關(guān)閉寫(xiě)日志文件。

InnoDB的行鎖模式及加鎖方法

InnoDB實(shí)現了以下兩種類(lèi)型的行鎖。

l  共享鎖(S):允許一個(gè)事務(wù)去讀一行,阻止其他事務(wù)獲得相同數據集的排他鎖。

l  排他鎖(X):允許獲得排他鎖的事務(wù)更新數據,阻止其他事務(wù)取得相同數據集的共享讀鎖和排他寫(xiě)鎖。

另外,為了允許行鎖和表鎖共存,實(shí)現多粒度鎖機制,InnoDB還有兩種內部使用的意向鎖(Intention Locks),這兩種意向鎖都是表鎖。

l  意向共享鎖(IS):事務(wù)打算給數據行加行共享鎖,事務(wù)在給一個(gè)數據行加共享鎖前必須先取得該表的IS鎖。

l  意向排他鎖(IX):事務(wù)打算給數據行加行排他鎖,事務(wù)在給一個(gè)數據行加排他鎖前必須先取得該表的IX鎖。

上述鎖模式的兼容情況具體如表20-6所示。

表20-6                                            InnoDB行鎖模式兼容性列表

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

意向鎖是InnoDB自動(dòng)加的,不需用戶(hù)干預。對于UPDATE、DELETE和INSERT語(yǔ)句,InnoDB會(huì )自動(dòng)給涉及數據集加排他鎖(X);對于普通SELECT語(yǔ)句,InnoDB不會(huì )加任何鎖;事務(wù)可以通過(guò)以下語(yǔ)句顯示給記錄集加共享鎖或排他鎖。

?  共享鎖(S):SELECT * FROM table_name WHERE ... LOCK IN SHARE MODE。

?  排他鎖(X):SELECT * FROM table_name WHERE ... FOR UPDATE。

用SELECT ... IN SHARE MODE獲得共享鎖,主要用在需要數據依存關(guān)系時(shí)來(lái)確認某行記錄是否存在,并確保沒(méi)有人對這個(gè)記錄進(jìn)行UPDATE或者DELETE操作。但是如果當前事務(wù)也需要對該記錄進(jìn)行更新操作,則很有可能造成死鎖,對于鎖定行記錄后需要進(jìn)行更新操作的應用,應該使用SELECT... FOR UPDATE方式獲得排他鎖。

在如表20-7所示的例子中,使用了SELECT ... IN SHARE MODE加鎖后再更新記錄,看看會(huì )出現什么情況,其中actor表的actor_id字段為主鍵。

表20-7  InnoDB存儲引擎的共享鎖例子

    當使用SELECT...FOR UPDATE加鎖后再更新記錄,出現如表20-8所示的情況。

表20-8 InnoDB存儲引擎的排他鎖例子

InnoDB行鎖實(shí)現方式

InnoDB行鎖是通過(guò)給索引上的索引項加鎖來(lái)實(shí)現的,這一點(diǎn)MySQL與Oracle不同,后者是通過(guò)在數據塊中對相應數據行加鎖來(lái)實(shí)現的。InnoDB這種行鎖實(shí)現特點(diǎn)意味著(zhù):只有通過(guò)索引條件檢索數據,InnoDB才使用行級鎖,否則,InnoDB將使用表鎖!

在實(shí)際應用中,要特別注意InnoDB行鎖的這一特性,不然的話(huà),可能導致大量的鎖沖突,從而影響并發(fā)性能。下面通過(guò)一些實(shí)際例子來(lái)加以說(shuō)明。

(1)在不通過(guò)索引條件查詢(xún)的時(shí)候,InnoDB確實(shí)使用的是表鎖,而不是行鎖。

在如表20-9所示的例子中,開(kāi)始tab_no_index表沒(méi)有索引:

mysql> create table tab_no_index(id int,name varchar(10)) engine=innodb;

Query OK, 0 rows affected (0.15 sec)

mysql> insert into tab_no_index values(1,'1'),(2,'2'),(3,'3'),(4,'4');

Query OK, 4 rows affected (0.00 sec)

Records: 4  Duplicates: 0  Warnings: 0

表20-9   InnoDB存儲引擎的表在不使用索引時(shí)使用表鎖例子

在如表20 -9所示的例子中,看起來(lái)session_1只給一行加了排他鎖,但session_2在請求其他行的排他鎖時(shí),卻出現了鎖等待!原因就是在沒(méi)有索引的情況下,InnoDB只能使用表鎖。當我們給其增加一個(gè)索引后,InnoDB就只鎖定了符合條件的行,如表20-10所示。

創(chuàng )建tab_with_index表,id字段有普通索引:

mysql> create table tab_with_index(id int,name varchar(10)) engine=innodb;

Query OK, 0 rows affected (0.15 sec)

mysql> alter table tab_with_index add index id(id);

Query OK, 4 rows affected (0.24 sec)

Records: 4  Duplicates: 0  Warnings: 0

表20-10   InnoDB存儲引擎的表在使用索引時(shí)使用行鎖例子

(2)由于MySQL的行鎖是針對索引加的鎖,不是針對記錄加的鎖,所以雖然是訪(fǎng)問(wèn)不同行的記錄,但是如果是使用相同的索引鍵,是會(huì )出現鎖沖突的。應用設計的時(shí)候要注意這一點(diǎn)。

在如表20-11所示的例子中,表tab_with_index的id字段有索引,name字段沒(méi)有索引:

mysql> alter table tab_with_index drop index name;

Query OK, 4 rows affected (0.22 sec)

Records: 4  Duplicates: 0  Warnings: 0

mysql> insert into tab_with_index  values(1,'4');

Query OK, 1 row affected (0.00 sec)

mysql> select * from tab_with_index where id = 1;

+------+------+

| id   | name |

+------+------+

| 1    | 1    |

| 1    | 4    |

+------+------+

2 rows in set (0.00 sec)

表20-11 InnoDB存儲引擎使用相同索引鍵的阻塞例子       

(3)當表有多個(gè)索引的時(shí)候,不同的事務(wù)可以使用不同的索引鎖定不同的行,另外,不論是使用主鍵索引、唯一索引或普通索引,InnoDB都會(huì )使用行鎖來(lái)對數據加鎖。

在如表20-12所示的例子中,表tab_with_index的id字段有主鍵索引,name字段有普通索引:

mysql> alter table tab_with_index add index name(name);

Query OK, 5 rows affected (0.23 sec)

Records: 5  Duplicates: 0  Warnings: 0

表20-12  InnoDB存儲引擎的表使用不同索引的阻塞例子

(4)即便在條件中使用了索引字段,但是否使用索引來(lái)檢索數據是由MySQL通過(guò)判斷不同執行計劃的代價(jià)來(lái)決定的,如果MySQL認為全表掃描效率更高,比如對一些很小的表,它就不會(huì )使用索引,這種情況下InnoDB將使用表鎖,而不是行鎖。因此,在分析鎖沖突時(shí),別忘了檢查SQL的執行計劃,以確認是否真正使用了索引。關(guān)于MySQL在什么情況下不使用索引的詳細討論,參見(jiàn)本章“索引問(wèn)題”一節的介紹。

在下面的例子中,檢索值的數據類(lèi)型與索引字段不同,雖然MySQL能夠進(jìn)行數據類(lèi)型轉換,但卻不會(huì )使用索引,從而導致InnoDB使用表鎖。通過(guò)用explain檢查兩條SQL的執行計劃,我們可以清楚地看到了這一點(diǎn)。

例子中tab_with_index表的name字段有索引,但是name字段是varchar類(lèi)型的,如果where條件中不是和varchar類(lèi)型進(jìn)行比較,則會(huì )對name進(jìn)行類(lèi)型轉換,而執行的全表掃描。

mysql> alter table tab_no_index add index name(name);

Query OK, 4 rows affected (8.06 sec)

Records: 4  Duplicates: 0  Warnings: 0

mysql> explain select * from tab_with_index where name = 1 \G

*************************** 1. row ***************************

           id: 1

  select_type: SIMPLE

        table: tab_with_index

         type: ALL

possible_keys: name

          key: NULL

      key_len: NULL

          ref: NULL

         rows: 4

        Extra: Using where

1 row in set (0.00 sec)

mysql> explain select * from tab_with_index where name = '1' \G

*************************** 1. row ***************************

           id: 1

  select_type: SIMPLE

        table: tab_with_index

         type: ref

possible_keys: name

          key: name

      key_len: 23

          ref: const

         rows: 1

        Extra: Using where

1 row in set (0.00 sec)

間隙鎖(Next-Key鎖)

當我們用范圍條件而不是相等條件檢索數據,并請求共享或排他鎖時(shí),InnoDB會(huì )給符合條件的已有數據記錄的索引項加鎖;對于鍵值在條件范圍內但并不存在的記錄,叫做“間隙(GAP)”,InnoDB也會(huì )對這個(gè)“間隙”加鎖,這種鎖機制就是所謂的間隙鎖(Next-Key鎖)。

舉例來(lái)說(shuō),假如emp表中只有101條記錄,其empid的值分別是 1,2,...,100,101,下面的SQL:

Select * from  emp where empid > 100 for update;

是一個(gè)范圍條件的檢索,InnoDB不僅會(huì )對符合條件的empid值為101的記錄加鎖,也會(huì )對empid大于101(這些記錄并不存在)的“間隙”加鎖。

InnoDB使用間隙鎖的目的,一方面是為了防止幻讀,以滿(mǎn)足相關(guān)隔離級別的要求,對于上面的例子,要是不使用間隙鎖,如果其他事務(wù)插入了empid大于100的任何記錄,那么本事務(wù)如果再次執行上述語(yǔ)句,就會(huì )發(fā)生幻讀;另外一方面,是為了滿(mǎn)足其恢復和復制的需要。有關(guān)其恢復和復制對鎖機制的影響,以及不同隔離級別下InnoDB使用間隙鎖的情況,在后續的章節中會(huì )做進(jìn)一步介紹。

很顯然,在使用范圍條件檢索并鎖定記錄時(shí),InnoDB這種加鎖機制會(huì )阻塞符合條件范圍內鍵值的并發(fā)插入,這往往會(huì )造成嚴重的鎖等待。因此,在實(shí)際應用開(kāi)發(fā)中,尤其是并發(fā)插入比較多的應用,我們要盡量?jì)?yōu)化業(yè)務(wù)邏輯,盡量使用相等條件來(lái)訪(fǎng)問(wèn)更新數據,避免使用范圍條件。

還要特別說(shuō)明的是,InnoDB除了通過(guò)范圍條件加鎖時(shí)使用間隙鎖外,如果使用相等條件請求給一個(gè)不存在的記錄加鎖,InnoDB也會(huì )使用間隙鎖!

在如表20-13所示的例子中,假如emp表中只有101條記錄,其empid的值分別是1,2,......,100,101。

表20-13                InnoDB存儲引擎的間隙鎖阻塞例子

免責聲明:本站發(fā)布的內容(圖片、視頻和文字)以原創(chuàng )、來(lái)自本網(wǎng)站內容采集于網(wǎng)絡(luò )互聯(lián)網(wǎng)轉載等其它媒體和分享為主,內容觀(guān)點(diǎn)不代表本網(wǎng)站立場(chǎng),如侵犯了原作者的版權,請告知一經(jīng)查實(shí),將立刻刪除涉嫌侵權內容,聯(lián)系我們QQ:712375056,同時(shí)歡迎投稿傳遞力量。

国产高清中文手机在线观看| 亚洲AV综合色一区二区三区| 国产精品美女久久久网站| 免费无码成人AV片在线在线播放| 少妇人妻真实偷人精品视频| 51妺嘿嘿午夜福利|