国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 數據庫 > MySQL > 正文

在MySQL中的事務隔離級別如何實現

2024-07-24 12:33:20
字體:
來源:轉載
供稿:網友
  小編給大家分享一下MySQL中的事務隔離級別如何實現,相信大部分人都還不怎么了解,因此分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后大有收獲,下面讓我們一起去了解一下吧!
 
  說到數據庫事務,大家腦子里一定很容易蹦出一堆事務的相關知識,如事務的ACID特性,隔離級別,解決的問題(臟讀,不可重復讀,幻讀)等等,但是可能很少有人真正的清楚事務的這些特性又是怎么實現的,為什么要有四個隔離級別。
 
  今天我們就先來聊聊MySQL中事務的隔離性的實現原理,后續還會繼續出文章分析其他特性的實現原理。
 
  當然MySQL博大精深,文章疏漏之處在所難免,歡迎批評指正。
 
  說明
 
  MySQL的事務實現邏輯是位于引擎層的,并且不是所有的引擎都支持事務的,下面的說明都是以InnoDB引擎為基準。
 
  定義
  隔離性(isolation)指的是不同事務先后提交并執行后,最終呈現出來的效果是串行的,也就是說,對于事務來說,它在執行過程中,感知到的數據變化應該只有自己操作引起的,不存在其他事務引發的數據變化。
 
  隔離性解決的是并發事務出現的問題。
 
  標準SQL隔離級別
  隔離性最簡單的實現方式就是各個事務都串行執行了,如果前面的事務還沒有執行完畢,后面的事務就都等待。但是這樣的實現方式很明顯并發效率不高,并不適合在實際環境中使用。
 
  為了解決上述問題,實現不同程度的并發控制,SQL的標準制定者提出了不同的隔離級別:未提交讀(read uncommitted)、提交讀(read committed)、可重復讀(repeatable read)、序列化讀(serializable)。其中最高級隔離級別就是序列化讀,而在其他隔離級別中,由于事務是并發執行的,所以或多或少允許出現一些問題。見以下的矩陣表:
 
  隔離級別(+:允許出現,-:不允許出現) 臟讀 不可重復讀 幻讀
  未提交讀 + + +
  提交讀 - + +
  可重復讀 - - +
  序列化讀 - - -
  注意,MySQL的InnoDB引擎在可重復讀級別通過間隙鎖解決了幻讀問題,通過MVCC解決了不可重復讀的問題,具體見下面的分析。
 
  實現原理
  標準SQL事務隔離級別實現原理
  我們上面遇到的問題其實就是并發事務下的控制問題,解決并發事務的最常見方式就是悲觀并發控制了(也就是數據庫中的鎖)。標準SQL事務隔離級別的實現是依賴鎖的,我們來看下具體是怎么實現的:

  可以看到,在只使用鎖來實現隔離級別的控制的時候,需要頻繁的加鎖解鎖,而且很容易發生讀寫的沖突(例如在RC級別下,事務A更新了數據行1,事務B則在事務A提交前讀取數據行1都要等待事務A提交并釋放鎖)。
 
  為了不加鎖解決讀寫沖突的問題,MySQL引入了MVCC機制,詳細可見我以前的分析文章:一文讀懂數據庫中的樂觀鎖和悲觀鎖和MVCC。
 
  在往下分析之前,我們有幾個概念需要先了解下:
 
  1、鎖定讀和一致性非鎖定讀
 
  鎖定讀:在一個事務中,主動給讀加鎖,如SELECT ... LOCK IN SHARE MODE 和 SELECT ... FOR UPDATE。分別加上了行共享鎖和行排他鎖。鎖的分類可見我以前的分析文章:你應該了解的MySQL鎖分類)。
 
  https://dev.mysql.com/doc/refman/8.0/en/innodb-locking-reads.html
  一致性非鎖定讀:InnoDB使用MVCC向事務的查詢提供某個時間點的數據庫快照。查詢會看到在該時間點之前提交的事務所做的更改,而不會看到稍后或未提交的事務所做的更改(本事務除外)。也就是說在開始了事務之后,事務看到的數據就都是事務開啟那一刻的數據了,其他事務的后續修改不會在本次事務中可見。
 
  Consistent read是InnoDB在RC和RR隔離級別處理SELECT語句的默認模式。一致性非鎖定讀不會對其訪問的表設置任何鎖,因此,在對表執行一致性非鎖定讀的同時,其它事務可以同時并發的讀取或者修改它們。
 
  https://dev.mysql.com/doc/refman/8.0/en/innodb-consistent-read.html
  2、當前讀和快照讀
 
  當前讀
 
  讀取的是最新版本,像UPDATE、DELETE、INSERT、SELECT ...  LOCK IN SHARE MODE、SELECT ... FOR UPDATE這些操作都是一種當前讀,為什么叫當前讀?就是它讀取的是記錄的最新版本,讀取時還要保證其他并發事務不能修改當前記錄,會對讀取的記錄進行加鎖。
 
  快照讀
 
  讀取的是快照版本,也就是歷史版本,像不加鎖的SELECT操作就是快照讀,即不加鎖的非阻塞讀;快照讀的前提是隔離級別不是未提交讀和序列化讀級別,因為未提交讀總是讀取最新的數據行,而不是符合當前事務版本的數據行,而序列化讀則會對表加鎖。
 
  3、隱式鎖定和顯式鎖定
 
  隱式鎖定
 
  InnoDB在事務執行過程中,使用兩階段鎖協議(不主動進行顯示鎖定的情況):
 
  隨時都可以執行鎖定,InnoDB會根據隔離級別在需要的時候自動加鎖;
  鎖只有在執行commit或者rollback的時候才會釋放,并且所有的鎖都是在同一時刻被釋放。
  顯式鎖定
 
  InnoDB也支持通過特定的語句進行顯示鎖定(存儲引擎層)
  select ... lock in share mode //共享鎖
  select ... for update //排他鎖
  MySQL Server層的顯示鎖定:
  lock table
  unlock table
  了解完上面的概念后,我們來看下InnoDB的事務具體是怎么實現的(下面的讀都指的是非主動加鎖的select)
 
  事務隔離級別 實現方式
  未提交讀(RU) 事務對當前被讀取的數據不加鎖,都是當前讀;
 
  事務在更新某數據的瞬間(就是發生更新的瞬間),必須先對其加行級共享鎖,直到事務結束才釋放。
  提交讀(RC) 事務對當前被讀取的數據不加鎖,且是快照讀;
 
  事務在更新某數據的瞬間(就是發生更新的瞬間),必須先對其加行級排他鎖(Record),直到事務結束才釋放。
  可重復讀(RR) 事務對當前被讀取的數據不加鎖,且是快照讀;
 
  事務在更新某數據的瞬間(就是發生更新的瞬間),必須先對其加行級排他鎖(Record,GAP,Next-Key),直到事務結束才釋放。
 
  通過間隙鎖,在這個級別MySQL就解決了幻讀的問題
 
  通過快照,在這個級別MySQL就解決了不可重復讀的問題
  序列化讀(S) 事務在讀取數據時,必須先對其加表級共享鎖 ,直到事務結束才釋放,都是當前讀;
 
  事務在更新數據時,必須先對其加表級排他鎖 ,直到事務結束才釋放。
  可以看到,InnoDB通過MVCC很好的解決了讀寫沖突的問題,而且提前一個級別就解決了標準級別下會出現的幻讀問題,大大提升了數據庫的并發能力。
 
  MVCC能解決了幻讀問題?
  網上很多文章會說MVCC或者MVCC+間隙鎖解決了幻讀問題,實際上MVCC并不能解決幻讀問題。如以下的例子:
 
  begin;
 
  #假設users表為空,下面查出來的數據為空
 
  select * from users; #沒有加鎖
 
  #此時另一個事務提交了,且插入了一條id=1的數據
 
  select * from users; #讀快照,查出來的數據為空
 
  update users set name='mysql' where id=1;#update是當前讀,所以更新成功,并生成一個更新的快照
 
  select * from users; #讀快照,查出來id為1的一條記錄,因為MVCC可以查到當前事務生成的快照
 
  commit;
  可以看到前后查出來的數據行不一致,發生了幻讀。所以說只有MVCC是不能解決幻讀問題的,解決幻讀問題靠的是間隙鎖。如下:
 
  begin;
 
  #假設users表為空,下面查出來的數據為空
 
  select * from users lock in share mode; #加上共享鎖
 
  #此時另一個事務B想提交且插入了一條id=1的數據,由于有間隙鎖,所以要等待
 
  select * from users; #讀快照,查出來的數據為空
 
  update users set name='mysql' where id=1;#update是當前讀,由于不存在數據,不進行更新
 
  select * from users; #讀快照,查出來的數據為空
 
  commit;
 
  #事務B提交成功并插入數據
  注意,RR級別下想解決幻讀問題,需要我們顯式加鎖,不然查詢的時候還是不會加鎖的。
 
  以上是“MySQL中的事務隔離級別如何實現”這篇文章的所有內容,感謝各位的閱讀!

(編輯:武林網)

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 三亚市| 崇义县| 潼南县| 渝北区| 洪湖市| 呼图壁县| 泸州市| 麻江县| 辽源市| 宜都市| 筠连县| 贺兰县| 察雅县| 遂宁市| 卢氏县| 杨浦区| 清徐县| 阿图什市| 南木林县| 神农架林区| 开封市| 榆社县| 扬州市| 灵武市| 库车县| 乌兰浩特市| 剑河县| 仁化县| 杂多县| 叶城县| 凤庆县| 阆中市| 浦江县| 长沙县| 阜阳市| 利津县| 图木舒克市| 海丰县| 南川市| 黄龙县| 双柏县|