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


關於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鎖對並發插入的影響。

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

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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM