MySQL/InnoDB的加鎖,一直是一個面試中常問的話題。例如,數據庫若是有高併發請求,如何保證數據完整性?產生死鎖問題如何排查並解決?我在工做過程當中,也會常常用到,樂觀鎖,排它鎖,等。因而今天就對這幾個概念進行學習,屢屢思路,記錄一下。java
注:MySQL是一個支持插件式存儲引擎的數據庫系統。本文下面的全部介紹,都是基於InnoDB存儲引擎,其餘引擎的表現,會有較大的區別。mysql
存儲引擎查看面試
MySQL給開發者提供了查詢存儲引擎的功能,我這裏使用的是MySQL5.6.4,可使用:sql
SHOW ENGINES
數據庫
用數據版本(Version)記錄機制實現,這是樂觀鎖最經常使用的一種實現方式。何謂數據版本?即爲數據增長一個版本標識,通常是經過爲數據庫表增長一個數字類型的 「version」 字段來實現。當讀取數據時,將version字段的值一同讀出,數據每更新一次,對此version值加1。當咱們提交更新的時候,判斷數據庫表對應記錄的當前版本信息與第一次取出來的version值進行比對,若是數據庫表當前版本號與第一次取出來的version值相等,則予以更新,不然認爲是過時數據。併發
舉例高併發
一、數據庫表設計性能
三個字段,分別是id,value、version
學習
select id,value,version from TABLE where id=#{id}
二、每次更新表中的value字段時,爲了防止發生衝突,須要這樣操做.net
update TABLE set value=2,version=version+1 where id=#{id} and version=#{version};
與樂觀鎖相對應的就是悲觀鎖了。悲觀鎖就是在操做數據時,認爲此操做會出現數據衝突,因此在進行每次操做時都要經過獲取鎖才能進行對相同數據的操做,這點跟java中的synchronized很類似,因此悲觀鎖須要耗費較多的時間。另外與樂觀鎖相對應的,悲觀鎖是由數據庫本身實現了的,要用的時候,咱們直接調用數據庫的相關語句就能夠了。
說到這裏,由悲觀鎖涉及到的另外兩個鎖概念就出來了,它們就是共享鎖與排它鎖。共享鎖和排它鎖是悲觀鎖的不一樣的實現,它倆都屬於悲觀鎖的範疇。
使用,排它鎖 舉例
要使用悲觀鎖,咱們必須關閉mysql數據庫的自動提交屬性,由於MySQL默認使用autocommit模式,也就是說,當你執行一個更新操做後,MySQL會馬上將結果進行提交。
咱們可使用命令設置MySQL爲非autocommit模式:
set autocommit=0; # 設置完autocommit後,咱們就能夠執行咱們的正常業務了。具體以下: # 1. 開始事務 begin;/begin work;/start transaction; (三者選一就能夠) # 2. 查詢表信息 select status from TABLE where id=1 for update; # 3. 插入一條數據 insert into TABLE (id,value) values (2,2); # 4. 修改數據爲 update TABLE set value=2 where id=1; # 5. 提交事務 commit;/commit work;
共享鎖又稱讀鎖 read lock,是讀取操做建立的鎖。其餘用戶能夠併發讀取數據,但任何事務都不能對數據進行修改(獲取數據上的排他鎖),直到已釋放全部共享鎖。
若是事務T對數據A加上共享鎖後,則其餘事務只能對A再加共享鎖,不能加排他鎖。得到共享鎖的事務只能讀數據,不能修改數據
打開第一個查詢窗口
begin;/begin work;/start transaction; (三者選一就能夠) SELECT * from TABLE where id = 1 lock in share mode;
而後在另外一個查詢窗口中,對id爲1的數據進行更新
update TABLE set name="www.souyunku.com" where id =1;
此時,操做界面進入了卡頓狀態,過了超時間,提示錯誤信息
若是在超時前,執行 commit
,此更新語句就會成功。
[SQL]update test_one set name="www.souyunku.com" where id =1; [Err] 1205 - Lock wait timeout exceeded; try restarting transaction
加上共享鎖後,也提示錯誤信息
update test_one set name="www.souyunku.com" where id =1 lock in share mode;
[SQL]update test_one set name="www.souyunku.com" where id =1 lock in share mode; [Err] 1064 - You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'lock in share mode' at line 1
在查詢語句後面增長** LOCK IN SHARE MODE
**,Mysql會對查詢結果中的每行都加共享鎖,當沒有其餘線程對查詢結果集中的任何一行使用排他鎖時,能夠成功申請共享鎖,不然會被阻塞。其餘線程也能夠讀取使用了共享鎖的表,並且這些線程讀取的是同一個版本的數據。
加上共享鎖後,對於update,insert,delete
語句會自動加排它鎖。
排他鎖 exclusive lock(也叫writer lock)又稱寫鎖。
排它鎖是悲觀鎖的一種實現,在上面悲觀鎖也介紹過。
若事務 1 對數據對象A加上X鎖,事務 1 能夠讀A也能夠修改A,其餘事務不能再對A加任何鎖,直到事物 1 釋放A上的鎖。這保證了其餘事務在事物 1 釋放A上的鎖以前不能再讀取和修改A。排它鎖會阻塞全部的排它鎖和共享鎖
讀取爲何要加讀鎖呢:防止數據在被讀取的時候被別的線程加上寫鎖,
使用方式:在須要執行的語句後面加上for update
就能夠了
行鎖又分共享鎖和排他鎖,由字面意思理解,就是給某一行加上鎖,也就是一條記錄加上鎖。
注意:行級鎖都是基於索引的,若是一條SQL語句用不到索引是不會使用行級鎖的,會使用表級鎖。
共享鎖:
名詞解釋:共享鎖又叫作讀鎖,全部的事務只能對其進行讀操做不能寫操做,加上共享鎖後在事務結束以前其餘事務只能再加共享鎖,除此以外其餘任何類型的鎖都不能再加了。
SELECT * from TABLE where id = "1" lock in share mode; 結果集的數據都會加共享鎖
排他鎖:
名詞解釋:若某個事物對某一行加上了排他鎖,只能這個事務對其進行讀寫,在此事務結束以前,其餘事務不能對其進行加任何鎖,其餘進程能夠讀取,不能進行寫操做,需等待其釋放。
select status from TABLE where id=1 for update;
能夠參考以前演示的共享鎖,排它鎖語句
因爲對於表中,id字段爲主鍵,就也至關於索引。執行加鎖時,會將id這個索引爲1的記錄加上鎖,那麼這個鎖就是行鎖。
如何加表鎖
innodb 的行鎖是在有索引的狀況下,沒有索引的表是鎖定全表的.
Innodb中的行鎖與表鎖
前面提到過,在Innodb引擎中既支持行鎖也支持表鎖,那麼何時會鎖住整張表,何時或只鎖住一行呢? 只有經過索引條件檢索數據,InnoDB才使用行級鎖,不然,InnoDB將使用表鎖!
在實際應用中,要特別注意InnoDB行鎖的這一特性,否則的話,可能致使大量的鎖衝突,從而影響併發性能。
行級鎖都是基於索引的,若是一條SQL語句用不到索引是不會使用行級鎖的,會使用表級鎖。行級鎖的缺點是:因爲須要請求大量的鎖資源,因此速度慢,內存消耗大。
死鎖(Deadlock) 所謂死鎖:是指兩個或兩個以上的進程在執行過程當中,因爭奪資源而形成的一種互相等待的現象,若無外力做用,它們都將沒法推動下去。此時稱系統處於死鎖狀態或系統產生了死鎖,這些永遠在互相等待的進程稱爲死鎖進程。因爲資源佔用是互斥的,當某個進程提出申請資源後,使得有關進程在無外力協助下,永遠分配不到必需的資源而沒法繼續運行,這就產生了一種特殊現象死鎖。
解除正在死鎖的狀態有兩種方法:
第一種:
1.查詢是否鎖表
show OPEN TABLES where In_use > 0;
2.查詢進程(若是您有SUPER權限,您能夠看到全部線程。不然,您只能看到您本身的線程)
show processlist
3.殺死進程id(就是上面命令的id列)
kill id
第二種:
1:查看當前的事務
SELECT * FROM INFORMATION_SCHEMA.INNODB_TRX;
2:查看當前鎖定的事務
SELECT * FROM INFORMATION_SCHEMA.INNODB_LOCKS;
3:查看當前等鎖的事務
SELECT * FROM INFORMATION_SCHEMA.INNODB_LOCK_WAITS;
殺死進程
kill 進程ID
若是系統資源充足,進程的資源請求都可以獲得知足,死鎖出現的可能性就很低,不然就會因爭奪有限的資源而陷入死鎖。其次,進程運行推動順序與速度不一樣,也可能產生死鎖。 產生死鎖的四個必要條件:
(1) 互斥條件:一個資源每次只能被一個進程使用。 (2) 請求與保持條件:一個進程因請求資源而阻塞時,對已得到的資源保持不放。 (3) 不剝奪條件:進程已得到的資源,在末使用完以前,不能強行剝奪。 (4) 循環等待條件:若干進程之間造成一種頭尾相接的循環等待資源關係。
雖然不能徹底避免死鎖,但可使死鎖的數量減至最少。將死鎖減至最少能夠增長事務的吞吐量並減小系統開銷,由於只有不多的事務回滾,而回滾會取消事務執行的全部工做。因爲死鎖時回滾而由應用程序從新提交。
下列方法有助於最大限度地下降死鎖:
(1)按同一順序訪問對象。 (2)避免事務中的用戶交互。 (3)保持事務簡短並在一個批處理中。 (4)使用低隔離級別。 (5)使用綁定鏈接。
參考 :
http://www.javashuo.com/article/p-guxaysjl-dv.html
https://www.jb51.net/article/78088.htm