相關博文推薦:mysql
平時的業務中,頂多也就是寫寫簡單的sql,連事務都用的少,對鎖這一塊的瞭解就更加欠缺了,以前一個大神分享了下mysql的事務隔離級別,感受挺有意思的,正好發現一個很棒的博文,而後也收集了一些相關知識,正好來學習下,mysql中鎖與事務的神祕面紗,主要內容包括ios
在學習多線程時,咱們也常常會遇到鎖這個東西,那個時候談的比較多的是樂觀鎖和悲觀鎖,那這兩種鎖和DB中常說的共享鎖和獨佔鎖有什麼區別呢?先給出咱們已知的樂觀鎖和悲觀鎖定義git
突出在共享這個關鍵詞上,顧名思義,表示這個鎖能夠多人共享,通常又能夠稱爲讀鎖(S鎖)github
在DB中,讀鎖表示全部的讀取數據的小夥伴都不會被鎖阻塞,能夠放心大膽的獲取數據,專業一點的說法就是同一時刻,容許多個鏈接併發的讀取同一資源sql
排它,表示當某我的持有這個鎖以後,其餘的人再來競爭鎖就會失敗,只能等待鎖釋放, 又稱爲寫鎖(X鎖)數據庫
在DB中,寫鎖表示同一時刻,只能有一個小夥伴操做,其餘的不論是讀仍是寫,都得排隊,專業說法是寫鎖會阻塞其餘的讀鎖或寫鎖請求,確保同一時刻只能有一個鏈接能夠寫入資源,並防止其餘鏈接讀取或者寫資源session
以下面的case(說明,columnA是非惟一索引,RR隔離級別)多線程
where columnA between 10 and 30
, next key lock 確保不會在10, 30 以內插入新的數據行where columnA = 10
, gap lock 確保不會再次插入一個columnA=10的行對於DB的操做,一般會出現兩種狀況,一個是鎖表,一個鎖行併發
那麼一個問題就來了,什麼sql會致使行鎖,什麼會致使寫鎖?甚至咱們如何判斷一個sql是否會請求鎖,請求的是讀鎖仍是寫鎖呢?hexo
上面一節拋出了問題,那麼如今就是來看下如何使用和分析鎖了,首先咱們是咱們最多見的幾個sql
其中很容易得出的結論是 update, delete, insert
三個涉及到寫鎖;並且這種操做絕大部分的場景是操做具體的某些行(想一想爲何?),因此更常見的是行鎖
select讀操做則有點特殊
MVCC(multiple-version-concurrency-control)是個行級鎖的變種,它在普通讀狀況下避免了加鎖操做,所以開銷更低。即下面這個沒有讀鎖也沒有寫鎖
快照讀,不加鎖
select * from table ...
複製代碼
當前讀,select 語句能夠指定讀鎖和寫鎖,以下
-- 讀鎖
select * from table lock in share mode;
-- 寫鎖
select * from table for update;
複製代碼
說明,insert, update, delete 也是當前讀,理由以下:
1.update和delete操做流程分解:
2.insert操做流程分解:
--- SQL1:
select * from t1 where id = 10;
--- SQL2:
delete from t1 where id = 10;
複製代碼
在分析上面的sql以前,須要明確幾個前提:
分別說明:
case1: 主鍵+RC級別
case2: 惟一索引+rc級別
case3: id非惟一索引+RC
case4: 無索引+RC
case5: 主鍵+RR
加鎖同case1
case6: 惟一索引+RR
加鎖同case2
case7: 非惟一索引+RR
RR級別不容許出現幻讀,簡單來講,在加鎖的過程當中,不容許在新增or修改知足條件的記錄
即下圖中,除了圖三中相似的x鎖以外,還會新增一個gap鎖,這個gap鎖主要確保那幾個位置上不能插入新的記錄
case8: 無索引+RR
case9: Serializable級別
事務可謂是db中很是重要的一個知識點了,接下來咱們的目標就是弄懂什麼是事務,怎麼使用事務,以及事務與鎖之間的關聯是怎樣的
說明:本文的分析主要是以mysql的innordb存儲引擎爲標準
事務就是一組原子性的sql,或者說一個獨立的工做單元。
事務就是說,要麼mysql引擎會所有執行這一組sql語句,要麼所有都不執行(好比其中一條語句失敗的話)。
一個事務必須保證其中的操做要麼所有執行,要麼所有回滾,不可能存在只執行了一部分這種狀況出現。
數據必須保證從一種一致性的狀態轉換爲另外一種一致性狀態。
在一個事務未執行完畢時,一般會保證其餘Session 沒法看到這個事務的執行結果
事務一旦commit,則數據就會保存下來,即便提交完以後系統崩潰,數據也不會丟失
前面在分析鎖的sql時,就提到了隔離級別,一般有四種: RU, RC, RR, Serializable
在說明這個以前,先了解幾個概念
select * from table ...
的執行是否加了讀鎖 (這個能夠參考上面的sql加鎖分析)事務中的修改,即便沒有提交,對其餘會話也是可見的,即表示可能出現髒讀,通常數據庫都不採用這種方案
這個隔離級別保證了一個事務若是沒有徹底成功(commit執行完),事務中的操做對其餘會話是不可見的,避免了髒讀的可能
可是可能出現不可重複度的狀況,舉例說明:
select * from where id=1
,第一次返回一個結果update table set updated=xxx where id=1
並提交select * from where id=1
,此次返回的結果中update字段就和前面的不同了實際的生產環境中,這個級別用的比較多,特地查了下公司的db隔離級別就是這個
一個RC級別的演示過程:
相關的sql代碼以下:
-- 設置會話隔離級別
set session transaction ioslation read commited;
-- 查看當前會話隔離級別
select @@tx_isolation;
-- 會話1的操做
start transaction;
select * from newuser where userId=1;
-- 會話2開始操做
start transaction;
select * from newuser where userId=1;
update newuser set updated=1521786092 where userId=1;
select * from newuser where userId=1;
commit;
-- 再次進入會話1,一樣執行上次的sql,對比兩次輸出結果
select * from newuser where userId=1;
-- 注意觀察,會話1,先後兩次這個sql的輸出結果,特別是updated字段
-- 正常狀況會如上面的demo圖,會發生改變
-- 關閉會話
commit;
-- 再次查詢
select * from newuser where userId=1;
複製代碼
一個事務中屢次執行統一讀SQL,返回結果同樣。 這個隔離級別解決了髒讀的問題,幻讀問題
實例演示解決髒讀的過程(將上面的過程一樣來一次)
最強的隔離級別,經過給事務中每次讀取的行加鎖,寫加寫鎖,保證不產生幻讀問題,可是會致使大量超時以及鎖爭用問題。
select @@tx_isolation
select @@global.tx_isolation
set session transaction isolation level read committed;
set global transaction isolation level read committed;
start transactioin;
commit;
前面演示事務隔離級別的時候,給出的實例就演示了事務的使用姿式,通常做爲三步驟:
start transaction;
commit;
咱們如今演示如下一個事務中,讀鎖、寫鎖對另外一個事務的影響
咱們採用mysql默認的RR級別進行測試,userId爲主鍵
-- 會話1
start transaction;
select * from newuser where userId=1 lock in share mode;
-- 轉入會話2
start transaction;
select * from newuser where userId=1; -- 會輸出
select * from newuser where userId=1 lock in share mode; -- 會輸出
update newuser set updated=1521787137 where userId=1; -- 會掛起
-- 轉入會話1
-- 提交, 此時觀察會話2的寫是否完成
commit;
-- 轉入會話2
commit;
複製代碼
實際執行演示:
-- 會話1
start transaction;
select * from newuser where userId=1 for update;
-- 轉入會話2
start transaction;
select * from newuser where userId=1; -- 會輸出
select * from newuser where userId=1 lock in share mode; -- 會掛住
-- update newuser set updated=1521787137 where userId=1; -- 會掛住
-- 轉入會話1
-- 提交, 此時觀察會話2的寫是否完成
commit;
-- 轉入會話2
commit;
複製代碼
實際執行演示:
鎖和事務可謂是db中很是重要的知識點了,在咱們實際的編碼過程當中(通常針對mysql, innordb存儲引擎,rr隔離級別),作出下面的一些總結
select * from table where xxx;
(讀快照,通常不加鎖)select * from table where xxx lock in share mode;
(讀鎖,會阻塞其餘的寫鎖請求,但其餘的讀鎖請求沒有影響)select * from table where xxx for update;
(寫鎖,會阻塞其餘的讀寫請求)update tableName set xxx
(寫鎖)insert
(寫鎖)delete
(寫鎖)簡單來說,事務就是一組sql,要麼所有執行成功,要麼所有失敗
四個特性: A(原子性)C(一致性)I(隔離性)D (持久性)
四種隔離級別:(mysql 默認採用的是RR級別)
隔離級別 | 髒讀 | 不可重複讀 | 幻讀 | 加鎖讀 |
---|---|---|---|---|
read uncommited | 可能 | 可能 | 可能 | 無 |
read commited | 不可能 | 可能 | 可能 | 無 |
repeatable read | 不可能 | 不可能 | 不可能 | 無 |
serializable | 不可能 | 不可能 | 不可能 | 有 |
使用姿式:
start transaction;
-- xxx 具體的sql
commit;
複製代碼
基於hexo + github pages搭建的我的博客,記錄全部學習和工做中的博文,歡迎你們前去逛逛
盡信書則不如,已上內容,純屬一家之言,因本人能力通常,見識有限,如發現bug或者有更好的建議,隨時歡迎批評指正