MySql 行級(jí)鎖 表級(jí)鎖
如何保證數(shù)據(jù)并發(fā)訪問(wèn)的一致性、有效性是所有數(shù)據(jù)庫(kù)必須解決的一個(gè)問(wèn)題,鎖沖突也是影響數(shù)據(jù)庫(kù)并發(fā)訪問(wèn)性能的一個(gè)重要因素。從這個(gè)角度來(lái)說(shuō),鎖對(duì)數(shù)據(jù)庫(kù)而言顯得尤其重要,也更加復(fù)雜。
MySQL鎖概述
相對(duì)其他數(shù)據(jù)庫(kù)而言,MySQL的鎖機(jī)制比較簡(jiǎn)單,其最顯著的特點(diǎn)是不同的存儲(chǔ)引擎支持不同的鎖機(jī)制。
比如
MyISAM和MEMORY存儲(chǔ)引擎采用的是表級(jí)鎖(table-level locking)。
InnoDB存儲(chǔ)引擎既支持行級(jí)鎖(row-level locking),也支持表級(jí)鎖,但默認(rèn)情況下是采用行級(jí)鎖。
MySQL這3種鎖的特性可大致歸納如下
表級(jí)鎖:開(kāi)銷(xiāo)小,加鎖快;不會(huì)出現(xiàn)死鎖;鎖定粒度大,發(fā)生鎖沖突的概率最高,并發(fā)度最低。
行級(jí)鎖:開(kāi)銷(xiāo)大,加鎖慢;會(huì)出現(xiàn)死鎖;鎖定粒度最小,發(fā)生鎖沖突的概率最低,并發(fā)度也最高。
頁(yè)面鎖:開(kāi)銷(xiāo)和加鎖時(shí)間界于表鎖和行鎖之間;會(huì)出現(xiàn)死鎖;鎖定粒度界于表鎖和行鎖之間,并發(fā)度一般。
僅從鎖的角度來(lái)說(shuō):表級(jí)鎖更適合于以查詢?yōu)橹鳎挥猩倭堪此饕龡l件更新數(shù)據(jù)的應(yīng)用,如Web應(yīng)用;而行級(jí)鎖則更適合于有大量按索引條件并發(fā)更新少量不同數(shù)據(jù),同時(shí)又有并發(fā)查詢的應(yīng)用,如一些在線事務(wù)處理(OLTP)系統(tǒng)。下面幾節(jié)我們重點(diǎn)介紹MySQL表鎖和 InnoDB行鎖的問(wèn)題。
MyISAM表鎖
MyISAM存儲(chǔ)引擎只支持表鎖,這也是MySQL開(kāi)始幾個(gè)版本中唯一支持的鎖類(lèi)型。隨著應(yīng)用對(duì)事務(wù)完整性和并發(fā)性要求的不斷提高,MySQL才開(kāi)始開(kāi)發(fā)基于事務(wù)的存儲(chǔ)引擎,后來(lái)慢慢出現(xiàn)了支持頁(yè)鎖的BDB存儲(chǔ)引擎和支持行鎖的InnoDB存儲(chǔ)引擎(實(shí)際 InnoDB是單獨(dú)的一個(gè)公司,現(xiàn)在已經(jīng)被Oracle公司收購(gòu))。但是MyISAM的表鎖依然是使用最為廣泛的鎖類(lèi)型。本節(jié)將詳細(xì)介紹MyISAM表鎖的使用。
查詢表級(jí)鎖爭(zhēng)用情況
可以通過(guò)檢查table_locks_waited和table_locks_immediate狀態(tài)變量來(lái)分析系統(tǒng)上的表鎖定爭(zhēng)奪:
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ō)明存在著較嚴(yán)重的表級(jí)鎖爭(zhēng)用情況。
MySQL表級(jí)鎖的鎖模式
MySQL的表級(jí)鎖有兩種模式:表共享讀鎖(Table Read Lock)和表獨(dú)占寫(xiě)鎖(Table Write Lock)。
MyISAM表的讀操作,不會(huì)阻塞其他用戶對(duì)同一表的讀請(qǐng)求,但會(huì)阻塞對(duì)同一表的寫(xiě)請(qǐng)求;
MyISAM表的寫(xiě)操作,則會(huì)阻塞其他用戶對(duì)同一表的讀和寫(xiě)操作;
MyISAM表的讀操作與寫(xiě)操作之間,以及寫(xiě)操作之間是串行的;
當(dāng)一個(gè)線程獲得對(duì)一個(gè)表的寫(xiě)鎖后,只有持有鎖的線程可以對(duì)表進(jìn)行更新操作。其他線程的讀、寫(xiě)操作都會(huì)等待,直到鎖被釋放為止。
獲得表film_text的WRITE鎖定
mysql> lock table film_text write;
Query OK, 0 rows affected (0.00 sec)
當(dāng)前session對(duì)鎖定表的查詢、更新、插入操作都可以執(zhí)行:
mysql> select film_id,title from film_text where film_id = 1001;
| film_id | title |
| 1001 | Update Test |
1 row in set (0.00 sec)
mysql> insert into film_text (film_id,title) values(1003,'Test');
Query OK, 1 row affected (0.00 sec)
mysql> update film_text set title = 'Test' where film_id = 1001;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0
其他session對(duì)鎖定表的查詢被阻塞,需要等待鎖被釋放:
mysql> select film_id,title from film_text where film_id = 1001;
等待
釋放鎖:
mysql> unlock tables;
Query OK, 0 rows affected (0.00 sec)
等待
Session2獲得鎖,查詢返回:
mysql> select film_id,title from film_text where film_id = 1001;
| film_id | title |
| 1001 | Test |
1 row in set (57.59 sec)
如何加表鎖?
MyISAM在執(zhí)行查詢語(yǔ)句(SELECT)前,會(huì)自動(dòng)給涉及的所有表加讀鎖,在執(zhí)行更新操作(UPDATE、DELETE、INSERT等)前,會(huì)自動(dòng)給涉及的表加寫(xiě)鎖,這個(gè)過(guò)程并不需要用戶干預(yù),因此,用戶一般不需要直接用LOCK TABLE命令給MyISAM表顯式加鎖。在示例中,顯式加鎖基本上都是為了方便而已,并非必須如此。
給MyISAM表顯示加鎖,一般是為了在一定程度模擬事務(wù)操作,實(shí)現(xiàn)對(duì)某一時(shí)間點(diǎn)多個(gè)表的一致性讀取。例如,有一個(gè)訂單表orders,其中記錄有各訂單的總金額total,同時(shí)還有一個(gè)訂單明細(xì)表order_detail,其中記錄有各訂單每一產(chǎn)品的金額小計(jì) subtotal,假設(shè)我們需要檢查這兩個(gè)表的金額合計(jì)是否相符,可能就需要執(zhí)行如下兩條SQL:
Select sum(total) from orders;
Select sum(subtotal) from order_detail;
這時(shí),如果不先給兩個(gè)表加鎖,就可能產(chǎn)生錯(cuò)誤的結(jié)果,因?yàn)榈谝粭l語(yǔ)句執(zhí)行過(guò)程中,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;
要特別說(shuō)明以下兩點(diǎn)內(nèi)容。
上面的例子在LOCK TABLES時(shí)加了“l(fā)ocal”選項(xiàng),其作用就是在滿足MyISAM表并發(fā)插入條件的情況下,允許其他用戶在表尾并發(fā)插入記錄,有關(guān)MyISAM表的并發(fā)插入問(wèn)題,后面還會(huì)進(jìn)一步介紹。
在用LOCK TABLES給表顯式加表鎖時(shí),必須同時(shí)取得所有涉及到表的鎖,并且MySQL不支持鎖升級(jí)。也就是說(shuō),在執(zhí)行LOCK TABLES后,只能訪問(wèn)顯式加鎖的這些表,不能訪問(wèn)未加鎖的表;同時(shí),如果加的是讀鎖,那么只能執(zhí)行查詢操作,而不能執(zhí)行更新操作。其實(shí),在自動(dòng)加鎖的情況下也基本如此,MyISAM總是一次獲得SQL語(yǔ)句所需要的全部鎖。這也正是MyISAM表不會(huì)出現(xiàn)死鎖(Deadlock Free)的原因。
一個(gè)session使用LOCK TABLE命令給表film_text加了讀鎖,這個(gè)session可以查詢鎖定表中的記錄,但更新或訪問(wèn)其他表都會(huì)提示錯(cuò)誤;同時(shí),另外一個(gè)session可以查詢表中的記錄,但更新就會(huì)出現(xiàn)鎖等待。
當(dāng)使用LOCK TABLES時(shí),不僅需要一次鎖定用到的所有表,而且,同一個(gè)表在SQL語(yǔ)句中出現(xiàn)多少次,就要通過(guò)與SQL語(yǔ)句中相同的別名鎖定多少次,否則也會(huì)出錯(cuò)!
舉例說(shuō)明如下。
(1)對(duì)actor表獲得讀鎖:
mysql> lock table actor read;
Query OK, 0 rows affected (0.00 sec)
(2)但是通過(guò)別名訪問(wèn)會(huì)提示錯(cuò)誤:
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)需要對(duì)別名分別鎖定:
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)
并發(fā)插入(Concurrent Inserts)
上文提到過(guò)MyISAM表的讀和寫(xiě)是串行的,但這是就總體而言的。在一定條件下,MyISAM表也支持查詢和插入操作的并發(fā)進(jìn)行。
MyISAM存儲(chǔ)引擎有一個(gè)系統(tǒng)變量concurrent_insert,專(zhuān)門(mén)用以控制其并發(fā)插入的行為,其值分別可以為0、1或2。
當(dāng)concurrent_insert設(shè)置為0時(shí),不允許并發(fā)插入。
當(dāng)concurrent_insert設(shè)置為1時(shí),如果MyISAM表中沒(méi)有空洞(即表的中間沒(méi)有被刪除的行),MyISAM允許在一個(gè)進(jìn)程讀表的同時(shí),另一個(gè)進(jìn)程從表尾插入記錄。這也是MySQL的默認(rèn)設(shè)置。
當(dāng)concurrent_insert設(shè)置為2時(shí),無(wú)論MyISAM表中有沒(méi)有空洞,都允許在表尾并發(fā)插入記錄。
可以利用MyISAM存儲(chǔ)引擎的并發(fā)插入特性,來(lái)解決應(yīng)用中對(duì)同一表查詢和插入的鎖爭(zhēng)用。例如,將concurrent_insert系統(tǒng)變量設(shè)為2,總是允許并發(fā)插入;同時(shí),通過(guò)定期在系統(tǒng)空閑時(shí)段執(zhí)行 OPTIMIZE TABLE語(yǔ)句來(lái)整理空間碎片,收回因刪除記錄而產(chǎn)生的中間空洞。有關(guān)OPTIMIZE TABLE語(yǔ)句的詳細(xì)介紹,可以參見(jiàn)第18章中“兩個(gè)簡(jiǎn)單實(shí)用的優(yōu)化方法”一節(jié)的內(nèi)容。
MyISAM的鎖調(diào)度
前面講過(guò),MyISAM存儲(chǔ)引擎的讀鎖和寫(xiě)鎖是互斥的,讀寫(xiě)操作是串行的。那么,一個(gè)進(jìn)程請(qǐng)求某個(gè) MyISAM表的讀鎖,同時(shí)另一個(gè)進(jìn)程也請(qǐng)求同一表的寫(xiě)鎖,MySQL如何處理呢?答案是寫(xiě)進(jìn)程先獲得鎖。不僅如此,即使讀請(qǐng)求先到鎖等待隊(duì)列,寫(xiě)請(qǐng)求后到,寫(xiě)鎖也會(huì)插到讀鎖請(qǐng)求之前!這是因?yàn)镸ySQL認(rèn)為寫(xiě)請(qǐng)求一般比讀請(qǐng)求要重要。這也正是MyISAM表不太適合于有大量更新操作和查詢操作應(yīng)用的原因,因?yàn)?,大量的更新操作?huì)造成查詢操作很難獲得讀鎖,從而可能永遠(yuǎn)阻塞。這種情況有時(shí)可能會(huì)變得非常糟糕!幸好我們可以通過(guò)一些設(shè)置來(lái)調(diào)節(jié)MyISAM 的調(diào)度行為。
通過(guò)指定啟動(dòng)參數(shù)low-priority-updates,使MyISAM引擎默認(rèn)給予讀請(qǐng)求以優(yōu)先的權(quán)利。
通過(guò)執(zhí)行命令SET LOW_PRIORITY_UPDATES=1,使該連接發(fā)出的更新請(qǐng)求優(yōu)先級(jí)降低。
通過(guò)指定INSERT、UPDATE、DELETE語(yǔ)句的LOW_PRIORITY屬性,降低該語(yǔ)句的優(yōu)先級(jí)。
雖然上面3種方法都是要么更新優(yōu)先,要么查詢優(yōu)先的方法,但還是可以用其來(lái)解決查詢相對(duì)重要的應(yīng)用(如用戶登錄系統(tǒng))中,讀鎖等待嚴(yán)重的問(wèn)題。
另外,MySQL也提供了一種折中的辦法來(lái)調(diào)節(jié)讀寫(xiě)沖突,即給系統(tǒng)參數(shù)max_write_lock_count設(shè)置一個(gè)合適的值,當(dāng)一個(gè)表的讀鎖達(dá)到這個(gè)值后,MySQL就暫時(shí)將寫(xiě)請(qǐng)求的優(yōu)先級(jí)降低,給讀進(jìn)程一定獲得鎖的機(jī)會(huì)。
上面已經(jīng)討論了寫(xiě)優(yōu)先調(diào)度機(jī)制帶來(lái)的問(wèn)題和解決辦法。這里還要強(qiáng)調(diào)一點(diǎn):一些需要長(zhǎng)時(shí)間運(yùn)行的查詢操作,也會(huì)使寫(xiě)進(jìn)程“餓死”!因此,應(yīng)用中應(yīng)盡量避免出現(xiàn)長(zhǎng)時(shí)間運(yùn)行的查詢操作,不要總想用一條SELECT語(yǔ)句來(lái)解決問(wèn)題,因?yàn)檫@種看似巧妙的SQL語(yǔ)句,往往比較復(fù)雜,執(zhí)行時(shí)間較長(zhǎng),在可能的情況下可以通過(guò)使用中間表等措施對(duì)SQL語(yǔ)句做一定的“分解”,使每一步查詢都能在較短時(shí)間完成,從而減少鎖沖突。如果復(fù)雜查詢不可避免,應(yīng)盡量安排在數(shù)據(jù)庫(kù)空閑時(shí)段執(zhí)行,比如一些定期統(tǒng)計(jì)可以安排在夜間執(zhí)行。
InnoDB鎖
InnoDB與MyISAM的最大不同有兩點(diǎn):一是支持事務(wù)(TRANSACTION);二是采用了行級(jí)鎖。行級(jí)鎖與表級(jí)鎖本來(lái)就有許多不同之處,另外,事務(wù)的引入也帶來(lái)了一些新問(wèn)題。下面我們先介紹一點(diǎn)背景知識(shí),然后詳細(xì)討論InnoDB的鎖問(wèn)題。
1.事務(wù)(Transaction)及其ACID屬性
事務(wù)是由一組SQL語(yǔ)句組成的邏輯處理單元,事務(wù)具有以下4個(gè)屬性,通常簡(jiǎn)稱為事務(wù)的ACID屬性。
(Atomicity)原子性:事務(wù)是一個(gè)原子操作單元,其對(duì)數(shù)據(jù)的修改,要么全都執(zhí)行,要么全都不執(zhí)行。
(Consistent)一致性:在事務(wù)開(kāi)始和完成時(shí),數(shù)據(jù)都必須保持一致?tīng)顟B(tài)。這意味著所有相關(guān)的數(shù)據(jù)規(guī)則都必須應(yīng)用于事務(wù)的修改,以保持?jǐn)?shù)據(jù)的完整性;事務(wù)結(jié)束時(shí),所有的內(nèi)部數(shù)據(jù)結(jié)構(gòu)(如B樹(shù)索引或雙向鏈表)也都必須是正確的。
(Isolation)隔離性:數(shù)據(jù)庫(kù)系統(tǒng)提供一定的隔離機(jī)制,保證事務(wù)在不受外部并發(fā)操作影響的“獨(dú)立”環(huán)境執(zhí)行。這意味著事務(wù)處理過(guò)程中的中間狀態(tài)對(duì)外部是不可見(jiàn)的,反之亦然。
(Durable)持久性:事務(wù)完成之后,它對(duì)于數(shù)據(jù)的修改是永久性的,即使出現(xiàn)系統(tǒng)故障也能夠保持。
銀行轉(zhuǎn)帳就是事務(wù)的一個(gè)典型例子。
2.并發(fā)事務(wù)處理帶來(lái)的問(wèn)題
相對(duì)于串行處理來(lái)說(shuō),并發(fā)事務(wù)處理能大大增加數(shù)據(jù)庫(kù)資源的利用率,提高數(shù)據(jù)庫(kù)系統(tǒng)的事務(wù)吞吐量,從而可以支持
相關(guān)推薦
- 域名過(guò)期多久后才可以重新注冊(cè)?RAKsmart域名攻略
- RAKsmart防護(hù)配置實(shí)戰(zhàn):10Gbps套餐部署指南
- 如何利用RAKsmart服務(wù)器實(shí)現(xiàn)高效多站點(diǎn)部署方案
- 華納云香港高防服務(wù)器150G防御4.6折促銷(xiāo),低至6888元/月,CN2大帶寬直連清洗,終身循環(huán)折扣
- RakSmart服務(wù)器成本優(yōu)化策略
- 2025年國(guó)內(nèi)免費(fèi)AI工具推薦:文章生成與圖像創(chuàng)作全攻略
- 自媒體推廣實(shí)時(shí)監(jiān)控從服務(wù)器帶寬到用戶行為解決方法
- AI時(shí)代,個(gè)人站長(zhǎng)如何用AI工具實(shí)現(xiàn)“一人公司”