關於資料庫事務和鎖的必會知識點,你掌握了多少?

zydybaby發表於2021-01-05

關於MySQL資料庫的這些核心知識點,你都掌握了嗎?

推薦閱讀:

資料庫的事務

什麼是資料庫的事務?

百度百科的解釋:資料庫事務( transaction)是訪問並可能操作各種資料項的一個資料庫操作序列,這些操作要麼全部執行,要麼全部不執行,是一個不可分割的工作單位。事務由事務開始與事務結束之間執行的全部資料庫操作組成。

事務的四大特性是什麼?

  • 原子性:原子性是指包含事務的操作要麼全部執行成功,要麼全部失敗回滾。
  • 一致性:一致性指事務在執行前後狀態是一致的。
  • 隔離性:一個事務所進行的修改在最終提交之前,對其他事務是不可見的。
  • 永續性:資料一旦提交,其所作的修改將永久地儲存到資料庫中。

資料庫的併發一致性問題

當多個事務併發執行時,可能會出現以下問題:

  • 髒讀:事務A更新了資料,但還沒有提交,這時事務B讀取到事務A更新後的資料,然後事務A回滾了,事務B讀取到的資料就成為髒資料了。
  • 不可重複讀:事務A對資料進行多次讀取,事務B在事務A多次讀取的過程中執行了更新操作並提交了,導致事務A多次讀取到的資料並不一致。
  • 幻讀:事務A在讀取資料後,事務B向事務A讀取的資料中插入了幾條資料,事務A再次讀取資料時發現多了幾條資料,和之前讀取的資料不一致。
  • 丟失修改:事務A和事務B都對同一個資料進行修改,事務A先修改,事務B隨後修改,事務B的修改覆蓋了事務A的修改。

不可重複度和幻讀看起來比較像,它們主要的區別是:在不可重複讀中,發現資料不一致主要是資料被更新了。在幻讀中,發現資料不一致主要是資料增多或者減少了。

資料庫的隔離級別有哪些?

  • 未提交讀:一個事務在提交前,它的修改對其他事務也是可見的。
  • 提交讀:一個事務提交之後,它的修改才能被其他事務看到。
  • 可重複讀:在同一個事務中多次讀取到的資料是一致的。
  • 序列化:需要加鎖實現,會強制事務序列執行。

資料庫的隔離級別分別可以解決資料庫的髒讀、不可重複讀、幻讀等問題。

隔離級別 髒讀 不可重複讀 幻讀
未提交讀 允許 允許 允許
提交讀 不允許 允許 允許
可重複讀 不允許 不允許 允許
序列化 不允許 不允許 不允許

MySQL的預設隔離級別是可重複讀。

隔離級別是如何實現的?

事務的隔離機制主要是依靠鎖機制和MVCC(多版本併發控制)實現的,提交讀和可重複讀可以通過MVCC實現,序列化可以通過鎖機制實現。

什麼是MVCC?

MVCC(multiple version concurrent control)是一種控制併發的方法,主要用來提高資料庫的併發效能。

在瞭解MVCC時應該先了解當前讀和快照讀。

  • 當前讀:讀取的是資料庫的最新版本,並且在讀取時要保證其他事務不會修該當前記錄,所以會對讀取的記錄加鎖。
  • 快照讀:不加鎖讀取操作即為快照讀,使用MVCC來讀取快照中的資料,避免加鎖帶來的效能損耗。

可以看到MVCC的作用就是在不加鎖的情況下,解決資料庫讀寫衝突問題,並且解決髒讀、幻讀、不可重複讀等問題,但是不能解決丟失修改問題。

