日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 运维知识 > 数据库 >内容正文

数据库

MySQL数据库:事务和ACID实现原理

發(fā)布時(shí)間:2024/9/30 数据库 38 豆豆
生活随笔 收集整理的這篇文章主要介紹了 MySQL数据库:事务和ACID实现原理 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

一、什么是事務(wù):

????????數(shù)據(jù)庫的事務(wù)是并發(fā)控制的基本單位,是指邏輯上的一組操作,要么全部執(zhí)行,要么全部不執(zhí)行。

1、事務(wù)的特性:

(1)原子性:事務(wù)是一個(gè)不可分割的工作單元,事務(wù)里的操作要么都成功,要么都失敗,如果事務(wù)執(zhí)行失敗,則需要進(jìn)行回滾。

(2)隔離性:事務(wù)的所操作的數(shù)據(jù)在提交之前,對(duì)其他事務(wù)的可見程度。

(3)持久性:一旦事務(wù)提交,它對(duì)數(shù)據(jù)庫中數(shù)據(jù)的改變就是永久的。

(4)一致性:事務(wù)不能破壞數(shù)據(jù)的完整性和業(yè)務(wù)的一致性。例如在轉(zhuǎn)賬時(shí),不管事務(wù)成功還是失敗,雙方錢的總額不變。

二、事務(wù)ACID特性的實(shí)現(xiàn)原理:

1、原子性:

????????原子性是通過MySQL的回滾日志undo log來實(shí)現(xiàn)的:當(dāng)事務(wù)對(duì)數(shù)據(jù)庫進(jìn)行修改時(shí),InnoDB會(huì)生成對(duì)應(yīng)的undo log;如果事務(wù)執(zhí)行失敗或調(diào)用了rollback,導(dǎo)致事務(wù)需要回滾,便可以利用undo log中的信息將數(shù)據(jù)回滾到修改之前的樣子。

undo log (回滾日志):是采用段(segment)的方式來記錄的,每個(gè)undo操作在記錄的時(shí)候占用一個(gè)undo log segment。在數(shù)據(jù)更改操作時(shí),記錄了相對(duì)應(yīng)的undo log的目的在于:

  • 保證數(shù)據(jù)的原子性,記錄事務(wù)發(fā)生之前的一個(gè)版本,用于回滾;
  • 通過mvcc?+?undo log實(shí)現(xiàn)Innodb事務(wù)可重復(fù)讀和讀已提交隔離級(jí)別。

2、隔離性:

隔離性是事務(wù)所操作的數(shù)據(jù)在提交之前,對(duì)其他事務(wù)的可見程度。

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

????????為保證在并發(fā)環(huán)境下讀取數(shù)據(jù)的完整性和一致性,數(shù)據(jù)庫提供了四種事務(wù)隔離級(jí)別,隔離級(jí)別越高,越能保證數(shù)據(jù)的完整性和一致性,但對(duì)高并發(fā)性能影響也越大,執(zhí)行效率越低。(四種隔離級(jí)別從上往下依次升高)

讀未提交:允許事務(wù)在執(zhí)行過程中,讀取其他事務(wù)尚未提交的數(shù)據(jù);

讀已提交:允許事務(wù)在執(zhí)行過程中讀取其他事務(wù)已經(jīng)提交的數(shù)據(jù);

可重復(fù)讀(默認(rèn)級(jí)別):在同一個(gè)事務(wù)內(nèi),任意時(shí)刻的查詢結(jié)果都是一致的;

讀序列化:所有事務(wù)逐個(gè)依次執(zhí)行,每次讀都需要獲取表級(jí)共享鎖,讀寫會(huì)相互阻塞。

2.2、如果不考慮事務(wù)的隔離性,在事務(wù)并發(fā)的環(huán)境下,可能存在問題有

(1)更新丟失:兩個(gè)或多個(gè)事務(wù)操作相同的數(shù)據(jù),然后基于選定的值更新該行時(shí),由于每個(gè)事務(wù)都不知道其他事務(wù)的存在,就會(huì)發(fā)生丟失更新問題:最后的更新覆蓋了其他事務(wù)所做的更新。

(2)臟讀:指事務(wù)A正在訪問數(shù)據(jù),并且對(duì)數(shù)據(jù)進(jìn)行了修改(事務(wù)未提交),這時(shí),事務(wù)B也使用這個(gè)數(shù)據(jù),后來事務(wù)A撤銷回滾,并把修改后的數(shù)據(jù)恢復(fù)原值,B讀到的數(shù)據(jù)就與數(shù)據(jù)庫中的數(shù)據(jù)不一致,即B讀到的數(shù)據(jù)是臟數(shù)據(jù)。

