MySQL锁机制(myisam表所与innoDB锁)
目錄
1、MySQL鎖的基本介紹
2、MyISAM表鎖
2.1寫鎖阻塞讀
2.2讀阻塞寫
3、InnoDB鎖
3.1、事務及其ACID屬性
3.2、并發事務帶來的問題
3.3.1、在不通過索引條件查詢的時候,innodb使用的是表鎖而不是行鎖
3.3.2、創建帶索引的表進行條件查詢,innodb使用的是行鎖
總結
1、MySQL鎖的基本介紹
鎖是計算機協調多個進程或線程并發訪問某一資源的機制。在數據庫中,除傳統的 計算資源(如CPU、RAM、I/O等)的爭用以外,數據也是一種供許多用戶共享的資源。如何保證數據并發訪問的一致性、有效性是所有數據庫必須解決的一 個問題,鎖沖突也是影響數據庫并發訪問性能的一個重要因素。從這個角度來說,鎖對數據庫而言顯得尤其重要,也更加復雜。
? 相對其他數據庫而言,MySQL的鎖機制比較簡單,其最 顯著的特點是不同的存儲引擎支持不同的鎖機制。比如,MyISAM和MEMORY存儲引擎采用的是表級鎖(table-level locking);InnoDB存儲引擎既支持行級鎖(row-level locking),也支持表級鎖,但默認情況下是采用行級鎖。
?表級鎖:開銷小,加鎖快;不會出現死鎖;鎖定粒度大,發生鎖沖突的概率最高,并發度最低。 ?
行級鎖:開銷大,加鎖慢;會出現死鎖;鎖定粒度最小,發生鎖沖突的概率最低,并發度也最高。
? 從上述特點可見,很難籠統地說哪種鎖更好,只能就具體應用的特點來說哪種鎖更合適!僅從鎖的角度 來說:表級鎖更適合于以查詢為主,只有少量按索引條件更新數據的應用,如Web應用;而行級鎖則更適合于有大量按索引條件并發更新少量不同數據,同時又有 并發查詢的應用,如一些在線事務處理(OLTP)系統。
2、MyISAM表鎖
MySQL的表級鎖有兩種模式:表共享讀鎖(Table Read Lock)和表獨占寫鎖(Table Write Lock)。
對MyISAM表的讀操作,不會阻塞其他用戶對同一表的讀請求,但會阻塞對同一表的寫請求;對 MyISAM表的寫操作,則會阻塞其他用戶對同一表的讀和寫操作;MyISAM表的讀操作與寫操作之間,以及寫操作之間是串行的!
建表語句:
CREATE TABLE `mylock` (`id` int(11) NOT NULL AUTO_INCREMENT,`NAME` varchar(20) DEFAULT NULL,PRIMARY KEY (`id`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8;INSERT INTO `mylock` (`id`, `NAME`) VALUES ('1', 'a'); INSERT INTO `mylock` (`id`, `NAME`) VALUES ('2', 'b'); INSERT INTO `mylock` (`id`, `NAME`) VALUES ('3', 'c'); INSERT INTO `mylock` (`id`, `NAME`) VALUES ('4', 'd');2.1寫鎖阻塞讀
當一個線程獲得對一個表的寫鎖之后,只有持有鎖的線程可以對表進行更新操作。其他線程的讀寫操作都會等待,直到鎖釋放為止。
| 獲取表的write鎖定<br />lock table mylock write; | ? |
| 當前session對表的查詢,插入,更新操作都可以執行<br />select * from mylock;<br />insert into mylock values(5,'e'); | 當前session對表的查詢會被阻塞<br />select * from mylock; |
| 釋放鎖:<br />unlock tables; | 當前session能夠立刻執行,并返回對應結果 |
2.2讀阻塞寫
一個session使用lock table給表加讀鎖,這個session可以鎖定表中的記錄,但更新和訪問其他表都會提示錯誤,同時,另一個session可以查詢表中的記錄,但更新就會出現鎖等待。
| 獲得表的read鎖定<br />lock table mylock read; | ? |
| 當前session可以查詢該表記錄:<br />select * from mylock; | 當前session可以查詢該表記錄:<br />select * from mylock; |
| 當前session不能查詢沒有鎖定的表<br />select * from person<br />Table 'person' was not locked with LOCK TABLES | 當前session可以查詢或者更新未鎖定的表<br />select * from mylock<br />insert into person values(1,'zhangsan'); |
| 當前session插入或者更新表會提示錯誤<br />insert into mylock values(6,'f')<br />Table 'mylock' was locked with a READ lock and can't be updated<br />update mylock set name='aa' where id = 1;<br />Table 'mylock' was locked with a READ lock and can't be updated | 當前session插入數據會等待獲得鎖<br />insert into mylock values(6,'f'); |
| 釋放鎖<br />unlock tables; | 獲得鎖,更新成功 |
注意:
MyISAM在執行查詢語句之前,會自動給涉及的所有表加讀鎖,在執行更新操作前,會自動給涉及的表加寫鎖,這個過程并不需要用戶干預,因此用戶一般不需要使用命令來顯式加鎖,上例中的加鎖時為了演示效果。
MyISAM的并發插入問題
MyISAM表的讀和寫是串行的,這是就總體而言的,在一定條件下,MyISAM也支持查詢和插入操作的并發執行。
可以通過檢查table_locks_waited和table_locks_immediate狀態變量來分析系統上的表鎖定爭奪:
mysql> show status like 'table%'; +-----------------------+-------+ | Variable_name | Value | +-----------------------+-------+ | Table_locks_immediate | 352 | | Table_locks_waited | 2 | +-----------------------+-------+
--如果Table_locks_waited的值比較高,則說明存在著較嚴重的表級鎖爭用情況。
3、InnoDB鎖
3.1、事務及其ACID屬性
事務是由一組SQL語句組成的邏輯處理單元,事務具有4屬性,通常稱為事務的ACID屬性。
原子性(Actomicity):事務是一個原子操作單元,其對數據的修改,要么全都執行,要么全都不執行。一致性(Consistent):在事務開始和完成時,數據都必須保持一致狀態。隔離性(Isolation):數據庫系統提供一定的隔離機制,保證事務在不受外部并發操作影響的“獨立”環境執行。持久性(Durable):事務完成之后,它對于數據的修改是永久性的,即使出現系統故障也能夠保持。
3.2、并發事務帶來的問題
相對于串行處理來說,并發事務處理能大大增加數據庫資源的利用率,提高數據庫系統的事務吞吐量,從而可以支持更多用戶的并發操作,但與此同時,會帶來一下問題:
臟讀: 一個事務正在對一條記錄做修改,在這個事務并提交前,這條記錄的數據就處于不一致狀態;這時,另一個事務也來讀取同一條記錄,如果不加控制,第二個事務讀取了這些“臟”的數據,并據此做進一步的處理,就會產生未提交的數據依賴關系。這種現象被形象地叫做“臟讀”
不可重復讀:一個事務在讀取某些數據已經發生了改變、或某些記錄已經被刪除了!這種現象叫做“不可重復讀”。
幻讀: 一個事務按相同的查詢條件重新讀取以前檢索過的數據,卻發現其他事務插入了滿足其查詢條件的新數據,這種現象就稱為“幻讀”
上述出現的問題都是數據庫讀一致性的問題,可以通過事務的隔離機制來進行保證。
數據庫的事務隔離越嚴格,并發副作用就越小,但付出的代價也就越大,因為事務隔離本質上就是使事務在一定程度上串行化,需要根據具體的業務需求來決定使用哪種隔離級別
| read uncommitted | √ | √ | √ |
| read committed | ? | √ | √ |
| repeatable read | ? | ? | √ |
| serializable | ? | ? | ? |
?
可以通過檢查InnoDB_row_lock狀態變量來分析系統上的行鎖的爭奪情況:
mysql> show status like 'innodb_row_lock%'; +-------------------------------+-------+ | Variable_name | Value | +-------------------------------+-------+ | Innodb_row_lock_current_waits | 0 | | Innodb_row_lock_time | 18702 | | Innodb_row_lock_time_avg | 18702 | | Innodb_row_lock_time_max | 18702 | | Innodb_row_lock_waits | 1 | +-------------------------------+-------+ --如果發現鎖爭用比較嚴重,如InnoDB_row_lock_waits和InnoDB_row_lock_time_avg的值比較高3.3、InnoDB的行鎖模式及加鎖方法
? 共享鎖(s):又稱讀鎖。允許一個事務去讀一行,阻止其他事務獲得相同數據集的排他鎖。若事務T對數據對象A加上S鎖,則事務T可以讀A但不能修改A,其他事務只能再對A加S鎖,而不能加X鎖,直到T釋放A上的S鎖。這保證了其他事務可以讀A,但在T釋放A上的S鎖之前不能對A做任何修改。? 排他鎖(x):又稱寫鎖。允許獲取排他鎖的事務更新數據,阻止其他事務取得相同的數據集共享讀鎖和排他寫鎖。若事務T對數據對象A加上X鎖,事務T可以讀A也可以修改A,其他事務不能再對A加任何鎖,直到T釋放A上的鎖。
? mysql InnoDB引擎默認的修改數據語句:update,delete,insert都會自動給涉及到的數據加上排他鎖,select語句默認不會加任何鎖類型,如果加排他鎖可以使用select …for update語句,加共享鎖可以使用select … lock in share mode語句。所以加過排他鎖的數據行在其他事務種是不能修改數據的,也不能通過for update和lock in share mode鎖的方式查詢數據,但可以直接通過select …from…查詢數據,因為普通查詢沒有任何鎖機制。
InnoDB行鎖實現方式
? InnoDB行鎖是通過給索引上的索引項加鎖來實現的,這一點MySQL與Oracle不同,后者是通過在數據塊中對相應數據行加鎖來實現的。InnoDB這種行鎖實現特點意味著:只有通過索引條件檢索數據,InnoDB才使用行級鎖,否則,InnoDB將使用表鎖!
3.3.1、在不通過索引條件查詢的時候,innodb使用的是表鎖而不是行鎖
create table tab_no_index(id int,name varchar(10)) engine=innodb; insert into tab_no_index values(1,'1'),(2,'2'),(3,'3'),(4,'4');?
| set autocommit=0<br />select * from tab_no_index where id = 1; | set autocommit=0<br />select * from tab_no_index where id =2 |
| select * from tab_no_index where id = 1 for update | ? |
| ? | select * from tab_no_index where id = 2 for update; |
session1只給一行加了排他鎖,但是session2在請求其他行的排他鎖的時候,會出現鎖等待。原因是在沒有索引的情況下,innodb只能使用表鎖。
3.3.2、創建帶索引的表進行條件查詢,innodb使用的是行鎖
create table tab_with_index(id int,name varchar(10)) engine=innodb; alter table tab_with_index add index id(id); insert into tab_with_index values(1,'1'),(2,'2'),(3,'3'),(4,'4');| set autocommit=0<br />select * from tab_with_indexwhere id = 1; | set autocommit=0<br />select * from tab_with_indexwhere id =2 |
| select * from tab_with_indexwhere id = 1 for update | ? |
| ? | select * from tab_with_indexwhere id = 2 for update; |
3、由于mysql的行鎖是針對索引加的鎖,不是針對記錄加的鎖,所以雖然是訪問不同行的記錄,但是依然無法訪問到具體的數據
insert into tab_with_index values(1,'4');| set autocommit=0 | set autocommit=0 |
| select * from tab_with_index where id = 1 and name='1' for update | ? |
| ? | select * from tab_with_index where id = 1 and name='4' for update<br />雖然session2訪問的是和session1不同的記錄,但是因為使用了相同的索引,所以需要等待鎖 |
總結
對于MyISAM的表鎖,主要討論了以下幾點: (1)共享讀鎖(S)之間是兼容的,但共享讀鎖(S)與排他寫鎖(X)之間,以及排他寫鎖(X)之間是互斥的,也就是說讀和寫是串行的。 (2)在一定條件下,MyISAM允許查詢和插入并發執行,我們可以利用這一點來解決應用中對同一表查詢和插入的鎖爭用問題。 (3)MyISAM默認的鎖調度機制是寫優先,這并不一定適合所有應用,用戶可以通過設置LOW_PRIORITY_UPDATES參數,或在INSERT、UPDATE、DELETE語句中指定LOW_PRIORITY選項來調節讀寫鎖的爭用。 (4)由于表鎖的鎖定粒度大,讀寫之間又是串行的,因此,如果更新操作較多,MyISAM表可能會出現嚴重的鎖等待,可以考慮采用InnoDB表來減少鎖沖突。
對于InnoDB表,本文主要討論了以下幾項內容: (1)InnoDB的行鎖是基于索引實現的,如果不通過索引訪問數據,InnoDB會使用表鎖。 (2)在不同的隔離級別下,InnoDB的鎖機制和一致性讀策略不同。
在了解InnoDB鎖特性后,用戶可以通過設計和SQL調整等措施減少鎖沖突和死鎖,包括:
-
盡量使用較低的隔離級別; 精心設計索引,并盡量使用索引訪問數據,使加鎖更精確,從而減少鎖沖突的機會;
-
選擇合理的事務大小,小事務發生鎖沖突的幾率也更小;
-
給記錄集顯式加鎖時,最好一次性請求足夠級別的鎖。比如要修改數據的話,最好直接申請排他鎖,而不是先申請共享鎖,修改時再請求排他鎖,這樣容易產生死鎖;
-
不同的程序訪問一組表時,應盡量約定以相同的順序訪問各表,對一個表而言,盡可能以固定的順序存取表中的行。這樣可以大大減少死鎖的機會;
-
盡量用相等條件訪問數據,這樣可以避免間隙鎖對并發插入的影響; 不要申請超過實際需要的鎖級別;除非必須,查詢時不要顯示加鎖;
-
對于一些特定的事務,可以使用表鎖來提高處理速度或減少死鎖的可能。
參考《馬士兵教育》相關視頻內容
?
總結
以上是生活随笔為你收集整理的MySQL锁机制(myisam表所与innoDB锁)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 虚拟机(VMware Workstati
- 下一篇: foreach无法给外部变量赋值(Loc