MySQL事務(wù)和隔離

如何理解MySQL的事務(wù)?

  • 簡單來說就是要保證一組數(shù)據(jù)庫操作,要么全部成功,要么全部失敗。MySQL中的事務(wù)支持是在引擎層實現(xiàn)的。
  • 事務(wù)具有原子性(Atomicity)、一致性(Consistency)隔離性(Isolation)、持久性(Durability)四個特性。

SQL標準的事務(wù)隔離級別包括:

  • 讀未提交 read-uncommited
  • 讀提交  read-commited
  • 可重復(fù)讀 repeatable read
      一個事務(wù)執(zhí)行過程中看到的數(shù)據(jù),總是和這個事務(wù)啟動時看到的數(shù)據(jù)是一致的。在可重復(fù)讀隔離級別下,未提交的變更對其他線程也是不可見的。
  • 串行化  serializable
      對同一行記錄,讀會加“讀鎖”,寫會加“寫鎖”,當(dāng)出現(xiàn)讀寫沖突時,前后訪問的事務(wù)必須等前一個事務(wù)執(zhí)行完成,才能繼續(xù)執(zhí)行。

查看隔離級別

  • 在MySQL客戶端中輸入show variables like 'transaction-isolation';,即可查看當(dāng)前的隔離級別。

MySQL中的2種“視圖”

  • 一種是view,是一個用查詢語句定義的虛擬表,在調(diào)用的時候執(zhí)行查詢語句并生成結(jié)果,創(chuàng)建view的語法是create view ...,它的查詢方法與表一樣。
  • 另一種是consistent read view,它是InnoDB在實現(xiàn)MVCC時用到的一致性視圖,用于支持RC(讀提交)和RR(可重復(fù)讀)隔離級別的實現(xiàn)。

InnoDB隔離級別的實現(xiàn)

  • 事務(wù)啟動時,數(shù)據(jù)庫里會創(chuàng)建一個視圖,訪問的時候以視圖的邏輯結(jié)果為準。
  • 可重復(fù)讀隔離級別下,視圖是在事務(wù)啟動時創(chuàng)建的,整個事務(wù)存在期間都用這個視圖。
  • 讀提交隔離級別下,視圖是在每個SQL語句開始執(zhí)行的時候創(chuàng)建的。
  • 讀未提交隔離級別下,直接返回記錄上的最新值,沒有視圖。
  • 串行化隔離級別下,直接用加鎖的方式避免并行訪問。

可重復(fù)讀隔離級別的實現(xiàn)

  • 在可重復(fù)讀隔離級別下,MySQL中每條記錄在更新的時候都會記錄一條回滾操作。記錄上的最新值,通過回滾操作(undo log),都會得到前一狀態(tài)的值。不同時刻啟動的事務(wù)有不同的視圖,即同一條記錄在系統(tǒng)中可以存在多個版本,這就是數(shù)據(jù)庫的MVCC(多版本并發(fā)控制)。
  • InnoDB里每個事務(wù)都有一個唯一的transaction id,它是事務(wù)開始的時候向InnoDB事務(wù)系統(tǒng)申請的,按申請順序嚴格遞增。
  • 而每行數(shù)據(jù)也都是有多個版本的。每次事務(wù)更新數(shù)據(jù)的時候,都會生成一個新的版本,并把本次事務(wù)的transaction id賦值給這個數(shù)據(jù)版本的事務(wù)ID,記為row trx_id。同時舊的數(shù)據(jù)版本要保留,并且在新的數(shù)據(jù)版本中,能有信息(undo log)可以直接拿到它。也就是說數(shù)據(jù)表中的同一行記錄,可能存在多個版本(row),每個版本都有自己的row trx_id
  • MVCC的實現(xiàn),InnoDB為每個事務(wù)構(gòu)造一個數(shù)組,用來保存這個事務(wù)的啟動瞬間,當(dāng)前所有啟動了,但還沒提交的事務(wù)的ID。
  • 一個數(shù)據(jù)版本,對于一個事務(wù)視圖來說,除了自己的更新總是可見的以外,還有3種情況:
      1、版本未提交,不可見;
      2、版本已提交,但是是在視圖創(chuàng)建后提交的,不可見;
      3、版本已提交,而且是在視圖創(chuàng)建前提交的,可見。
    https://blog.csdn.net/syilt/article/details/107647007

基于可重復(fù)讀隔離的實現(xiàn),說明為什么盡量不要使用長事務(wù)?

  • 長事務(wù)意味著系統(tǒng)里面會存在很老的事務(wù)視圖,由于這些事務(wù)隨時可能訪問數(shù)據(jù)庫里面的任何數(shù)據(jù),所以這個事務(wù)提交前,數(shù)據(jù)庫里面它可能用到回滾記錄都必須保留,這就會導(dǎo)致大量占用存儲空間。
  • MySQL5.5及以前版本,回滾日志(undo log)是和“數(shù)據(jù)字典”一起放在ibdata文件里的,即使長事務(wù)最終提交,回滾段被清理,ibdata文件也不會變小。
  • 注意,長事務(wù)不是“長連接”。

MySQL事務(wù)的啟動方式

  • 顯示啟動,begin;strat transaction;(并不是立即開啟,而是執(zhí)行第一條操作InnoDB表的語句時才是真正開啟,提這點主要想說明它影響視圖的出現(xiàn)時機;如果想馬上啟動一個事務(wù),獲得一個read view,可以使用start transaction with consistent snap shot;),配套的提交語句commit,回滾語句rollback,commit work and chain提交事務(wù)并自動開啟下一個事務(wù)(節(jié)省再次執(zhí)行begin語句的開銷),此時一般是參數(shù)autocommit=1的情況。
  • 當(dāng)set autocommit=0的時候,線程不會自動提交。此時,若只執(zhí)行一個select語句,事務(wù)就已經(jīng)啟動了,并且不會自動提交。直到commit或rollback或斷開連接。

長事務(wù)查詢

  • 查詢持續(xù)時間超過60s的事務(wù):
    select * from information_schema.innodb_trx where TIME_TO_SET(timediff(now(),trx_started)) > 60;

事務(wù)更新時的版本控制邏輯

  • 更新數(shù)據(jù)都是先讀后寫,這個讀只能是當(dāng)前讀(current read),即讀當(dāng)前的值(不管當(dāng)前值得row trx_id是否大于本事務(wù),都能讀到);讀到后會將自己修改后的數(shù)據(jù)和自己的事務(wù)ID賦給新的“當(dāng)前值”。如果當(dāng)前記錄的行鎖被其他事務(wù)占用,就需要進入鎖等待狀態(tài)。
  • 除update語句外,select語句如果加鎖,也是當(dāng)前讀。有讀鎖(S鎖,共享鎖) lock in share mode;,寫鎖(X鎖,排它鎖)for update。

讀提交和可重復(fù)讀的主要區(qū)別

  • 在可重復(fù)讀隔離級別下,只需要在事務(wù)開始的時候創(chuàng)建一致性視圖,之后事務(wù)里的其他查詢都公用這個一致性視圖。
  • 在讀提交隔離級別下,每一個語句執(zhí)行前都會重新計算出一個新的視圖。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容