(3)不可重復(fù)讀:在一個(gè)事務(wù)內(nèi),多次讀取同一個(gè)數(shù)據(jù),但是由于另一個(gè)事務(wù)在此期間對(duì)這個(gè)數(shù)據(jù)做了修改并提交,導(dǎo)致前后讀取到的數(shù)據(jù)不一致;

(4)幻讀:在一個(gè)事務(wù)中,先后兩次進(jìn)行讀取相同的數(shù)據(jù)(一般是范圍查詢),但由于另一個(gè)事務(wù)新增或者刪除了數(shù)據(jù),導(dǎo)致前后兩次結(jié)果不一致。

① 不可重復(fù)讀和幻讀的區(qū)別:

不可重復(fù)讀側(cè)重于讀取到其他事務(wù)修改的數(shù)據(jù),幻讀側(cè)重于讀取到其他事務(wù)新增或者刪除的數(shù)據(jù)。

② 可以采用鎖機(jī)制來解決不可重復(fù)讀和幻讀:

對(duì)于不可重復(fù)讀,只需對(duì)操作的數(shù)據(jù)添加行級(jí)鎖,防止操作的數(shù)據(jù)發(fā)生變化;而對(duì)于幻讀,需要添加表級(jí)鎖,將整張表鎖定,防止新增或者刪除數(shù)據(jù)。

不同的事務(wù)隔離級(jí)別,在并發(fā)環(huán)境會(huì)存在不同的并發(fā)問題:

√:可能出現(xiàn)的情況?

×:不會(huì)出現(xiàn)該情況

臟讀

不可重復(fù)讀

幻讀

讀序列化

×

×

×

可重復(fù)讀

×

×

讀已提交

×

讀未提交

2.3、事務(wù)隔離性的實(shí)現(xiàn)原理:

????????為了實(shí)現(xiàn)事務(wù)隔離,數(shù)據(jù)庫延伸出了數(shù)據(jù)庫鎖,其中Innodb事務(wù)的隔離級(jí)別是由鎖機(jī)制和MVVC(多版本并發(fā)控制)實(shí)現(xiàn)的:

2.3.1、Mysql鎖機(jī)制:

????????MySQL鎖機(jī)制的基本工作原理就是:事務(wù)在修改數(shù)據(jù)庫之前,需要先獲得相應(yīng)的鎖,獲得鎖的事務(wù)才可以修改數(shù)據(jù);在該事務(wù)操作期間,這部分的數(shù)據(jù)是鎖定,其他事務(wù)如果需要修改數(shù)據(jù),需要等待當(dāng)前事務(wù)提交或回滾后釋放鎖。(鎖機(jī)制的更多說明可以參考另一篇博客:MySQL數(shù)據(jù)庫:鎖機(jī)制)

????????通過對(duì)InnoDB不同鎖類型的特性分析,可以利用鎖解決臟讀、不可重復(fù)讀、幻讀:

  • 排它鎖解決臟讀:在讀已提交的隔離級(jí)別下,事務(wù)A只有在對(duì)數(shù)據(jù)修改時(shí)才加排它鎖,但直到事務(wù)?commit 時(shí)才釋放鎖。因此,同時(shí)進(jìn)行的事務(wù)B希望讀取同一行數(shù)據(jù)時(shí),會(huì)被事務(wù)A的排它鎖堵塞,所以解決了臟讀的問題

  • 共享鎖解決不可重復(fù)讀:在可重復(fù)讀的隔離級(jí)別下,除了執(zhí)行讀已提交的排它鎖方式,還會(huì)在讀取一行數(shù)據(jù)時(shí),為這行數(shù)據(jù)添加共享鎖直至事務(wù) commit。例如,事務(wù)A讀取ID=1這一行數(shù)據(jù),然后為ID=1添加共享鎖,事務(wù)B同時(shí)希望update ID=1,此時(shí)獲取寫鎖失敗,因此在事務(wù)A執(zhí)行完之前,沒有其他任何事務(wù)可以對(duì)ID=1這一行做修改,因此解決了重復(fù)讀的問題

  • 臨鍵鎖解決幻讀

????????雖然共享鎖和排它鎖解決了事務(wù)隔離的并發(fā)問題,但鎖會(huì)導(dǎo)致大量的堵塞,性能下降。某些時(shí)候會(huì)造成死鎖,為了解決死鎖,還要添加死鎖探測(cè)機(jī)制,性能進(jìn)一步下降,因此需要更高效的方式實(shí)現(xiàn)事務(wù)的隔離級(jí)別,也就是 MVCC 多版本并發(fā)控制。

