MySQL 中的事務(wù)?

          對(duì) MySQL 來(lái)說(shuō),事務(wù)通常是一組包含對(duì)數(shù)據(jù)庫(kù)操作的集合。在執(zhí)行時(shí),只有在該組內(nèi)的事務(wù)都執(zhí)行成功,這個(gè)事務(wù)才算執(zhí)行成功,否則就算失敗。MySQL
          中,事務(wù)支持是在引擎層實(shí)現(xiàn)的,像 MySQL 原生的 MyISAM 引擎就不支持事務(wù),這也是被 InooDB 取代的重要原因。

          為什么要有事務(wù)呢,舉個(gè)例子來(lái)說(shuō),你的賬戶(hù)有 100 元,現(xiàn)在想給朋友轉(zhuǎn)賬 100 元。其中就會(huì)包含兩個(gè)很重要的操作,你的賬戶(hù)減 100 元,朋友賬戶(hù)多
          100 元。由于轉(zhuǎn)賬過(guò)程中出現(xiàn)失敗是很常見(jiàn)的,假設(shè)操作不包含在事務(wù)內(nèi),你的賬戶(hù)減錢(qián)操作成功,朋友賬戶(hù)加錢(qián)操作失敗。就會(huì)出現(xiàn),你的賬戶(hù)扣錢(qián),對(duì)方?jīng)]有收到錢(qián)的情況。

          再比如,在發(fā)起轉(zhuǎn)賬操作時(shí),由于系統(tǒng)需要進(jìn)行像查詢(xún)余額,計(jì)算,更新余額的操作,如果在等待時(shí)間內(nèi),又發(fā)起了轉(zhuǎn)賬操作,但目前更新余額的操作還沒(méi)有成功,就會(huì)出現(xiàn)你的
          100 元,可以給別人轉(zhuǎn)賬多次的情況,這對(duì)于銀行來(lái)說(shuō)是肯定不允許的。

          對(duì)于一個(gè)事務(wù)來(lái)說(shuō)通常要滿(mǎn)足四個(gè)特性,也就是通常所說(shuō)的 ACID:

          *
          Atomicity - 保證在一個(gè)工作單元(就是一組操作)中所有的操作都執(zhí)行成功,否則的話(huà)當(dāng)前這個(gè)事務(wù)就會(huì)失敗,之前的操作都被會(huì)回滾。

          *
          Consistency - 保證一個(gè)事務(wù)被成功提交后,數(shù)據(jù)庫(kù)的狀態(tài)是從一致性狀態(tài)變成另一個(gè)一致性狀態(tài)。

          *
          Isolation - 保證每個(gè)事務(wù)中的操作時(shí)是獨(dú)立的,對(duì)于其他事務(wù)沒(méi)有影響。

          *
          Durability - 對(duì)于已經(jīng)提交的事務(wù),即使在數(shù)據(jù)庫(kù)損壞的情況下,也不會(huì)造成數(shù)據(jù)的丟失和損壞。

          MySQL 中的事務(wù)隔離是如何實(shí)現(xiàn)的?

          當(dāng)數(shù)據(jù)庫(kù)中有多個(gè)事務(wù)同時(shí)執(zhí)行時(shí),就可能會(huì)出現(xiàn)臟讀,幻讀,不可重復(fù)讀的問(wèn)題,為了解決這些問(wèn)題,就出現(xiàn)了"隔離級(jí)別"的概念。

          事務(wù)隔離的問(wèn)題:

          臟讀:事務(wù) A 中訪(fǎng)問(wèn)了事務(wù) B 中未提交的數(shù)據(jù)。這里 Transaction 1 讀到了,Transaction 2 中未提交的年齡數(shù)據(jù)。



          不可重復(fù)讀:事務(wù) A 中多次查詢(xún)同一數(shù)據(jù),但由于事務(wù) B 在事務(wù) A 兩次查詢(xún)中,修改了改數(shù)據(jù)的值,導(dǎo)致兩次查詢(xún)的結(jié)果不一樣。下面 Transaction
          1 中的兩次查詢(xún)查詢(xún)結(jié)果是一致的,第二次讀到的 age 已經(jīng)被修改的內(nèi)容。



          幻讀:通常發(fā)生在事務(wù) B 對(duì)事務(wù) A 正在讀取的內(nèi)容,添加或刪除了一條數(shù)據(jù)。造成數(shù)據(jù)莫名出現(xiàn)或者消失的情況。這里 Transaction 1
          中,兩次查詢(xún)的結(jié)果并不一致,第二次查詢(xún)會(huì)多出一條記錄。



          事務(wù)的隔離級(jí)別:

          隔離級(jí)別 解釋 可能出現(xiàn)的問(wèn)題
          讀未提交 讀未提交是指,一個(gè)事務(wù)還沒(méi)提交時(shí),它做的變更就能被別的事務(wù)看到。 臟讀,不可重復(fù)讀,幻讀
          讀提交 讀提交是指,一個(gè)事務(wù)提交之后,它做的變更才會(huì)被其他事務(wù)看到。 不可重復(fù)讀,幻讀
          可重復(fù)讀 可重復(fù)讀是指,一個(gè)事務(wù)執(zhí)行過(guò)程中看到的數(shù)據(jù),總是跟這個(gè)事務(wù)在啟動(dòng)時(shí)看到的數(shù)據(jù)是一致的。當(dāng)然在可重復(fù)讀隔離級(jí)別下,未提交變更對(duì)其他事務(wù)也是不可見(jiàn)的。
          幻讀
          串行化 串行化,顧名思義是對(duì)于同一行記錄,“寫(xiě)”會(huì)加“寫(xiě)鎖”,“讀”會(huì)加“讀鎖”。當(dāng)出現(xiàn)讀寫(xiě)鎖沖突的時(shí)候,后訪(fǎng)問(wèn)的事務(wù)必須等前一個(gè)事務(wù)執(zhí)行完成,才能繼續(xù)執(zhí)行。
          無(wú)
          在 MySQL 中 RR 級(jí)別引入了間隙鎖,解決了幻讀的問(wèn)題。

          舉一個(gè)實(shí)際的例子,來(lái)看一下這四種隔離級(jí)別對(duì)應(yīng)的結(jié)果,假設(shè)表結(jié)構(gòu)如下:
          mysql> create table T(c int) engine=InnoDB; insert into T(c) values(1);
          此時(shí)發(fā)生的事務(wù)如下:


          隔離級(jí)別 返回結(jié)果
          讀未提交 V1 是 2。事務(wù) B 雖然還沒(méi)有提交,但是結(jié)果已經(jīng)被 A 看到了。因此,V2、V3 也都是 2
          讀提交 V1 是 1,V2 的值是 2。事務(wù) B 的更新在提交后才能被 A 看到。所以, V3 的值也是 2。
          可重復(fù)讀 則 V1、V2 是 1,V3 是 2。 V2 還是 1 的原因,需要遵循:事務(wù)在執(zhí)行期間看到的數(shù)據(jù)前后必須是一致的。
          串行化 事務(wù) B 執(zhí)行“將 1 改成 2”時(shí),會(huì)被鎖住。直到事務(wù) A 提交后,事務(wù) B 才可以繼續(xù)執(zhí)行。所以從 A 的角度看, V1、V2 值是 1,V3
          的值是 2。
          執(zhí)行的效率會(huì)和執(zhí)行的級(jí)別有關(guān),隔離的越高,效率越低,需要在二者間尋找平衡。

          事務(wù)隔離的應(yīng)用場(chǎng)景?

          讀未提交:

          * 這個(gè)基本沒(méi)人會(huì)選擇,連事務(wù)都構(gòu)不成。
          讀提交:

          * 一般互聯(lián)網(wǎng)公司的隔離級(jí)別會(huì)選用這個(gè),原因在:
          * RR 級(jí)別下,存在間歇鎖,出現(xiàn)死鎖的幾率比 RC 大的多。
          * RR 級(jí)別下,條件列未命中索引會(huì)鎖表。在 RC 級(jí)別下會(huì)鎖行。
          * RC 級(jí)別下,半一致性特性增加了 update 操作的并發(fā)性。
          可重復(fù)讀:

          *
          數(shù)據(jù)校驗(yàn):假設(shè)你在管理一個(gè)個(gè)人銀行賬戶(hù)表。一個(gè)表存了每個(gè)月月底的余額,一個(gè)表存了賬單明細(xì)。這時(shí)候你要做數(shù)據(jù)校對(duì),也就是判斷上個(gè)月的余額和當(dāng)前余額的差額,是否與本月的賬單明細(xì)一致。你一定希望在校對(duì)過(guò)程中,即使有用戶(hù)發(fā)生了一筆新的交易,也不影響你的校對(duì)結(jié)果。這時(shí)使用“可重復(fù)讀”隔離級(jí)別就很方便。事務(wù)啟動(dòng)時(shí)的視圖可以認(rèn)為是靜態(tài)的,不受其他事務(wù)更新的影響。
          串行化:

          * 每次讀操作都會(huì)加鎖,性能不佳。
          事務(wù)隔離的實(shí)現(xiàn)?

          在實(shí)現(xiàn)上,數(shù)據(jù)庫(kù)里面會(huì)創(chuàng)建一個(gè)視圖,訪(fǎng)問(wèn)的時(shí)候以視圖的邏輯結(jié)果為準(zhǔn)。

          * 在可重復(fù)讀時(shí)的隔離級(jí)別下,視圖是在事務(wù)啟動(dòng)時(shí)創(chuàng)建的,整個(gè)事務(wù)存在期間都會(huì)用這個(gè)視圖。
          * 在讀提交的隔離級(jí)別下,這個(gè)視圖是在每個(gè) SQL 語(yǔ)句開(kāi)始執(zhí)行時(shí)創(chuàng)建的。
          * 在讀未提交的隔離級(jí)別下,會(huì)直接返回記錄的最新值,沒(méi)有視圖的概念。
          * 串行化是直接通過(guò)加鎖的方式來(lái)避免并行訪(fǎng)問(wèn)。
          注意這個(gè)視圖不是用于查詢(xún)定義的虛擬表,而是在 InnoDB 中實(shí)現(xiàn) MVCC 用到的一致性讀視圖(consistent read view),用于支持 RC
          和 RR 隔離級(jí)別的實(shí)現(xiàn)。

          可重復(fù)讀的具體實(shí)現(xiàn):

          在 MySQL 中,實(shí)際上每條記錄在更新時(shí)都會(huì)同時(shí)記錄一條回滾操作。記錄上的最新值,通過(guò)回滾都可以得到前一個(gè)狀態(tài)的值。比如一個(gè)值 從 1 按照順序,被修改成
          2、3、4 ,就會(huì)在回滾日志中有如下的記錄。



          當(dāng)前最新是 4,在查詢(xún)這條記錄時(shí),不同時(shí)刻啟動(dòng)的事務(wù)會(huì)有不同的 read-view. 在視圖 A B C 中,記錄值為 1, 2, 4.
          同一條記錄可以存在多個(gè)版本,這就是數(shù)據(jù)庫(kù)多版本并發(fā)控制(MVCC)。對(duì)于 read-view A 來(lái)說(shuō),要得到
          1,就必須將當(dāng)前值依次執(zhí)行圖中所有的回滾操作得到。假如,有另外一個(gè)事務(wù)將 4 改成 5,但對(duì)于視圖 A B C 來(lái)說(shuō),事務(wù)是不沖突的。

          回滾段的刪除,為什么要避免使用長(zhǎng)事務(wù)?


          既然每一條記錄都會(huì)更新是都會(huì)產(chǎn)生一條回滾操作記錄,時(shí)間一長(zhǎng),肯定會(huì)占用大量的存儲(chǔ)空間。那么系統(tǒng)會(huì)在什么時(shí)候刪除這些回滾日志呢,就是在當(dāng)前系統(tǒng)里不存在比該回滾日志更早的
          read-view 時(shí)。


          但如果系統(tǒng)里存在著很老的事務(wù)視圖。由于這些事務(wù)可能會(huì)訪(fǎng)問(wèn)數(shù)據(jù)庫(kù)里的任何數(shù)據(jù),所以在事務(wù)提交之前,所有可能用到的回滾記錄都必須保留,這就可能出現(xiàn)占用大量存儲(chǔ)空間的情況。

          在 MySQL 5.5 之前,回滾日志和數(shù)據(jù)字典一起放在 ibdata 文件里,即使長(zhǎng)事務(wù)被提交,回滾段被清理,文件也不會(huì)變小。

          并且長(zhǎng)事務(wù)還占用鎖資源,也可能拖垮整個(gè)庫(kù)。

          MySQL 中事務(wù)的啟動(dòng)方式?

          顯式啟動(dòng)事務(wù):
          # 使用 START TRANSACTION 或者 BEGIN 開(kāi)啟事務(wù): START TRANSACTION
          [transaction_characteristic [, transaction_characteristic] ...]
          transaction_characteristic: { WITH CONSISTENT SNAPSHOT | READ WRITE | READ ONLY
          } BEGIN [WORK] # 使用 COMMIT 來(lái)提交事務(wù) COMMIT [WORK] [AND [NO] CHAIN] [[NO] RELEASE]
          # 使用 ROLLBACK 來(lái)回滾事務(wù) ROLLBACK [WORK] [AND [NO] CHAIN] [[NO] RELEASE]
          隱式啟動(dòng)事務(wù):
          # 設(shè)置當(dāng)前事務(wù)的是否自動(dòng)提交 SET autocommit = {0 | 1}
          autocommit 的討論:

          * 當(dāng) autocommit=0 時(shí),事務(wù)啟動(dòng)后,不會(huì)自動(dòng)關(guān)閉直到主動(dòng)的輸入 COMMIT 或者 ROLLBACK 語(yǔ)句,或者斷開(kāi)連接時(shí),當(dāng)前事務(wù)才結(jié)束。
          * 一些客戶(hù)端連接框架會(huì)默認(rèn)連接成功后先執(zhí)行一個(gè) set autocommit=0
          的命令。這就導(dǎo)致接下來(lái)的查詢(xún)都在事務(wù)中,如果是長(zhǎng)連接,就導(dǎo)致了意外的長(zhǎng)事務(wù)。
          * 當(dāng) autocommit=1 時(shí),建議總是以這種方式啟動(dòng)事務(wù)。
          * 如果擔(dān)心多一次交互的問(wèn)題,可以使用 commit work and chain 語(yǔ)法。
          多一次交互的問(wèn)題,如果采用 autocommit=0 的這種方式,不需要每次輸入 begin ,減少了語(yǔ)句的交互次數(shù)。

          查詢(xún)長(zhǎng)事務(wù):
          select * from information_schema.innodb_trx where
          TIME_TO_SEC(timediff(now(),trx_started))>60
          避免長(zhǎng)事務(wù)的方案:

          * 在開(kāi)發(fā)過(guò)程中,減少事務(wù)范圍,少用長(zhǎng)事務(wù)。如果無(wú)法避免,保證邏輯日志空間夠用,并且支持動(dòng)態(tài)日志空間的增長(zhǎng)。
          * 監(jiān)控Innodb_trx表,發(fā)現(xiàn)長(zhǎng)事務(wù)報(bào)警。
          總結(jié)

          在開(kāi)始部分,介紹了 MySQL 中事務(wù)的概念,并回顧了事務(wù)的 ACID
          的特性。接著探討了事務(wù)隔離的可能出現(xiàn)的臟讀,不可重復(fù)讀以及幻讀的問(wèn)題,并給出了相應(yīng)的解決方案-隔離級(jí)別。并分析了常見(jiàn)事務(wù)隔離的應(yīng)用場(chǎng)景以及事務(wù)隔離的實(shí)現(xiàn)方式。

          并在最后引出了回滾段的概念,以及為什么要避免使用長(zhǎng)事務(wù)。并給出了開(kāi)啟事務(wù)的方法。

          參考

          友情鏈接
          ioDraw流程圖
          API參考文檔
          OK工具箱
          云服務(wù)器優(yōu)惠
          阿里云優(yōu)惠券
          騰訊云優(yōu)惠券
          京東云優(yōu)惠券
          站點(diǎn)信息
          問(wèn)題反饋
          郵箱:[email protected]
          QQ群:637538335
          關(guān)注微信

                日韩特级毛片 | 久草在线视频福利资源站 | 主人调教女m乳夹扇耳光网站 | 亚洲在线免费视频 | 豆花视频在线免费观看 |