MVCC的實現原理:

  • 版本號

    系統版本號:是一個自增的ID,每開啟一個事務,系統版本號都會遞增。

    事務版本號:事務版本號就是事務開始時的系統版本號,可以通過事務版本號的大小判斷事務的時間順序。

  • 行記錄隱藏的列

    DB_ROW_ID:所需空間6byte,隱含的自增ID,用來生成聚簇索引,如果資料表沒有指定聚簇索引,InnoDB會利用這個隱藏ID建立聚簇索引。

    DB_TRX_ID:所需空間6byte,最近修改的事務ID,記錄建立這條記錄或最後一次修改這條記錄的事務ID。

    DB_ROLL_PTR:所需空間7byte,回滾指標,指向這條記錄的上一個版本。

    它們大致長這樣,省略了具體欄位的值。·
    在這裡插入圖片描述

  • undo日誌

    MVCC做使用到的快照會儲存在Undo日誌中,該日誌通過回滾指標將一個一個資料行的所有快照連線起來。它們大致長這樣。
    在這裡插入圖片描述

舉一個簡單的例子說明下,比如最開始的某條記錄長這樣

在這裡插入圖片描述

現在來了一個事務對他的年齡欄位進行了修改,變成了這樣

在這裡插入圖片描述

現在又來了一個事務2對它的性別進行了修改,它又變成了這樣

在這裡插入圖片描述

從上面的分析可以看出,事務對同一記錄的修改,記錄的各個會在Undo日誌中連線成一個線性表,在表頭的就是最新的舊紀錄。

在重複讀的隔離級別下,InnoDB的工作流程:

  • SELECT

    作為查詢的結果要滿足兩個條件:

    1. 當前事務所要查詢的資料行快照的建立版本號必須小於當前事務的版本號,這樣做的目的是保證當前事務讀取的資料行的快照要麼是在當前事務開始前就已經存在的,要麼就是當前事務自身插入或者修改過的。
    2. 當前事務所要讀取的資料行快照的刪除版本號必須是大於當前事務的版本號,如果是小於等於的話,表示該資料行快照已經被刪除,不能讀取。
  • INSERT

    將當前系統版本號作為資料行快照的建立版本號。

  • DELETE

    將當前系統版本號作為資料行快照的刪除版本號。

  • UPDATE

    儲存當前系統版本號為更新前的資料行快照建立行版本號,並儲存當前系統版本號為更新後的資料行快照的刪除版本號,其實就是,先刪除在插入即為更新。

總結一下,MVCC的作用就是在避免加鎖的情況下最大限度解決讀寫併發衝突的問題,它可以實現提交讀和可重複度兩個隔離級。

資料庫的鎖

什麼是資料庫的鎖?

當資料庫有併發事務的時候,保證資料訪問順序的機制稱為鎖機制。

資料庫的鎖與隔離級別的關係?

隔離級別 實現方式
未提交讀 總是讀取最新的資料,無需加鎖
提交讀 讀取資料時加共享鎖,讀取資料後釋放共享鎖
可重複讀 讀取資料時加共享鎖,事務結束後釋放共享鎖
序列化 鎖定整個範圍的鍵,一直持有鎖直到事務結束

資料庫鎖的型別有哪些?

按照鎖的粒度可以將MySQL鎖分為三種:

MySQL鎖類別 資源開銷 加鎖速度 是否會出現死鎖 鎖的粒度 併發度
表級鎖 不會
行級鎖
頁面鎖 一般 一般 不會 一般 一般

MyISAM預設採用表級鎖,InnoDB預設採用行級鎖。

從鎖的類別上區別可以分為共享鎖和排他鎖

  • 共享鎖:共享鎖又稱讀鎖,簡寫為S鎖,一個事務對一個資料物件加了S鎖,可以對這個資料物件進行讀取操作,但不能進行更新操作。並且在加鎖期間其他事務只能對這個資料物件加S鎖,不能加X鎖。
  • 排他鎖:排他鎖又稱為寫鎖,簡寫為X鎖,一個事務對一個資料物件加了X鎖,可以對這個物件進行讀取和更新操作,加鎖期間,其他事務不能對該資料物件進行加X鎖或S鎖。

它們的相容情況如下(不太會用excel,圖太醜了):

在這裡插入圖片描述