2.3.2、Multiversion concurrency control?(MVCC?多版本并發(fā)控制):

????????InnoDB 的?MVCC 的實(shí)現(xiàn)思路是:對(duì)每一行數(shù)據(jù)用 undo log 記錄多個(gè)版本,每個(gè)版本的數(shù)據(jù)可能都不相同,然后根據(jù) 事務(wù)ID 去尋找適合它的版本數(shù)據(jù),從而實(shí)現(xiàn)不同事務(wù)之間的隔離性。MVCC 具體實(shí)現(xiàn)實(shí)現(xiàn)方式是在每行記錄后面保存兩個(gè)隱藏的列:

  • DB_TRX_ID:標(biāo)識(shí)當(dāng)前數(shù)據(jù)屬于哪個(gè)事務(wù),每次提交事務(wù),事務(wù)ID會(huì)自增,事務(wù)開始時(shí)會(huì)把該事務(wù)ID放到當(dāng)前事務(wù)影響的行事務(wù)ID字段中,
  • DB_ROLL_PTR:指向該行數(shù)據(jù) undo log 的指針,undo log 日志文件保存了該行記錄的所有版本數(shù)據(jù),并在日志中通過鏈表形式組織

????????通過 MVCC,數(shù)據(jù)庫可以使得事務(wù)的讀取不需要很多讀鎖,提升了數(shù)據(jù)庫的性能。當(dāng)一個(gè)事務(wù)完成時(shí),數(shù)據(jù)庫會(huì)刪除關(guān)于這條事務(wù)所有的 undo log,若未完成事務(wù)數(shù)據(jù)庫崩潰則根據(jù) undo log回滾,實(shí)現(xiàn)原子性。

????????MVCC只在 可重復(fù)度 和 讀已提交 兩個(gè)隔離級(jí)別下才會(huì)工作,其中,MVCC實(shí)質(zhì)就是通過保存數(shù)據(jù)在某個(gè)時(shí)間點(diǎn)的快照來實(shí)現(xiàn)的。?

  • 讀已提交:事務(wù)A執(zhí)行每一條語句時(shí),生成一份活躍事務(wù)表,根據(jù)這份表去獲取數(shù)據(jù),因此不會(huì)獲取到臟數(shù)據(jù)(未提交事務(wù)引起的),但會(huì)有重復(fù)讀問題(因?yàn)榭梢宰x取到commit 的事務(wù)數(shù)據(jù))
  • 可重復(fù)讀:事務(wù)A只有在事務(wù)開始時(shí)才生成一份活躍事務(wù)表,因此不會(huì)讀取到事務(wù)A執(zhí)行中 commit 的其它事務(wù)引起的數(shù)據(jù)變更,也就不存在重復(fù)讀問題。

????????在并發(fā)訪問數(shù)據(jù)庫時(shí),對(duì)正在事務(wù)中的數(shù)據(jù)做MVCC多版本的管理,以避免寫操作阻塞讀操作,并且可以通過比較版本解決快照讀方式的幻讀問題,但對(duì)于當(dāng)前讀的幻讀,MVCC并不能解決,需要通過臨鍵鎖來解決。

  • 快照讀:Innodb快照讀,數(shù)據(jù)的讀取將由 cache(原本數(shù)據(jù)) + undo(事務(wù)修改前的數(shù)據(jù)) 兩部分組成
  • 當(dāng)前讀:SQL讀取的數(shù)據(jù)是最新版本。通過鎖機(jī)制來保證讀取的數(shù)據(jù)無法通過其他事務(wù)進(jìn)行修改

在可重復(fù)讀的隔離級(jí)別下,MVCC具體操作:

(1)SELECT操作:InnoDB遵循以下兩個(gè)規(guī)則:

  • 只查找數(shù)據(jù)行的事務(wù)ID小于或等于當(dāng)前事務(wù)ID的版本,這樣可以確保事務(wù)讀取的行,要么是在事務(wù)開始前已經(jīng)存在的,要么是事務(wù)自身插入或者修改過的記錄。
  • 行的刪除版本要未被定義,讀取到事務(wù)開始之前狀態(tài)的版本,這可以確保事務(wù)讀取到的行,在事務(wù)開始之前未被刪除。只有同時(shí)滿足的兩者的記錄,才能返回作為查詢結(jié)果。

(2)INSERT:InnoDB為新插入的每一行保存當(dāng)前事務(wù)編號(hào)作為行版本號(hào)。

(3)DELETE:InnoDB為刪除的每一行保存當(dāng)前事務(wù)編號(hào)作為行刪除標(biāo)識(shí)。

