資料庫之事務、隔離級別和併發問題

roc_guo發表於2022-08-24

資料庫之事務、隔離級別和併發問題資料庫之事務、隔離級別和併發問題

資料庫的事務一直以來是資料庫最核心的基礎知識,熟悉事務知識是深入學習資料庫的前提;同時,資料庫的事務也是網際網路面試最最最常問的知識之一。

本文我們將從以下幾個角度深入分析:

  • 事務的四大特性
  • 資料庫併發環境下的一致性問題
  • 資料庫的隔離級別分別所解決的一致性問題。
  • 話不多說,小夥伴們,上車吧!

    事務
    什麼是事務?

    事務是資料庫系統裡面非常重要的術語。它可以由一行簡單的SQL來實現,也可以由一組複雜的SQL來實現。對於MySQL來說,有兩種事務實現方式,一種是顯式事務,另一種則是隱式事務。顯式事務需要我們自己手動使用begin或start transaction開啟事務,執行完中間的SQL語句後使用commit提交事務,即手動提交。

    InnoDB預設為隱式事務,即自動提交,每一行insert、update、delete的SQL語句操作都預設為一個獨立的事務。如果想關閉自動提交,可以set autocommit = 0來實現。

    本質上來說,事務其實就是一系列邏輯操作,為了保證這一系列邏輯操作能夠被準確、統一、安全地執行,就需要透過規範和技術來實現事務,讓事務具備特性。

    事務有什麼特性?

    原子性(Atomicity)
    一個事務被視為不可分割的最小單元,一個事務的所有操作要麼全部提交成功,要麼全部失敗回滾。當你為一組行為開啟事務時,這組行為的全部操作要麼同時成功,要麼同時失敗,不存在某一步行為成功執行而另一步執行失敗的場景。一旦某個行為操作失敗,那麼這組行為裡包括執行成功和執行失敗的會全部回滾,就像什麼事都沒發生過一樣。

    隔離性(Isolation)
    一個事務所做的修改在最終提交以前,對其它事務是不可見的。

    一致性(Consistency)
    資料庫的資料在事務執行前後都保持一致性狀態。在一致性狀態下,所有事務對同一個資料的讀取結果都是相同的。不能存在同一個事務對某一組資料前後兩次讀取的內容不一致的場景。

    永續性(Durability)
    一旦事務提交,則其所做的修改將會永遠儲存到資料庫中。即使系統發生崩潰,事務執行的結果也不能丟失。

    對於一個事務,想要實現它必須遵循以上ACID四個特性,也就是這四個特性必須全部得到滿足才可以稱之為一個完整的資料庫“事務”。

    那麼InnoDB如何去實現這四個特性呢?不同的特性其實有不同的實現方式:

    資料庫之事務、隔離級別和併發問題資料庫之事務、隔離級別和併發問題

    併發一致性問題
    什麼是資料庫的併發一致性問題?

    併發一致性問題,指的是在併發環境下,因為事務的隔離性很難保證,所以會出現很多併發問題。

    資料庫的併發一致性問題一共有三種,分別是:

    髒讀(Dirty Read)
    指的是事務A讀取到了事務B已經修改但是還未提交的資料。

    假設A和B同時讀取一個資料,事務A讀取到這個資料的值為10,隨後將其值修改為20,按道理,事務A對這個資料的修改在事務未提交之前是不會被其他事務看到的,但是由於資料庫的隔離性未能保證,此時事務B也去讀取這個資料的值,就會直接讀取到事務A修改完之後的值20,那麼此時如果事務A進行了資料的回滾,不提交了。那麼事務B最終讀取到的就是一個過期的值20。

    這種情況就稱為髒讀。

    資料庫之事務、隔離級別和併發問題資料庫之事務、隔離級別和併發問題

    不可重複讀(Nnrepeatable Read)
    指的是在一個事務裡面兩次讀取到的內容不一樣。

    事務B讀取到某個資料S的值為10,隨後事務A將S的值修改為20。如果B再次讀取這個資料,讀取到的值就變為20。此時讀取的結果和第一次讀取的結果不同,這就是不可重複讀。按道理,事務B都還沒有提交,所讀取到的資料應該是對別的事務不可見的,換句話來說應該是安全的。但是由於併發環境下事務的隔離型未能滿足,多個事務在某一個相同時刻對同一個資料進行修改,就會出現這樣的併發衝突問題。

    資料庫之事務、隔離級別和併發問題資料庫之事務、隔離級別和併發問題

    幻讀(Phantom Read)
    指的是在一個事務內查詢某個資料範圍的資料,如果出現了兩次查詢的結果不一樣,就稱為“幻讀”。

    事務A根據條件查詢到某個範圍的資料[10,20,30,40.50],此時B在這個符合條件的範圍內插入新的資料,A再次讀取這個範圍的資料後,發現該範圍多出了一條資料60,此時就發生了“幻讀”現象。幻讀現象發生的本質,也是由於事務的隔離型未能保證導致的。

    資料庫之事務、隔離級別和併發問題資料庫之事務、隔離級別和併發問題

    所以,MySQL如何解決併發一致性問題?

    首先強調一下,MySQL並不等於InnoDB。

    InnoDB是MySQL5.5版本之後預設使用的儲存引擎。InnoDB使用MVCC可以解決髒讀和不可重複讀問題。但是,MVCC並不是唯一可以解決併發一致性問題的措施。MVCC本質上是一種樂觀鎖,透過比較不同事務的版本號的方式來解決問題。可以使用樂觀鎖,那麼一樣也可以使用悲觀鎖。MySQL的其他儲存引擎比如Myisam甚至無法使用事務,所以它一般用鎖來解決併發一致性問題。

    在這裡,我先不贅述InnoDB的MVCC和MySQL各種各樣的鎖,我們放到之後的文章來講。本篇文章主要強調事務本身。

    隔離級別
    什麼是資料庫的隔離級別?

    指的是實現了資料庫中的安全級別。從對ACID的實現程度上分為四個隔離級別。隔離級別越高的資料庫越安全,能解決的併發一致性問題也就越多。

    那麼資料庫有幾種隔離級別呢?

    未提交讀(Read Uncommitted)
    事務中的修改,即使沒有提交,對其它事務也是可見的。該隔離級別會發生髒讀、不可重複讀、幻讀。所以是最差的一個隔離級別。

    提交讀(Read Committed)
    一個事務只能讀取已經提交的事務所做的修改。換句話說,一個事務所做的修改在提交之前對其它事務是不可見的,所以該隔離級別解決了髒讀問題,也就是說,當你的資料庫實現到了提交讀這個隔離級別時,髒讀現象就不會再發生。

    可重複讀(Repeatable Read)
    保證在同一個事務中多次讀取同一資料的結果是一樣的。這是第三個隔離級別,也是InnoDB預設實現的隔離級別。

    當你的資料庫實現到了提交讀這個隔離級別時,髒讀和不可重複讀現象就都不會再發生。

    可序列化(Serializable)
    強制事務序列執行,這樣多個事務互不干擾,自然而然就不會出現併發一致性問題。

    該隔離級別需要加鎖實現,因為要使用加鎖機制保證同一時間只有一個事務執行。

    因為可序列化是序列執行,所以不會有併發問題。這也是最安全的,第四個隔離級別。

    總結一下:

  • 讀未提交就是一種最差的資料庫隔離級別, 說明你這個資料庫在多事務的時候非常不安全;
  • 提交讀能解決髒讀問題;
  • 可重複讀能解決不可重複讀和髒讀問題;
  • 序列化能解決髒讀、不可重複讀、幻讀問題。
  • 為什麼InnoDB不預設實現可序列化?

    資料庫提出了這四種隔離級別分別來解決不同的併發一致性問題。

    但是,難道隔離級別越高就越好嗎?對於各大程式語言,不僅僅要考慮“安全”,還要考慮“效能”,對於資料庫一樣如此。

    隔離級別越高,就代表著越安全,但是同時效能效率也就越低。你想想,當你的資料庫做到了序列化,就意味沒有併發問題產生。但是此時你讀取的資料身上掛著一把鎖,一個資料同一時刻只能被一個事務訪問,那麼剩下的事務獲取不到就只能排隊。實際生產環境中往往都是在併發環境中對資料庫進行操作,業務高峰的時候甚至會有幾萬、幾十萬個事務同時存在,所以序列化往往得不到業務上的滿足。這就需要在“安全”和“效能”之間做一個衡量,於是MySQL的InnoDB儲存引擎預設實現的隔離級別為“可重複讀”,而非可序列化。

    總結

    事務本質上就是一系列邏輯操作,不同資料庫、不同儲存引擎對事務的支援強度都是不一樣的。比如Mysql資料庫InnoDB引擎天然支援事務,而Myisam引擎則不支援事務。

    資料庫事務只有滿足了ACID四大特性,才能安全的被我們執行。如果是在某一個時刻只有一個事務在操作,那麼就不會出現併發一致性問題,那麼ACID就很容易滿足。因為隔離性是可以滿足的,我們只要滿足了原子性,就可以滿足一致性。

    但是在多事務的併發環境下,由於事務的隔離性很難滿足,就會產生髒讀、不可重複讀、幻讀的併發一致性問題。為了解決這些併發一致性問題,資料庫系統規範了四個隔離級別:未提交讀、提交讀、可重複讀、可序列化。

    隔離級別越高,併發環境下資料庫越安全,但是效能也越低。所以為了權衡安全和效能,InnoDB預設實現的隔離級別是“可重複讀”。

    那麼如何實現可重複讀呢?可以使用鎖,也可以使用MVCC。


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69901823/viewspace-2911757/,如需轉載,請註明出處,否則將追究法律責任。

相關文章