做者:z小趙web
★一枚用心堅持寫原創的「無趣」程序猿,在自身受益的同時也讓朋友們在技術上有所提高。面試
目錄
-
爲何須要鎖? -
MySQL 中鎖分類? -
什麼是事務? -
事務的隔離級別 -
MySQL 是怎麼實現事務機制的? -
MVCC 機制 -
總結
爲何須要鎖?
相信你們都比較熟悉電商系統中庫存管理的場景,對於平常活動促銷、61八、雙 11 等場景,會在規定時間內對商品進行促銷活動,假設如今有一款 HHKB 機械鍵盤要參與促銷活動,數據庫中準備了 10 件,促銷活動開始時,多位買家開始爭搶,每賣出一件商品,庫存減 1,直到賣完,那麼怎麼能保證商品不會賣超呢?數據庫
對於以上這個場景來講,咱們須要用到鎖機制來保證每賣出一件商品,對庫存進行更新操做時,其餘用戶請求不能對該商品庫存進行修改;換句話說,用戶 1 拿到了修改庫存的鎖,則只有用戶 1 能修改數據,而用戶 2 只能等着不能修改數據。以下圖所示:緩存
相反,若是沒有鎖的加持,用戶 1 和用戶 2 發現庫存還有 1 件商品,同時都開始下單,用戶 1 先將庫存更新爲 0,此時商品已經售完,而用戶 2 也將庫存更新爲 0,就致使了賣超的尷尬狀況。微信
MySQL 中鎖分類?
鎖根據使用場景不一樣,被分紅了各類各樣的鎖。好比讀寫能夠分爲讀鎖和寫鎖,對於讀請求之間相互是互不影響的,由於數據沒有被全部,你們讀取到的數據都是同樣的,因此讀鎖也稱之爲共享鎖;對於寫請求,因爲存在數據的變動,因此請求之間是互斥的,因此也稱之爲排它鎖。併發
對於根據鎖鎖定的範圍大小,能夠分爲全局鎖、表鎖、元數據鎖、行鎖:app
-
全局鎖:顧名思義就是對整個數據庫進行加鎖操做,加鎖期間,整個數據庫只可以進行讀操做。 -
表鎖:是對數據庫中的某張表進行加鎖,此時表與表之間能夠同時進行寫操做而互不影響,可是同一時刻 同一張表只能有一個寫操做。 -
頁面鎖:頁面鎖是介於表鎖和行鎖之間的一種鎖,其優點是中和表鎖和行鎖的鎖開銷。 -
行鎖:是對數據庫表中的某一行進行加鎖操做。
從上也可以看出,鎖的範圍是逐漸減少的,在實際生產環境中須要根據業務場景來選擇不一樣粒度的鎖。編輯器
什麼是事務?
由多個事件組成的一組動做,要麼同時成功,要麼發生失敗時全體進行回滾;換句話說就是多個原子操做合併爲一個原子操做。怎麼可以保證一個事務可以正確執行呢?想要保證一個事務正確執行的依據是其必須知足 ACID,即原子性(atomicity)、一致性(consistency)、隔離性(isolation)、持久性(durability)。flex
舉個例子:初始狀態 A 帳戶有 100 塊,B 帳戶有 100 塊。atom
-
原子性:一個事務或執行動做不能被分割爲多個階段去處理,因此整個執行流程要麼所有成功,要麼失敗 所有回滾。舉例:A 向 B 轉帳 100 塊,A 的帳戶變成 0,B 的帳戶變成 200,兩個帳戶的錢增減總體視爲一個動做。 -
一致性:一個狀態在經歷一些動做以後轉變成另一個狀態。舉例:A 向 B 轉帳 100 塊,通過 A 向 B 的一個轉帳動做而且執行成功時,A 的帳戶變成 0,B 的帳戶變成 200 塊,執行轉帳動做先後帳戶的總金額沒有發生改變。 -
隔離性:多個事務在併發操做時,相互之間不可以看到對方執行的動做,即相互之間是隔離的。隔離也分爲多個級別,不一樣的隔離級別所保證的隔離程度也是不同的。 -
持久性:一個事務一旦提交之後,其對數據源的修改是永久性的保存到了數據庫中。如何作到 100%的持久性是一個幾乎不太可能實現的事情,好比數據雖然持久化到了磁盤上,但因爲一些不可抗拒的外力因素致使數據發生了丟失,因此在實際狀況中須要規定一個持久性的級別,即認爲只須要數據持久化到磁盤上就能夠認爲達到了持久性的特性。
事務的隔離級別
MySQL 的事務隔離級別分爲一下幾種:
舉個例子:初始狀態 A 帳戶有 100 塊,B 帳戶有 100 塊。
-
讀未提交(Read Uncommitted):在一個事務中,部分提交後對其餘事務可見。舉例:A 轉帳給 B,A 的帳戶金額變成 0,B 的帳戶還未增長到 200,A 讀取本身的帳戶時,發現本身的帳戶金額變爲 0,可是因爲事務執行中途出現故障(假設 B 帳戶由於某種緣由帳戶被鎖定不能被轉帳),此時事務進行了回滾操做,那麼就會致使 A 讀取到帳戶金額是錯誤的。這種現象也稱之爲 髒讀。 -
讀已提交(Read Committed):讀操做只可以讀取到本身事務中的數據或者是事務提交後的結果。舉例:轉帳操做,若是一個請求讀取帳戶 A 的金額,若是事務正常提交了,則其讀取的帳戶金額必定是 0,若是事務回滾,則讀取到的金額必定是 100。可是讀已提交不可以避免重複讀,有可能兩次讀取到結果不一致。 -
可重複讀(Repeatable Read):該級別可以保證屢次讀取到的結果是相同的,可是不可以解決 幻讀的狀況。幻讀在數據庫中具體的體現是範圍查詢,好比第一次一個範圍查詢到的結果集的同時,另一個事務插入了數據,第二次查詢的結果和第一次查詢的結果集不同。 -
串行化(Serializable):強制全部事務按照順序依次執行,只有一個事務執行完成後,下一個事務才能接着執行。這樣就可以避免產生髒讀、不可重複讀、幻讀等狀況,可是同時也下降了數據庫的併發度。
在實際開發場景中,一樣須要根據實際業務場景來選擇合適的隔離級別,通常用的比較廣泛的兩種隔離級別是讀已提交和可重複讀。
MySQL 是怎麼實現事務機制的?
MySQL 中實現了事務機制的常見存儲引擎有 InnoDB 和 NDB(上篇文章也介紹過,本系列全程以 InnoDB 展開介紹)。使用 InnoDB 提交事務的時候,首先須要關閉自動提交,經過 set autocommit = 0
命令關閉自動提交功能,而後經過 start transaction
開啓一個事務,接着編寫在事務內要執行的 SQL,最後經過 commit
提交事務。
若是兩個事務同時提交,而且都須要操做同一個資源,此時會產生 死鎖,那麼 MySQL 是怎麼處理這樣狀況的呢?InnoDB 採用的是將持有最少行級排他鎖進行回滾操做,什麼叫持有最少行級排排它鎖?大白話解釋一下:就是每一個事務開啓後,須要執行增刪改等操做 SQL 的個數。好比有兩個事務,一個須要執行 2 個 select 語句和 3 個 update 語句,另一個須要執行 1 個 select 語句和 1 個 update 語句,當兩個事務的 update 同一時刻須要對方鎖住的資源時,會將後者的事務進行回滾操做。
MVCC 機制
爲了下降死鎖狀況的發生,MySQL 引入錄入 MVCC 機制,從而來進一步下降由於加減鎖而形成的系統開銷。經過在數據表上增長兩個隱藏列,一個列用於存儲當前行的建立時的版本,另一個列用於存儲當前行的刪除時的版本。下面咱們來看看 MVCC 機制在 CRUD 中是怎麼工做的。
-
SELECT 操做(由兩個條件決定): -
InnoDB 會查找建立行的版本小於等於當前事務版本的數據行。好比目前數據表裏的第一行如今有兩個版本,分別爲 1 和 2,而當前事務的版本號是 1,則此時會選擇版本號爲 1 的這一行. -
InnoDB 會查找刪除行的版本大於當前事務版本的數據行。好比目前數據表裏的第一行有兩個版本,分別爲 1 和 2,可是版本號爲 1 的那行的刪除列標記爲 0,而當前書屋的版本號爲 1,則此時會選擇版本號爲 1 的這一行。
當同時知足以上兩個條件的時候,select 語句就能夠獲得一條最新的已提交的且未被刪除的行記錄。
-
UPDATE 操做:新插入一條數據到表中,而且將建立列的版本號設置爲當前事務的版本號,而且將當前事務的版本號保存到原來記錄的刪除列上。
-
INSERT 操做:新插入一條數據到表中,而且將建立列的版本號設置爲當前的事務版本號。
-
DELETE 操做:會將要刪除的行的歷史全部版本號所有設置上當前的事務版本號。
經過 CRUD 四種操做能夠看出,MVCC 機制只能做用於 讀已提交 和 可重複讀 兩個事務隔離級別下,由於讀未提交會使得 SELECT 產生髒讀,而串行化自己已是順序操做了,沒有增長 MVCC 機制的必要。
至於 MVCC 的具體實現細節,它結合了 undo log 來實現的,咱們在後面講解 MySQL 的相關日誌文件功能的時候再詳細展開。
總結
本文介紹了 MySQL 的鎖機制和事務的概念及實現原理。下篇文章咱們來接着研究MySQL 的索引機制,看看它到底該怎麼用才能使得查詢操做變得更加高效,敬請期待。
z小趙
關注系列優秀文章不走丟
進羣的小夥伴請加右側私人微信(備註:進羣)
本文分享自微信公衆號 - z小趙(Id_Bean)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。