閱讀本文須要對MySQL事務,隔離級別有些概念上的瞭解,關於這些概念你還不夠了解的話,請你當即中止閱讀!接下來的內容會讓你頭疼欲裂安全
在處理併發讀或者寫時,能夠經過實現一個由兩種類型的鎖組成的鎖系統來解決併發問題。這兩種類型的鎖一般被稱爲共享鎖(shared lock)和排他鎖(exclusive lock),也叫讀鎖(read lock)和寫鎖(write lock)服務器
這裏先不討論鎖的具體實現,描述一下鎖的概念以下:讀鎖是共享的,或者說是相互不阻塞的。多個客戶在同一時刻能夠同時讀取同一個資源,而互不干擾。寫鎖則是排他的,也就是說一個寫鎖會阻塞其餘的寫鎖和讀鎖,這是出於安全策略的考慮,只有這樣,才能確保在給定的時間裏,只有一個用戶能執行寫入,並防止其餘用戶讀取正在寫入的同一資源併發
簡而言之,容許同時讀,可是不容許邊讀邊寫,也不容許同時寫性能
表鎖3d
表鎖是MySQL中最基本的鎖策略,而且是開銷最小的策略。 它會鎖定整張表。一個用戶在對錶進行寫操做(插入、刪除、更新等)前,須要先得到寫鎖,這會阻塞其餘用戶對該表的全部讀寫操做。 只有沒有寫鎖時,其餘讀取的用戶才能得到讀鎖,讀鎖之間是不相互阻塞的cdn
在特定的場景中,表鎖也可能有良好的性能。例如,READ LOCAL表鎖支持某些類型的併發寫操做blog
儘管存儲引擎能夠管理本身的鎖,MySQL自己仍是會使用各類有效的表鎖來實現不一樣的目的。例如,服務器會爲諸如ALTER TABLE之類的語句使用表鎖,而忽略存儲引擎的鎖機制事務
行鎖資源
行級鎖能夠最大程度地支持併發處理(同時也帶來了最大的鎖開銷)。衆所周知,在InnoDB和XtraDB,以及其餘一些存儲引擎中實現了行級鎖。行級鎖只在存儲引擎層實現,而MySQL服務器層有實現it
提出意向鎖概念以前,先試想一個場景。當一個事務1已經對錶A的某一行加了讀鎖,此時事務2想在表A上加寫鎖,那麼這種狀況應該容許嗎?
固然不該該容許,事務2在表A上加寫鎖,那麼是否是意味着事務2能夠對錶A的任意行進行修改?那麼事務1加的讀鎖又有什麼用?因此這就矛盾了
最簡單的解決方案是,事務2再加表鎖以前對錶A進行全表掃描,看一看有沒有行鎖,若是有的話,還要判斷要加的表鎖和行鎖會不會衝突。這種方案真的誇張,加個鎖有必要全表掃描一遍嗎?
事實上InnoDB確定不會這麼幹的,這裏新增了一種鎖,叫作意向鎖
在InnoDB中,意圖鎖定是表鎖定。它的用途就是說告訴其它的表鎖,如今這個表中有行鎖;若是是讀鎖,那麼就會給表加意圖共享鎖;若是是寫鎖,就會給表加意圖佔有鎖;
事先明確
意圖鎖協議以下:
這些結果能夠方便地用一個鎖類型兼容矩陣來總結:
X | IX | S | IS | |
---|---|---|---|---|
X | 衝突 | 衝突 | 衝突 | 衝突 |
IX | 衝突 | 兼容 | 衝突 | 兼容 |
S | 衝突 | 衝突 | 兼容 | 兼容 |
IS | 衝突 | 兼容 | 兼容 | 兼容 |
這個表很明瞭了,假設給表加了IS,那麼就不能給表加寫鎖(X),以前提的問題就迎刃而解了
在事務執行過程當中,隨時均可以執行鎖定,鎖只有在執行COMMIT或者ROLLBACK的時候纔會釋放,而且全部的鎖是在同一時刻被釋放。諸如SELECT... UPDATE... DELETE... 的是隱式鎖定,InnoDB會根據隔離級別在須要的時候自動加鎖
另外,InnoDB也支持經過特定的語句進行顯式鎖定,這些語句不屬於SQL規範
InnoDB存儲引擎實現了行鎖,這確實提升了併發性能,但這只是提升了表層面的併發性能,若是單行數據被頻繁訪問,使用行鎖的話,併發性能仍是不夠理想。MySQL的大多數事務型存儲引擎實現的都不是簡單的行級鎖。基於提高併發性能的考慮,它們通常都同時實現了多版本併發控制(MVCC)
關於MVCC的博文不少,再也不花時間囉嗦,本身搜去吧
若是你不瞭解MVCC,另外你對髒讀,幻讀,不可重複讀的概念不理解的話,請你當即中止閱讀!接下來的內容會讓你產生頭暈眼花,噁心乾嘔等不良反應
InnoDB實現了四種隔離級別:
READ UNCOMMITTED(未提交讀)
這個級別中,SELECT語句以非鎖定方式被執行。事務中的修改,即便沒有提交,對其餘事務也都是可見的。事務能夠讀取未提交的數據,這也被稱爲髒讀(Dirty Read)。
READ COMMITTED(提交讀)
一個事務從開始直到提交以前,所作的任何修改對其餘事務都是不可見的。這個級別有時候也叫作不可重複讀(nonrepeatable read),由於兩次執行一樣的查詢,可能會獲得不同的結果
REPEATABLE READ(可重複讀)
可重複讀隔離級別仍是沒法解決另一個幻讀(Phantom Read)的問題。InnoDB和XtraDB存儲引擎經過多版本併發控制(MVCC,Multiversion Concurrency Control)解決了幻讀的問題。
SERIALIZABLE(可串行化)
SERIALIZABLE是最高的隔離級別。它經過強制事務串行執行,避免了前面說的幻讀的問題。簡單來講,SERIALIZABLE會在讀取的每一行數據上都加鎖,因此可能致使大量的超時和鎖爭用的問題。
READ UNCOMMITTED(未提交讀)的實現
SELECT語句以非鎖定方式被執行
READ COMMITTED(提交讀)的實現
這個隔離級別是經過MVCC實現的,事務每次讀的時候,都會讀刷新過的快照版本,也就是最新的數據。
REPEATABLE READ(可重複讀)的實現
這個隔離級別也是經過MVCC實現的,事務每次讀的時候,都會讀快照版本,也就是舊的版本數據。
SERIALIZABLE(可串行化)的實現
這個級別相似REPEATABLE READ,可是全部無格式SELECT語句被 隱式轉換成SELECT ... LOCK IN SHARE MODE,也就是默認的加共享鎖!
InnoDB的默認隔離級別是RR(REPEATABLE READ),這個級別使用了MVCC+快照讀,完美的解決了不可重複讀和幻讀的問題
雖然RC(READ COMMITTED)也是MVCC實現的,可是每次讀的時候,都會刷新快照,因此會存在不可重複讀和幻讀的現象
PS:關於幻行,在RR級別,事務A雖然SELECT不到事務B的INSERT,可是能夠經過UPDATE,DELETE感知到事務B事務B的INSERT,能夠去實驗一下
上面說到了使用MVCC+快照讀實際上是能夠避免幻讀的,不過還有一種狀況沒有考慮到
在InnoDB下,使用SELECT ... FOR UPDATE 和 SELECT ... LOCK IN SHARE MODE時,如何避免幻讀?
單純的使用行鎖的話,是沒法阻止幻讀的(想一想爲何);解決這個問題,蠢辦法就是使用表鎖;InnoDB設置者固然不會這麼幹,這裏須要先了解間隙鎖
關於間隙鎖和next-lock。。。。百度去吧,累了