1. 程式人生 > >資料庫隔離級別及實現原理

資料庫隔離級別及實現原理

事情的起源於一個面試,面試官讓我說說資料庫的隔離級別,以及他們各自對應著什麼問題,這個還好說,說出來後他接著追問readcommited的原理,當時楞了一下,因為的確沒接觸過,雖然知道肯定是鎖的作用,但不知道怎麼說好,怎麼著手,就直接說不清楚了。。。然後就涼了。。。下面記錄一下吧!

所謂的資料庫事務操作其實就是一組原子性的操作,要麼全部操作成功,要麼全部操作失敗。

    並行事務的四大問題:
    1.更新丟失:和別的事務讀到相同的東西,各自寫,自己的寫被覆蓋了。(誰寫的快誰的更新就丟失了)
    2.髒讀:讀到別的事務未提交的資料。(萬一回滾,資料就是髒的無效的了)
    3.不可重複讀:兩次讀之間有別的事務修改。
    4.幻讀:兩次讀之間有別的事務增刪。

    對應隔離級別
    1.READ UNCOMMITTED:讀未提交,不處理。
    2.READ COMMITTED:讀已提交,只讀提交的資料,無髒讀;
    3.REPEATABLE READ:可重複讀,加行鎖,兩次讀之間不會有修改,無髒讀無重複讀;
    4.SERIALIZABLE: 序列化,加表鎖,全部序列,無所有問題。

這裡寫圖片描述
 

  1.READ UNCIMMITTED(未提交讀)
  事務還沒提交,而別的事務可以看到他其中修改的資料的後果,也就是髒讀。

  2.READ COMMITTED(提交讀)
  首先大多數資料庫系統的預設隔離級別是READ COMMITTED,這種隔離級別就是一個事務的開始,只能看到已經完成的事務的結果,正在執行的,是無法被其他事務看到的。這種級別會出現讀取舊資料的現象

  3.REPEATABLE READ(可重複讀)
   REPEATABLE READ解決了髒讀的問題,該級別保證了每行的記錄的結果是一致的,也就是上面說的讀了舊資料的問題,但是卻無法解決另一個問題,幻行,顧名思義就是突然蹦出來的行資料。指的就是某個事務在讀取某個範圍的資料,但是另一個事務又向這個範圍的資料去插入資料,導致多次讀取的時候,資料的行數不一致。

  總結:雖然讀取同一條資料可以保證一致性,但是卻不能保證沒有插入新的資料

  4.SERIALIZABLE(可序列化)
  SERIALIZABLE是最高的隔離級別,它通過強制事務序列執行(注意是序列),避免了前面的幻讀情況,由於他大量加上鎖,導致大量的請求超時,因此效能會比較低下,在需要資料一致性且併發量不需要那麼大的時候才可能考慮這個隔離級別。

下面是各個隔離級別的原理:

READ_UNCOMMITED 的原理:

1,事務對當前被讀取的資料不加鎖;
2,事務在更新某資料的瞬間(就是發生更新的瞬間),必須先對其加 行級共享鎖,直到事務結束才釋放。

下面分別對應上面1,2產生的表現:

1,事務1讀取某行記錄時,事務2也能對這行記錄進行讀取、更新;當事務2對該記錄進行更新時,事務1再次讀取該記錄,能讀到事務2對該記錄的修改版本,即使該修改尚未被提交。
2,事務1更新某行記錄時,事務2不能對這行記錄做更新,直到事務1結束。

READ_COMMITED 的原理:

1,事務對當前被讀取的資料加 行級共享鎖(當讀到時才加鎖),一旦讀完該行,立即釋放該行級共享鎖;
2,事務在更新某資料的瞬間(就是發生更新的瞬間),必須先對其加 行級排他鎖,直到事務結束才釋放。

表現:

1,事務1讀取某行記錄時,事務2也能對這行記錄進行讀取、更新;當事務2對該記錄進行更新時,事務1再次讀取該記錄,讀到的只能是事務2對其更新前的版本,要不就是事務2提交後的版本。
2,事務1更新某行記錄時,事務2不能對這行記錄做更新,直到事務1結束。

REPEATABLE READ 的原理:

1,事務在讀取某資料的瞬間(就是開始讀取的瞬間),必須先對其加 行級共享鎖,直到事務結束才釋放;
2,事務在更新某資料的瞬間(就是發生更新的瞬間),必須先對其加 行級排他鎖,直到事務結束才釋放。

表現:

1,事務1讀取某行記錄時,事務2也能對這行記錄進行讀取、更新;當事務2對該記錄進行更新時,事務1再次讀取該記錄,讀到的仍然是第一次讀取的那個版本。
2,事務1更新某行記錄時,事務2不能對這行記錄做更新,直到事務1結束。

