我的學習MySQL的總結學習筆記,參考資料都在文末給出,建議閱讀
有如下兩個規定:html
鎖的兼容關係以下:git
一種提升共享資源併發性的方式就是讓鎖定對象更具備選擇性。github
應該儘可能只鎖定須要修改的那部分數據,而不是全部的資源。在給定的資源上,鎖定的數據量越少,發生鎖爭用的可能性就越小,於是能夠提升系統的併發程度。sql
可是加鎖須要消耗資源,鎖的各類操做(包括得到鎖、釋放鎖、檢查鎖是否已經解除)都會增長系統開銷。所以封鎖粒度越小,系統開銷就越大。數據庫
所謂的鎖策略,就是在鎖的開銷和數據的安全性之間尋求平衡。MySQL 中提供了兩種封鎖粒度:行級鎖以及表級鎖。安全
通常都是在表上施加行級鎖,並以各類複雜的方式來實現,以便鎖在比較多的狀況下儘量地提供更好的性能。服務器
每種MySQL存儲引擎均可以實現本身的鎖策略和鎖粒度,因此能夠根據應用場景選擇不一樣的方案。併發
鎖定整張表,是MySQL中最基本的鎖策略,而且是開銷最小的策略。性能
READ_LOCAL表鎖支持某些類型的併發操做。在表鎖中,寫鎖比讀鎖具備更高的優先級,所以寫鎖能夠插入到鎖隊列中讀鎖的前面,反之則不能。學習
MySQL服務器會爲諸如ALTER_TABLE之類的語句使用表鎖,而忽略存儲引擎的鎖機制。
最大程度地支持併發處理,同時帶來了最大的鎖開銷。
行級鎖只在存儲引擎層面實現,MySQL服務器層徹底不瞭解存儲引擎的鎖實現。
事務指的是知足 ACID 特性的一組操做,事務內的語句要麼所有執行成功,要麼所有執行失敗,能夠經過 Commit 提交一個事務,也可使用 Rollback 進行回滾。
可使用START TRANSACTION顯式地開啓一個事務,而後使用COMMIT提交事務將修改的數據持久保留,或者使用ROOLBACK撤銷全部的修改(回滾)。
MySQL 默認採用自動提交模式。也就是說,若是不顯式START TRANSACTION使用語句來開始一個事務,那麼每一個查詢操做都會被當作一個事務並自動提交。
原子性(Atomicity)
一個事務被視爲一個不可分割的最小工做單元,整個事務中的全部操做要麼所有提交成功,要麼所有失敗回滾。
回滾能夠用回滾日誌(Undo Log)來實現,回滾日誌記錄着事務所執行的修改操做,在回滾時反向執行這些修改操做便可。
一致性(Consistency)
數據庫老是從一個一致性狀態轉換到另外一個一致性狀態。事務若是最終沒有提交,那麼事務的修改也不會保存到數據庫中。在一致性狀態下,全部事務對同一個數據的讀取結果都是相同的。
隔離性(Isolation)
一個事務所作的修改在最終提交之前,對其它事務是不可見的。
持久性(durability)
一旦事務提交,則其所作的修改將會永遠保存到數據庫中。即便系統發生崩潰,事務執行的結果也不能丟失。持久性策略有多種級別,但不可能作到100%的持久性保證。
系統發生奔潰能夠用重作日誌(Redo Log)進行恢復,從而實現持久性。與回滾日誌記錄數據的邏輯修改不一樣,重作日誌記錄的是數據頁的物理修改。
事務的 ACID 特性概念簡單,但不是很好理解,主要是由於這幾個特性不是一種平級關係:
對於一些不須要事務的查詢類應用,選擇一個非事務型的存儲引擎(如MyISAM),能夠得到更高的性能。即便存儲引擎不支持事務,也能夠經過使用LOCK TABLES爲應用提供保護。
事務中的修改即便沒有提交,對其餘事務也都是可見的,事務能夠讀取未提交的數據,叫作髒讀。
未提交讀的性能不會比其餘級別好太多,但卻缺少其餘級別的不少優勢,在實際應用中不多使用。
大多數據庫系統的默認隔離級別都是提交讀,但MySQL不是。也可叫作不可重複度級別,由於執行兩次相同的查詢,可能會獲得不同的結果。
一個事務開始時,只能讀取到已經被提交的修改。換句話說,一個事務所作的修改在提交以前對其它事務是不可見的。
可重複讀是MySQL的默認事務隔離級別。
解決了未提交讀級別中的髒讀問題。該級別保證在同一個事務中屢次讀取同一數據的結果是同樣的。
可重複讀沒法解決幻讀問題。所謂幻讀,指的是,T1 讀取某個範圍的數據,T2 在這個範圍內插入新的數據,T1 再次讀取這個範圍的數據,此時讀取的結果和和第一次讀取的結果不一樣。
經過強制事務串行執行,在讀取的每一行數據上都加鎖,解決了幻讀的問題,可是可能會致使超時和鎖爭用問題。
產生併發不一致性問題的主要緣由是破壞了事務的隔離性,解決方法是經過併發控制來保證隔離性。併發控制能夠經過封鎖來實現,可是封鎖操做須要用戶本身控制,至關複雜。數據庫管理系統提供了事務的隔離級別,讓用戶以一種更輕鬆的方式處理併發一致性問題。
一級封鎖協議
事務 T 要修改數據 A 時必須加 X 鎖,直到 T 結束才釋放鎖。
能夠解決丟失修改問題,由於不能同時有兩個事務對同一個數據進行修改,那麼事務的修改就不會被覆蓋。
二級封鎖協議
在一級的基礎上,要求讀取數據 A 時必須加 S 鎖,讀取完立刻釋放 S 鎖。
能夠解決讀髒數據問題,由於若是一個事務在對數據 A 進行修改,根據 1 級封鎖協議,會加 X 鎖,那麼就不能再加 S 鎖了,也就是不會讀入數據。
三級封鎖協議
在二級的基礎上,要求讀取數據 A 時必須加 S 鎖,直到事務結束了才能釋放 S 鎖。
能夠解決不可重複讀的問題,由於讀 A 時,其它事務不能對 A 加 X 鎖,從而避免了在讀的期間數據發生改變。
隱式鎖定
InnoDB採用兩階段鎖定協議,也叫兩段鎖協議。
在事務執行過程當中,隨時均可以執行鎖定,鎖只有在執行提交或者回滾時纔會同時釋放。InnoDB會根據隔離級別在須要的時候自動加鎖
顯示鎖定
* SELECT ... LOCK IN SHARE MODE * SELECT ... FOR UPDATE
MySQL也能夠在服務器層面實現 LOCK TABLES 和 UNLOCK TABLES。
加鎖階段
在該階段能夠進行加鎖操做。在對任何數據進行讀操做以前要申請並得到共享鎖鎖,在進行寫操做以前要申請並得到排他鎖。加鎖不成功,則事務進入等待狀態,直到加鎖成功才繼續執行。
解鎖階段
當事務釋放了一個封鎖之後,事務進入解鎖階段,在該階段只能進行解鎖操做不能再進行加鎖操做。
多版本併發控制(Multi-Version Concurrency Control, MVCC)是 MySQL 的 InnoDB 存儲引擎實現隔離級別的一種具體方式,用於實現提交讀和可重複讀這兩種隔離級別。而未提交讀隔離級別老是讀取最新的數據行,要求很低,無需使用 MVCC。可串行化隔離級別須要對全部讀取的行都加鎖,單純使用 MVCC 沒法實現。
MVCC是經過保存數據在某個時間點的快照來實現的,不一樣的存儲引擎的MVCC實現是不一樣的,典型的有樂觀併發控制和悲觀併發控制。
InnoDB的MVCC經過在每行記錄後面保存兩個隱藏的列來實現。一個列保存建立時的系統版本號,一個列保存行的過時時(刪除時間)的系統版本號。
每開始一個新的事務,系統版本號都會自動遞增。事務開始時刻系統的版本號會做爲事務的版本號,用來和查詢到的每行記錄的版本號進行比較。
SELECT
InnoDB會根據兩個條件檢查每行記錄,只有符合如下兩個條件的記錄纔會被做爲結果返回
INSERT
InnoDB爲新插入的每一行保存當前系統版本號做爲行版本號。
DELETE
InnoDB爲刪除的每一行保存當前系統版本號做爲刪除標識。
UPDATE
InnoDB新插入一條紀錄,保存當前系統版本號爲行版本號,同時保存當前系統版本號到原來的行做爲行刪除標識。
使用系統版本號,在大多數狀況下的讀操做均可以不用加鎖,使得讀數據變得更簡單,提供系統性能,而且保證讀取到的記錄符合標準。
可是每一行都須要額外使用存儲空間,且須要作更多的檢查以及維護。
參考資料
《高性能MySQL》
cyc2018
美團技術團隊——InnoDB中的事務隔離級別和鎖的關係