MySQL中InnoDB引擎的行鎖模式及其是如何實現的?

行鎖模式

在存在行鎖和表鎖的情況下,一個事務想對某個表加X鎖時,需要先檢查是否有其他事務對這個表加了鎖或對這個表的某一行加了鎖,對錶的每一行都進行檢測一次這是非常低效率的,為了解決這種問題,實現多粒度鎖機制,InnoDB還有兩種內部使用的意向鎖,兩種意向鎖都是表鎖。

  • 意向共享鎖:簡稱IS鎖,一個事務打算給資料行加共享鎖前必須先獲得該表的IS鎖。
  • 意向排他鎖:簡稱IX鎖,一個事務打算給資料行加排他鎖前必須先獲得該表的IX鎖。

有了意向鎖,一個事務想對某個表加X鎖,只需要檢查是否有其他事務對這個表加了X/IX/S/IS鎖即可。

鎖的相容性如下:

在這裡插入圖片描述

行鎖實現方式:INnoDB的行鎖是通過給索引上的索引項加鎖實現的,如果沒有索引,InnoDB將通過隱藏的聚簇索引來對記錄進行加鎖。

InnoDB行鎖主要分三種情況:

  • Record lock:對索引項加鎖
  • Grap lock:對索引之間的“間隙”、第一條記錄前的“間隙”或最後一條後的間隙加鎖。
  • Next-key lock:前兩種放入組合,對記錄及前面的間隙加鎖。

InnoDB行鎖的特性:如果不通過索引條件檢索資料,那麼InnoDB將對錶中所有記錄加鎖,實際產生的效果和表鎖是一樣的。

MVCC不能解決幻讀問題,在可重複讀隔離級別下,使用MVCC+Next-Key Locks可以解決幻讀問題。

什麼是資料庫的樂觀鎖和悲觀鎖,如何實現?

樂觀鎖:系統假設資料的更新在大多數時候是不會產生衝突的,所以資料庫只在更新操作提交的時候對資料檢測衝突,如果存在衝突,則資料更新失敗。

樂觀鎖實現方式:一般通過版本號和CAS演算法實現。

悲觀鎖:假定會發生併發衝突,遮蔽一切可能違反資料完整性的操作。通俗講就是每次去拿資料的時候都認為別人會修改,所以每次在拿資料的時候都會上鎖。

悲觀鎖的實現方式:通過資料庫的鎖機制實現,對查詢語句新增for updata。

什麼是死鎖?如何避免?

死鎖是指兩個或者兩個以上程式在執行過程中,由於競爭資源或者由於彼此通訊而造成的一種阻塞的現象。在MySQL中,MyISAM是一次獲得所需的全部鎖,要麼全部滿足,要麼等待,所以不會出現死鎖。在InnoDB儲存引擎中,除了單個SQL組成的事務外,鎖都是逐步獲得的,所以存在死鎖問題。

如何避免MySQL發生死鎖或鎖衝突:

  • 如果不同的程式併發存取多個表,儘量以相同的順序訪問表。

  • 在程式以批量方式處理資料的時候,如果已經對資料排序,儘量保證每個執行緒按照固定的順序來處理記錄。

  • 在事務中,如果需要更新記錄,應直接申請足夠級別的排他鎖,而不應該先申請共享鎖,更新時在申請排他鎖,因為在當前使用者申請排他鎖時,其他事務可能已經獲得了相同記錄的共享鎖,從而造成鎖衝突或者死鎖。

  • 儘量使用較低的隔離級別

  • 儘量使用索引訪問資料,使加鎖更加準確,從而減少鎖衝突的機會

  • 合理選擇事務的大小,小事務發生鎖衝突的概率更低

  • 儘量用相等的條件訪問資料,可以避免Next-Key鎖對併發插入的影響。

  • 不要申請超過實際需要的鎖級別,查詢時儘量不要顯示加鎖

  • 對於一些特定的事務,可以表鎖來提高處理速度或減少死鎖的概率。

相關文章