SERIALIZABLE 的原理:

1,事務在讀取資料時,必須先對其加 表級共享鎖 ,直到事務結束才釋放;
2,事務在更新資料時,必須先對其加 表級排他鎖 ,直到事務結束才釋放。

表現:

1,事務1正在讀取A表中的記錄時,則事務2也能讀取A表,但不能對A表做更新、新增、刪除,直到事務1結束。
2,事務1正在更新A表中的記錄時,則事務2不能讀取A表的任意記錄,更不可能對A表做更新、新增、刪除,直到事務1結束。

這裡咋一看覺得能理解,但細想沒有特別搞清,主要是這裡面出現的幾種鎖,下面是資料庫涉及和本文涉及到的鎖的解釋:
這裡只針對MySQL,其他的可能有細微差別,但總體都是一個思想;
MySQL的鎖機制比較簡單,其最顯著的特點是不同的儲存引擎支援不同的鎖機制。比如,

MyISAM和MEMORY儲存引擎採用的是表級鎖(table-level locking)。
InnoDB儲存引擎既支援行級鎖(row-level locking),也支援表級鎖,但預設情況下是採用行級鎖。
所有的鎖都是繫結在資料庫的索引機制上的!!!
首先鎖可以分為:

表級鎖:開銷小,加鎖快;不會出現死鎖;鎖定粒度大,發生鎖衝突的概率最高,併發度最低。
行級鎖:開銷大,加鎖慢;會出現死鎖;鎖定粒度最小,發生鎖衝突的概率最低,併發度也最高。
頁面鎖:開銷和加鎖時間界於表鎖和行鎖之間;會出現死鎖;鎖定粒度界於表鎖和行鎖之間,併發度一般。

而除了粒度,鎖根據模式分為:

共享鎖(S):發生在資料查詢之前,多個事務的共享鎖之間可以共存

排他鎖(X):發生在資料更新之前,排他鎖是一個獨佔鎖,與其他鎖都不相容

更新鎖(U):發生在更新語句中,更新鎖用來查詢資料,當查詢的資料不是要更新的資料時轉化為S鎖,當是要更新的資料時轉化為X鎖

意向鎖:發生在較低粒度級別的資源獲取之前,表示對該資源下低粒度的資源新增對應的鎖,意向鎖有分為:意向共享鎖(IS) ,意向排他鎖(IX),意向更新鎖(IU),共享意向排他鎖(SIX),共享意向更新鎖(SIU),更新意向排他鎖(UIX)

意向鎖又分為

意向共享鎖(IS):事務打算給資料行加行共享鎖,事務在給一個數據行加共享鎖前必須先取得該表的IS鎖。
意向排他鎖(IX):事務打算給資料行加行排他鎖,事務在給一個數據行加排他鎖前必須先取得該表的IX鎖。

共享鎖/排他鎖/更新鎖一般作用在較低級別上,例如資料行或資料頁,意向鎖一般作用在較高的級別上,例如資料表或資料。鎖是有層級結構的,若在資料行上持有排他鎖的時候,則會在所在的資料頁上持有意向排他鎖. 在一個事務中,可能由於鎖持有的時間太長或個數太多,出於節約資源的考慮,會造成鎖升級;

我理解的就以最後一個SERIALIZABLE來說,開始時是設定的表級共享鎖,分為表級別的而且是共享鎖,表級別就是作用於整個表,不是行級別!而共享鎖,則說明了其他事務也是共享鎖的情況下可以共享這個表!雖然僅限於讀,但這樣也可能存在髒讀等情況的存在,而如果換成表級排它鎖,那麼第一個事務在使用了這個鎖之後,那其他事務連這個表的讀的許可權也沒有,從根本上避免了各種可能的問題。

最後總結一下各個索引引擎的情況:

1.InnoDB(MySQL預設儲存引擎 從版本5.5.5開始)
支援事務,行級鎖,以及外來鍵,擁有高併發處理能力。但是在建立索引和載入資料時,比MyISAM慢。預設的隔離級別是Repeatable Read(可重複讀)

2.MyISAM
不支援事務和行級鎖。所以速度很快,效能優秀。可以對整張表加鎖,支援併發插入,支援全文索引。

3.MEMORY
支援Hash索引,記憶體表,Memory引擎將資料儲存在記憶體中,表結構不是儲存在記憶體中的,查詢時不需要執行磁碟I/O操作,所以要比MyISAM和InnoDB快很多倍,但是資料庫斷電或是重啟後,表中的資料將會丟失,表結構不會丟失。

先這樣吧,如果散開說,那又要扯到各個索引引擎的實現原理,B樹和B+樹的區別了。。。