定義java
鎖是計算機協調多個進程或線程併發訪問某一資源的機制。
在數據庫中,除傳統的計算資源(如CPU、RAM、I/O等)的爭用之外,數據也是一種供許多用戶共享的資源。如何保證數據併發訪問的一致性、有效性是全部數據庫必須解決的一個問題,鎖衝突也是影響數據庫併發訪問性能的一個重要因素。從這個角度來講,鎖對數據庫而言顯得尤爲重要,也更加複雜。
生活購物mysql
打個比方,咱們到淘寶上買一件商品,商品只有一件庫存,這個時候若是還有另外一我的買,
那麼如何解決是你買到仍是另外一我的買到的問題?程序員
這裏確定要用到事務,咱們先從庫存表中取出物品數量,而後插入訂單,付款後插入付款表信息,
而後更新商品數量。在這個過程當中,使用鎖能夠對有限的資源進行保護,解決隔離和併發的矛盾。sql
鎖的分類數據庫
(1)從對數據操做的類型(讀\寫)分數據結構
讀鎖(共享鎖):針對同一份數據,多個讀操做能夠同時進行而不會互相影響。併發
寫鎖(排它鎖):當前寫操做沒有完成前,它會阻斷其餘寫鎖和讀鎖。高併發
(2)從對數據操做的粒度分性能
表鎖優化
行鎖
【爲了儘量提升數據庫的併發度,每次鎖定的數據範圍越小越好,理論上每次只鎖定當前操做的數據的方案會獲得最大的併發度,可是管理鎖是很耗資源的事情(涉及獲取,檢查,釋放鎖等動做),所以數據庫系統須要在高併發響應和系統性能兩方面進行平衡,這樣就產生了「鎖粒度(Lock granularity)」的概念。
一種提升共享資源併發發性的方式是讓鎖定對象更有選擇性。儘可能只鎖定須要修改的部分數據,而不是全部的資源。更理想的方式是,只對會修改的數據片進行精確的鎖定。任什麼時候候,在給定的資源上,鎖定的數據量越少,則系統的併發程度越高,只要相互之間不發生衝突便可。】
(1)特色
偏向MyISAM存儲引擎,開銷小,加鎖快;無死鎖;鎖定粒度大,發生鎖衝突的機率最高,併發度最低。
(2)案例分析
1)建表SQL
【表級鎖分析--建表SQL】
create table mylock(
id int not null primary key auto_increment,
name varchar(20)
)engine myisam;
insert into mylock(name) values('a');
insert into mylock(name) values('b');
insert into mylock(name) values('c');
insert into mylock(name) values('d');
insert into mylock(name) values('e');
select * from mylock;
【手動增長表鎖】
lock table 表名字1 read(write),表名字2 read(write),其它;
【查看錶上加過的鎖】
show open tables;
【釋放表鎖】
unlock tables;
2)加讀鎖
3)加寫鎖
(3)案例結論
MyISAM在執行查詢語句(SELECT)前,會自動給涉及的全部表加讀鎖,在執行增刪改操做前,會自動給涉及的表加寫鎖。
MySQL的表級鎖有兩種模式:
表共享讀鎖(Table Read Lock)
表獨佔寫鎖(Table Write Lock)
結論:
結合上表,因此對MyISAM表進行操做,會有如下狀況:
一、對MyISAM表的讀操做(加讀鎖),不會阻塞其餘進程對同一表的讀請求,但會阻塞對同一表的寫請求。只有當讀鎖釋放後,纔會執行其它進程的寫操做。
二、對MyISAM表的寫操做(加寫鎖),會阻塞其餘進程對同一表的讀和寫操做,只有當寫鎖釋放後,纔會執行其它進程的讀寫操做。
簡而言之,就是讀鎖會阻塞寫,可是不會堵塞讀。而寫鎖則會把讀和寫都堵塞
(4)表鎖分析
一、特色
偏向InnoDB存儲引擎,開銷大,加鎖慢;會出現死鎖;鎖定粒度最小,發生鎖衝突的機率最低,併發度也最高。
InnoDB與MyISAM的最大不一樣有兩點:一是支持事務(TRANSACTION);二是採用了行級鎖
二、因爲行鎖支持事務,複習老知識
(1)事務(Transaction)及其ACID屬性
事務是由一組SQL語句組成的邏輯處理單元,事務具備如下4個屬性,一般簡稱爲事務的ACID屬性。
l 原子性(Atomicity):事務是一個原子操做單元,其對數據的修改,要麼全都執行,要麼全都不執行。
l 一致性(Consistent):在事務開始和完成時,數據都必須保持一致狀態。這意味着全部相關的數據規則都必須應用於事務的修改,以保持數據的完整性;事務結束時,全部的內部數據結構(如B樹索引或雙向鏈表)也都必須是正確的。
l 隔離性(Isolation):數據庫系統提供必定的隔離機制,保證事務在不受外部併發操做影響的「獨立」環境執行。這意味着事務處理過程當中的中間狀態對外部是不可見的,反之亦然。
l 持久性(Durable):事務完成以後,它對於數據的修改是永久性的,即便出現系統故障也可以保持。
(2)併發事務處理帶來的問題
1)更新丟失(Lost Update)
當兩個或多個事務選擇同一行,而後基於最初選定的值更新該行時,因爲每一個事務都不知道其餘事務的存在,就會發生丟失更新問題--最後的更新覆蓋了由其餘事務所作的更新。
例如,兩個程序員修改同一java文件。每程序員獨立地更改其副本,而後保存更改後的副本,這樣就覆蓋了原始文檔。最後保存其更改副本的編輯人員覆蓋前一個程序員所作的更改。
若是在一個程序員完成並提交事務以前,另外一個程序員不能訪問同一文件,則可避免此問題。
2)髒讀(Dirty Reads)
一個事務正在對一條記錄作修改,在這個事務完成並提交前,這條記錄的數據就處於不一致狀態;這時,另外一個事務也來讀取同一條記錄,若是不加控制,第二個事務讀取了這些「髒」數據,並據此作進一步的處理,就會產生未提交的數據依賴關係。這種現象被形象地叫作」髒讀」。
一句話:事務A讀取到了事務B已修改但還沒有提交的的數據,還在這個數據基礎上作了操做。此時,若是B事務回滾,A讀取的數據無效,不符合一致性要求。
3)不可重複讀(Non-Repeatable Reads)
在一個事務內,屢次讀同一個數據。在這個事務尚未結束時,另外一個事務也訪問該同一數據。那麼,在第一個事務的兩次讀數據之間。因爲第二個事務的修改,那麼第一個事務讀到的數據可能不同,這樣就發生了在一個事務內兩次讀到的數據是不同的,所以稱爲不可重複讀,即原始讀取不可重複。
一句話:一個事務範圍內兩個相同的查詢卻返回了不一樣數據。
4)幻讀(Phantom Reads)
一個事務按相同的查詢條件從新讀取之前檢索過的數據,卻發現其餘事務插入了知足其查詢條件的新數據,這種現象就稱爲「幻讀」。
一句話:事務A 讀取到了事務B提交的新增數據,不符合隔離性。
多說一句:幻讀和髒讀有點相似:
髒讀是事務B裏面修改了數據,
幻讀是事務B裏面新增了數據。
(3)事務隔離級別
髒讀」、「不可重複讀」和「幻讀」,其實都是數據庫讀一致性問題,必須由數據庫提供必定的事務隔離機制來解決。
數據庫的事務隔離越嚴格,併發反作用越小,但付出的代價也就越大,由於事務隔離實質上就是使事務在必定程度上 「串行化」進行,這顯然與「併發」是矛盾的。同時,不一樣的應用對讀一致性和事務隔離程度的要求也是不一樣的,好比許多應用對「不可重複讀」和「幻讀」並不敏感,可能更關心數據併發訪問的能力。
常看當前數據庫的事務隔離級別:show variables like 'tx_isolation';
(4)案例分析
(1)建表SQL
create table test_innodb_lock (a int(11),b varchar(16))engine=innodb;
insert into test_innodb_lock values(1,'b2');
insert into test_innodb_lock values(3,'3');
insert into test_innodb_lock values(4,'4000');
insert into test_innodb_lock values(5,'5000');
insert into test_innodb_lock values(6,'6000');
insert into test_innodb_lock values(7,'7000');
insert into test_innodb_lock values(8,'8000');
insert into test_innodb_lock values(9,'9000');
insert into test_innodb_lock values(1,'b1');
create index test_innodb_a_ind on test_innodb_lock(a);
create index test_innodb_lock_b_ind on test_innodb_lock(b);
select * from test_innodb_lock;
(2)行鎖定基本演示
行鎖定基本演示
(3)無索引行鎖升級爲表鎖
(4)Select也能夠加鎖
讀鎖 select ..lock in share mode
共享鎖(Share Lock)
共享鎖又稱讀鎖,是讀取操做建立的鎖。其餘用戶能夠併發讀取數據,但任何事務都不能對數據進行修改(獲取數據上的排他鎖),直到已釋放全部共享鎖。
若是事務T對數據A加上共享鎖後,則其餘事務只能對A再加共享鎖,不能加排他鎖。獲准共享鎖的事務只能讀數據,不能修改數據。
用法
SELECT ... LOCK IN SHARE MODE;
在查詢語句後面增長 LOCK IN SHARE MODE ,Mysql會對查詢結果中的每行都加共享鎖,當沒有其餘線程對查詢結果集中的任何一行使用排他鎖時,能夠成功申請共享鎖,不然會被阻塞。其餘線程也能夠讀取使用了共享鎖的表(行?),並且這些線程讀取的是同一個版本的數據。
寫鎖 select... for update
排他鎖(eXclusive Lock)
共享鎖又稱寫鎖,若是事務T對數據A加上排他鎖後,則其餘事務不能再對A加任任何類型的封鎖。獲准排他鎖的事務既能讀數據,又能修改數據。
用法
SELECT ... FOR UPDATE;
在查詢語句後面增長 FOR UPDATE ,Mysql會對查詢結果中的每行都加排他鎖,當沒有其餘線程對查詢結果集中的任何一行使用排他鎖時,能夠成功申請排他鎖,不然會被阻塞。
(5)案列結論
Innodb存儲引擎因爲實現了行級鎖定,雖然在鎖定機制的實現方面所帶來的性能損耗可能比表級鎖定會要更高一些,可是在總體併發處理能力方面要遠遠優於MyISAM的表級鎖定的。當系統併發量較高的時候,Innodb的總體性能和MyISAM相比就會有比較明顯的優點了。
可是,Innodb的行級鎖定一樣也有其脆弱的一面,當咱們使用不當的時候,可能會讓Innodb的總體性能表現不只不能比MyISAM高,甚至可能會更差。
(6)行鎖分析
【如何分析行鎖定】
經過檢查InnoDB_row_lock狀態變量來分析系統上的行鎖的爭奪狀況
mysql>show status like 'innodb_row_lock%';
對各個狀態量的說明以下:
Innodb_row_lock_current_waits:當前正在等待鎖定的數量;
Innodb_row_lock_time:從系統啓動到如今鎖定總時間長度;
Innodb_row_lock_time_avg:每次等待所花平均時間;
Innodb_row_lock_time_max:從系統啓動到如今等待最常的一次所花的時間;
Innodb_row_lock_waits:系統啓動後到如今總共等待的次數;
對於這5個狀態變量,比較重要的主要是
Innodb_row_lock_time_avg(等待平均時長),
Innodb_row_lock_waits(等待總次數)
Innodb_row_lock_time(等待總時長)這三項。
尤爲是當等待次數很高,並且每次等待時長也不小的時候,咱們就須要分析系統中爲何會有如此多的等待,而後根據分析結果着手指定優化計劃。
最後能夠經過
SELECT * FROM information_schema.INNODB_TRX\G;
來查詢正在被鎖阻塞的sql語句。
(7)優化建議
1)儘量讓全部數據檢索都經過索引來完成,避免無索引行鎖升級爲表鎖。
2)儘量較少檢索條件,避免間隙鎖
3)儘可能控制事務大小,減小鎖定資源量和時間長度
4)鎖住某行後,儘可能不要去調別的行或表,趕忙處理被鎖住的行而後釋放掉鎖。
5)涉及相同表的事務,對於調用表的順序儘可能保持一致。
6)在業務環境容許的狀況下,儘量低級別事務隔離
開銷和加鎖時間界於表鎖和行鎖之間;會出現死鎖;鎖定粒度界於表鎖和行鎖之間,併發度通常。
瞭解一下便可