(4)UPDATE:InnoDB為插入一行新記錄,保存當(dāng)前事務(wù)編號(hào)作為行版本號(hào),同時(shí)保存當(dāng)前事務(wù)編號(hào)到原來的行作為行刪除標(biāo)識(shí)。

保存這兩個(gè)額外系統(tǒng)版本號(hào),使大多數(shù)讀操作都可以不用加鎖。這樣設(shè)計(jì)使得讀數(shù)據(jù)操作很簡(jiǎn)單,性能很好,并且也能保證只會(huì)讀取到符合標(biāo)準(zhǔn)的行,不足之處是每行記錄都需要額外的存儲(chǔ)空間,需要做更多的行檢查工作,以及一些額外的維護(hù)工作。

3、持久性:

????????持久性的實(shí)現(xiàn)關(guān)鍵在于redo log日志,在執(zhí)行SQL時(shí)會(huì)保存已執(zhí)行的SQL語句到一個(gè)指定的Log文件,當(dāng)執(zhí)行recovery時(shí)重新執(zhí)行redo log記錄的SQL操作。

3.1、redo log日志:

????????當(dāng)向數(shù)據(jù)庫寫入數(shù)據(jù)時(shí),執(zhí)行過程會(huì)首先寫入Buffer Pool,Buffer Pool中修改的數(shù)據(jù)會(huì)定期刷新到磁盤中(這一過程叫做刷盤),這整一過程稱為redo log。redo log 分為:

  • Buffer Pool內(nèi)存中的日志緩沖(redo log buffer),該部分日志是易失性的;
  • 磁盤上的重做日志文件(redo log file),該部分日志是持久的。

????????Buffer Pool的使用可以大大提高了讀寫數(shù)據(jù)的效率,但是也帶了新的問題:如果MySQL宕機(jī),而此時(shí)Buffer Pool中修改的數(shù)據(jù)在內(nèi)存還沒有刷新到磁盤,就會(huì)導(dǎo)致數(shù)據(jù)的丟失,事務(wù)的持久性無法保證。

????????為了確保事務(wù)的持久性,在當(dāng)事務(wù)提交時(shí),會(huì)調(diào)用fsync接口對(duì)redo log進(jìn)行刷盤, (即redo log buffer寫日志到磁盤的redo log file中 ),刷新頻率由 innodb_flush_log_at_trx_commit變量來控制的:

  • 0:?每秒刷新緩沖池中的數(shù)據(jù)寫入到磁盤中的,當(dāng)系統(tǒng)崩潰,會(huì)丟失1秒鐘的數(shù)據(jù) ;
  • 1: 事務(wù)每次提交的時(shí)候,就把緩沖池中的數(shù)據(jù)刷新到磁盤中;
  • 2:提交事務(wù)的時(shí)候,把緩沖池中的數(shù)據(jù)寫入磁盤文件對(duì)應(yīng)的 os cache 緩存里去,而不是直接進(jìn)入磁盤文件。可能 1 秒后才會(huì)把 os cache 里的數(shù)據(jù)寫入到磁盤文件里去。

4、一致性:

一致性指的是事務(wù)不能破壞數(shù)據(jù)的完整性和業(yè)務(wù)的一致性 :

  • 數(shù)據(jù)的完整性: 實(shí)體完整性、列完整性(如字段的類型、大小、長(zhǎng)度要符合要求)、外鍵約束等

  • 業(yè)務(wù)的一致性:例如在銀行轉(zhuǎn)賬時(shí),不管事務(wù)成功還是失敗,雙方錢的總額不變。

那是如何保證數(shù)據(jù)一致性的?其實(shí)數(shù)據(jù)一致性是通過事務(wù)的原子性、持久性和隔離性來保證的:

  • 原子性:語句要么全執(zhí)行,要么全不執(zhí)行,是事務(wù)最核心的特性,事務(wù)本身就是以原子性來定義的;主要基于undo log實(shí)現(xiàn)
  • 持久性:保證事務(wù)提交后不會(huì)因?yàn)殄礄C(jī)等原因?qū)е聰?shù)據(jù)丟失;主要基于redo log實(shí)現(xiàn)
  • 隔離性:保證事務(wù)執(zhí)行盡可能不受其他事務(wù)影響;InnoDB默認(rèn)的隔離級(jí)別是RR,RR的實(shí)現(xiàn)主要基于鎖機(jī)制(包含next-key lock)、MVCC(包括數(shù)據(jù)的隱藏列、基于undo log的版本鏈、ReadView)

總結(jié)

以上是生活随笔為你收集整理的MySQL数据库:事务和ACID实现原理的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。