Mysql事務原理四大特性

一届氢、什么是事務

事務:是數(shù)據(jù)庫操作的最小工作單元,是作為單個邏輯工作單元執(zhí)行的一系列操作辰斋;這些操作作為一個整體一起向系統(tǒng)提交,要么都執(zhí)行瘸味、要么都不執(zhí)行宫仗;事務是一組不可再分割的操作集合(工作邏輯單元);

事務的四大特性:

  • 原子性(Atomicity):事務是數(shù)據(jù)庫的邏輯工作單位旁仿,事務中包含的各操作要么都做藕夫,要么都不做
  • 一致性(Consistency):事務開始前和結束后,數(shù)據(jù)庫的完整性約束沒有被破壞 枯冈。比如A向B轉賬毅贮,不可能A扣了錢,B卻沒收到尘奏。
  • 隔離型(Isolation):一個事務的執(zhí)行不能被其它事務干擾滩褥。即一個事務內部的操作及使用的數(shù)據(jù)對其它并發(fā)事務是隔離的,并發(fā)執(zhí)行的各個事務之間不能互相干擾罪既。
  • 持久性(Durability):指一個事務一旦提交铸题,它對數(shù)據(jù)庫中的數(shù)據(jù)的改變就應該是永久性的。接下來的其它操作或故障不應該對其執(zhí)行結果有任何影響琢感。

個人認為這四大特性總結起來就是兩種:

可靠性:原子性丢间、一致性、持久性可以歸納為可靠性驹针『娲欤可靠就是要保證數(shù)據(jù)的一致與不丟失。數(shù)據(jù)庫要保證數(shù)據(jù)的一致,就要處理commit與rollBack饮六;顯然處理commit指令的時候需要記錄要提交哪些數(shù)據(jù)其垄,rollback的時候需要知道回退的原數(shù)據(jù)。mysql中commit需要redo log卤橄,rollBack 對應undo log

并發(fā)控制(隔離性):當多個并發(fā)請求過來绿满,并且其中有一個請求是對數(shù)據(jù)修改操作的時候會有影響,為了避免讀到臟數(shù)據(jù)窟扑,所以需要對事務之間的讀寫進行隔離喇颁,至于隔離到啥程度得看業(yè)務系統(tǒng)的場景了,實現(xiàn)這個就得用MySQL 的隔離級別嚎货。

二橘霎、redo log 與undo log

1、redo log

redo log和undo log都屬于InnoDB的事務日志殖属。redo log 主要實現(xiàn)數(shù)據(jù)的持久化

InnoDB作為MySQL的存儲引擎姐叁,數(shù)據(jù)是存放在磁盤中的,但如果每次讀寫數(shù)據(jù)都需要磁盤IO洗显,效率會很低外潜。為此,InnoDB提供了緩存(Buffer Pool)墙懂,Buffer Pool中包含了磁盤中部分數(shù)據(jù)頁的映射橡卤,作為訪問數(shù)據(jù)庫的緩沖:當從數(shù)據(jù)庫讀取數(shù)據(jù)時,會首先從Buffer Pool中讀取损搬,如果Buffer Pool中沒有碧库,則從磁盤讀取后放入Buffer Pool;當向數(shù)據(jù)庫寫入數(shù)據(jù)時巧勤,會首先寫入Buffer Pool嵌灰,Buffer Pool中修改的數(shù)據(jù)會定期刷新到磁盤中(這一過程稱為刷臟)。Buffer Pool的使用大大提高了讀寫數(shù)據(jù)的效率颅悉,但是也帶了新的問題:如果MySQL宕機沽瞭,而此時Buffer Pool中修改的數(shù)據(jù)還沒有刷新到磁盤,就會導致數(shù)據(jù)的丟失剩瓶,事務的持久性無法保證驹溃。

如上圖所示mysql采用redo log來處理該問題:當數(shù)據(jù)修改時,除了修改Buffer Pool中的數(shù)據(jù)延曙,還會在redo log Buffer 中記錄這次操作豌鹤;當事務提交時,會調用fsync接口對redo log進行刷盤枝缔。如果MySQL宕機布疙,重啟時可以讀取redo log中的數(shù)據(jù),對數(shù)據(jù)庫進行恢復。redo log采用的是WAL(Write-ahead logging灵临,預寫式日志)截型,所有修改先寫入日志,再更新到Buffer Pool儒溉,保證了數(shù)據(jù)不會因MySQL宕機而丟失宦焦,從而滿足了持久性要求。

MySQL支持用戶自定義在commit時如何將log buffer中的日志刷log file中睁搭。這種控制通過變量 innodb_flush_log_at_trx_commit 的值來決定赶诊。該變量有3種值:0笼平、1园骆、2,默認為1寓调。但注意锌唾,這個變量只是控制commit動作是否刷新log buffer到磁盤。

  • 當設置為1的時候夺英,事務每次提交都會將log buffer中的日志寫入os buffer并調用fsync()刷到log file on disk中晌涕。這種方式即使系統(tǒng)崩潰也不會丟失任何數(shù)據(jù),但是因為每次提交都寫入磁盤痛悯,IO的性能較差余黎。
  • 當設置為0的時候,事務提交時不會將log buffer中日志寫入到os buffer载萌,而是每秒寫入os buffer并調用fsync()寫入到log file on disk中惧财。也就是說設置為0時是(大約)每秒刷新寫入到磁盤中的,當系統(tǒng)崩潰扭仁,會丟失1秒鐘的數(shù)據(jù)垮衷。
  • 當設置為2的時候,每次提交都僅寫入到os buffer乖坠,然后是每秒調用fsync()將os buffer中的日志寫入到log file on disk搀突。

既然redo log也需要在事務提交時將日志寫入磁盤,為什么它比直接將Buffer Pool中修改的數(shù)據(jù)寫入磁盤(即刷臟)要快呢熊泵?主要有以下兩方面的原因:

  • 刷臟是隨機IO仰迁,因為每次修改的數(shù)據(jù)位置隨機,但寫redo log是追加操作顽分,屬于順序IO徐许。
  • 刷臟是以數(shù)據(jù)頁(Page)為單位的,MySQL默認頁大小是16KB怯邪,一個Page上一個小修改都要整頁寫入绊寻;而redo log中只包含真正需要寫入的部分,無效IO大大減少。

2澄步、undo log

undo log 的寫入時機與redo log一致冰蘑。

InnoDB實現(xiàn)回滾,靠的是undo log:當事務對數(shù)據(jù)庫進行修改時村缸,InnoDB會生成對應的undo log祠肥;如果事務執(zhí)行失敗或調用了rollback,導致事務需要回滾梯皿,便可以利用undo log中的信息將數(shù)據(jù)回滾到修改之前的樣子仇箱。

undo log屬于邏輯日志,它記錄的是sql執(zhí)行相關的信息东羹。當發(fā)生回滾時剂桥,InnoDB會根據(jù)undo log的內容做與之前相反的工作:對于每個insert,回滾時會執(zhí)行delete属提;對于每個delete权逗,回滾時會執(zhí)行insert;對于每個update冤议,回滾時會執(zhí)行一個相反的update斟薇,把數(shù)據(jù)改回去。以update操作為例:當事務執(zhí)行update時恕酸,其生成的undo log中會包含被修改行的主鍵(以便知道修改了哪些行)堪滨、修改了哪些列、這些列在修改前后的值等信息蕊温,回滾時便可以使用這些信息將數(shù)據(jù)還原到update之前的狀態(tài)袱箱。

三、Mysql的鎖機制

當數(shù)據(jù)庫有并發(fā)事務的時候寿弱,可能會產(chǎn)生數(shù)據(jù)的不一致犯眠,這時候需要一些機制來保證訪問的次序,mysql的鎖機制可以達到該目的

1. 按照鎖的粒度分數(shù)據(jù)庫鎖有哪些症革?鎖機制與InnoDB鎖算法

在關系型數(shù)據(jù)庫中筐咧,可以按照鎖的粒度把數(shù)據(jù)庫鎖分為行級鎖(INNODB引擎)、表級鎖(MYISAM引擎)和頁級鎖(BDB引擎 )噪矛。

MyISAM和InnoDB存儲引擎使用的鎖:

  • MyISAM采用表級鎖(table-level locking)量蕊。
  • InnoDB支持行級鎖(row-level locking)和表級鎖,默認為行級鎖

行級鎖:行級鎖是Mysql中鎖定粒度最細的一種鎖艇挨,表示只針對當前操作的行進行加鎖残炮。行級鎖能大大減少數(shù)據(jù)庫操作的沖突。其加鎖粒度最小缩滨,但加鎖的開銷也最大势就。行級鎖分為共享鎖 和 排他鎖泉瞻。

特點:開銷大,加鎖慢苞冯;會出現(xiàn)死鎖袖牙;鎖定粒度最小,發(fā)生鎖沖突的概率最低舅锄,并發(fā)度也最高鞭达。

表級鎖:表級鎖是MySQL中鎖定粒度最大的一種鎖,表示對當前操作的整張表加鎖皇忿,它實現(xiàn)簡單畴蹭,資源消耗較少,被大部分MySQL引擎支持鳍烁。最常使用的MYISAM與INNODB都支持表級鎖定叨襟。表級鎖定分為表共享讀鎖(共享鎖)與表獨占寫鎖(排他鎖)。

特點:開銷小老翘,加鎖快芹啥;不會出現(xiàn)死鎖;鎖定粒度大铺峭,發(fā)出鎖沖突的概率最高,并發(fā)度最低汽纠。

頁級鎖 :頁級鎖是MySQL中鎖定粒度介于行級鎖和表級鎖中間的一種鎖卫键。表級鎖速度快,但沖突多虱朵,行級沖突少莉炉,但速度慢。所以取了折衷的頁級碴犬,一次鎖定相鄰的一組記錄絮宁。

特點:開銷和加鎖時間界于表鎖和行鎖之間;會出現(xiàn)死鎖服协;鎖定粒度界于表鎖和行鎖之間绍昂,并發(fā)度一般

從鎖的類別上來講,有共享鎖和排他鎖偿荷。

  • 共享鎖(S鎖): 又叫做讀鎖窘游。當用戶要進行數(shù)據(jù)的讀取時,對數(shù)據(jù)加上共享鎖跳纳。共享鎖可以同時加上多個忍饰。事務T對數(shù)據(jù)對象A加上共享鎖,則事務T可以讀A但不能修改A寺庄,其他事務只能再對A加共享鎖艾蓝,而不能加排他鎖力崇,直到T釋放A上的共享鎖。這保證了其他事務可以讀A赢织,但在T釋放A上的共享鎖之前不能對A做任何修改餐曹。
  • 排他鎖(X鎖): 又叫做寫鎖。當用戶要進行數(shù)據(jù)的寫入時敌厘,對數(shù)據(jù)加上排他鎖台猴。排他鎖只可以加一個。若事務T對數(shù)據(jù)對象A加上排他鎖俱两,事務T可以讀A也可以修改A饱狂,其他事務不能再對A加任何鎖,直到T釋放A上的鎖宪彩。這保證了其他事務在T釋放A上的排他鎖之前不能再讀取和修改A休讳。

2、InnoDB鎖的特性

由于 MySQL 的Innodb引擎的行鎖是針對索引加的鎖,不是針對記錄加的鎖,所以雖然是訪問不同行的記錄,但是如果是使用相同的索引鍵,是會出現(xiàn)鎖沖突的尿孔。

在不通過索引條件查詢的時候俊柔,InnoDB使用的是表鎖!

當表有多個索引的時候,不同的事務可以使用不同的索引鎖定不同的行,另外,不論 是使用主鍵索引活合、唯一索引或普通索引,InnoDB 都會使用行鎖來對數(shù)據(jù)加鎖雏婶。
即便在條件中使用了索引字段,但是否使用索引來檢索數(shù)據(jù)是由 MySQL 通過判斷不同 執(zhí)行計劃的代價來決定的,如果 MySQL 認為全表掃 效率更高,比如對一些很小的表,它 就不會使用索引,這種情況下 InnoDB 將使用表鎖,而不是行鎖。因此,在分析鎖沖突時, 別忘了檢查 SQL 的執(zhí)行計劃(explain查看),以確認是否真正使用了索引白指。

三留晚、Mysql的隔離機制

Read uncommitted 讀未提交:READ UNCOMMITTED級別忽略其它事務放置的鎖。使用READ UNCOMMITTED級別運行的事務告嘲,能夠讀取尚未由其它事務提交的改動后的數(shù)據(jù)值错维,這些行為稱為“臟”讀。我們所說的臟讀橄唬,兩個并發(fā)的事務赋焕,事務A可以讀取到事務B未提交的數(shù)據(jù)。假設事務A回滾仰楚,事務B就讀取了一行沒有提交的數(shù)據(jù)隆判。這種數(shù)據(jù)我們覺得是不存在的。

Read committed 讀提交:一個事務只能讀取另一個事務已經(jīng)提交的修改缸血。其避免了臟讀蜜氨,但仍然存在不可重復讀和幻讀問題。大多數(shù)數(shù)據(jù)庫的默認級別就是Read committed捎泻。比方Sql Server , Oracle飒炎。

Repeatable read 反復讀:該級別指定了在當前事務提交之前,其它不論什么事務均不能夠改動或刪除當前事務已讀取的數(shù)據(jù)笆豁。并發(fā)性低于 READ COMMITTED郎汪。由于已讀數(shù)據(jù)的共享鎖在整個事務期間持有赤赊,而不是在每一個語句結束時釋放。這個隔離級別僅僅是說煞赢,不可以改動和刪除抛计,可是并沒有強制不能插入新的滿足條件查詢的數(shù)據(jù)行。所以會產(chǎn)生“幻讀”照筑;Mysql的默認隔離級別就是Repeatable read

Serializable 串行讀:完全串行化的讀吹截,每次讀都需要獲得表級共享鎖,讀寫相互都會阻塞

| 隔離級別 | 讀數(shù)據(jù)一致性 | 臟讀 | 不可重復讀 | 幻讀 |
| 未提交讀(Read uncommitted) | 最低級別隔離凝危,只能保證不讀取物理上損壞的數(shù)據(jù) | 是 | 是 | 是 |
| 已提交讀(Read committed) | 語句級別 | 否 | 是 | 是 |
| 可重復讀(Repeatable read) | 事務級別 | 否 | 否 | 是 |
| 可序列化(Serializable) | 最高級別波俄,事務級 | 否 | 否 | 否 |

  • 臟讀(Drity Read):某個事務已更新一份數(shù)據(jù),另一個事務在此時讀取了同一份數(shù)據(jù)蛾默,由于某些原因懦铺,前一個RollBack了操作,則后一個事務所讀取的數(shù)據(jù)就會是不正確的支鸡。
  • 不可重復讀(Non-repeatable read):在一個事務的兩次查詢之中數(shù)據(jù)不一致冬念,這可能是兩次查詢過程中間插入了一個事務更新了原有的數(shù)據(jù)。不可重復讀主要針對的是update與delete
  • 幻讀(Phantom Read):在一個事務的兩次查詢中數(shù)據(jù)筆數(shù)不一致牧挣,例如有一個事務查詢了幾列(Row)數(shù)據(jù)急前,而另一個事務卻在此時插入了新的幾列數(shù)據(jù),先前的事務在接下來的查詢中浸踩,就會發(fā)現(xiàn)有幾列數(shù)據(jù)是它先前所沒有的叔汁。幻讀主要是針對insert检碗;

1、mysql解決幻讀的方式:MVCC

在InnoDB中码邻,會在每行數(shù)據(jù)后添加兩個額外的隱藏的值來實現(xiàn)MVCC折剃,這兩個值一個記錄這行數(shù)據(jù)何時被創(chuàng)建,另外一個記錄這行數(shù)據(jù)何時過期(或者被刪除)像屋。 在實際操作中怕犁,存儲的并不是時間,而是事務的版本號己莺,每開啟一個新事務奏甫,事務的版本號就會遞增。 在可重讀Repeatable reads事務隔離級別下:

  • SELECT時凌受,讀取創(chuàng)建版本號<=當前事務版本號阵子,并且會移除版本號為空或>當前事務版本號的數(shù)據(jù)行。
  • INSERT時胜蛉,保存當前事務版本號為行的創(chuàng)建版本號
  • DELETE時挠进,保存當前事務版本號為行的刪除版本號
  • UPDATE時色乾,插入一條新紀錄,保存當前事務版本號為行創(chuàng)建版本號领突,同時保存當前事務版本號到原來刪除的行

舉例說明MVCC如何避免幻讀的:事務A讀取age<20的數(shù)據(jù)暖璧,返回5條,Mysql為其創(chuàng)建的事務版本號是10001君旦;此時事務B插入age=18的一條數(shù)據(jù)澎办,Mysql為其創(chuàng)建的事務版本號是10001;緊接著事務A再次查詢age<20的數(shù)據(jù)金砍,返回依然是5條局蚀,也就是事務B新插入的數(shù)據(jù)對于事務A來說是隔離的。

由此我們發(fā)現(xiàn)在RR級別中捞魁,通過MVCC機制至会,雖然讓數(shù)據(jù)變得可重復讀,并且避免的幻讀谱俭,但我們讀到的數(shù)據(jù)可能是歷史數(shù)據(jù)奉件,是不及時的數(shù)據(jù),不是數(shù)據(jù)庫當前的數(shù)據(jù)昆著!這在一些對于數(shù)據(jù)的時效特別敏感的業(yè)務中县貌,就很可能出問題。對于這種讀取歷史數(shù)據(jù)的方式凑懂,我們叫它快照讀 (snapshot read)煤痕,而讀取數(shù)據(jù)庫當前版本數(shù)據(jù)的方式,叫當前讀 (current read)接谨。很顯然摆碉,在MVCC中是采取的快照讀;如果要實現(xiàn)當前讀就需要使用鎖機制脓豪。

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末巷帝,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子扫夜,更是在濱河造成了極大的恐慌楞泼,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,277評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件笤闯,死亡現(xiàn)場離奇詭異堕阔,居然都是意外死亡,警方通過查閱死者的電腦和手機颗味,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,689評論 3 393
  • 文/潘曉璐 我一進店門超陆,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人脱衙,你說我怎么就攤上這事侥猬±裕” “怎么了?”我有些...
    開封第一講書人閱讀 163,624評論 0 353
  • 文/不壞的土叔 我叫張陵退唠,是天一觀的道長鹃锈。 經(jīng)常有香客問我,道長瞧预,這世上最難降的妖魔是什么屎债? 我笑而不...
    開封第一講書人閱讀 58,356評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮垢油,結果婚禮上盆驹,老公的妹妹穿的比我還像新娘。我一直安慰自己滩愁,他們只是感情好躯喇,可當我...
    茶點故事閱讀 67,402評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著硝枉,像睡著了一般廉丽。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上妻味,一...
    開封第一講書人閱讀 51,292評論 1 301
  • 那天正压,我揣著相機與錄音,去河邊找鬼责球。 笑死焦履,一個胖子當著我的面吹牛,可吹牛的內容都是我干的雏逾。 我是一名探鬼主播嘉裤,決...
    沈念sama閱讀 40,135評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼栖博!你這毒婦竟也來了择克?” 一聲冷哼從身側響起几迄,我...
    開封第一講書人閱讀 38,992評論 0 275
  • 序言:老撾萬榮一對情侶失蹤可训,失蹤者是張志新(化名)和其女友劉穎莺治,沒想到半個月后兄旬,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體梯找,經(jīng)...
    沈念sama閱讀 45,429評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡倦炒,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,636評論 3 334
  • 正文 我和宋清朗相戀三年庸队,在試婚紗的時候發(fā)現(xiàn)自己被綠了获枝。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片蠢正。...
    茶點故事閱讀 39,785評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖省店,靈堂內的尸體忽然破棺而出嚣崭,到底是詐尸還是另有隱情笨触,我是刑警寧澤,帶...
    沈念sama閱讀 35,492評論 5 345
  • 正文 年R本政府宣布雹舀,位于F島的核電站芦劣,受9級特大地震影響,放射性物質發(fā)生泄漏说榆。R本人自食惡果不足惜虚吟,卻給世界環(huán)境...
    茶點故事閱讀 41,092評論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望签财。 院中可真熱鬧串慰,春花似錦、人聲如沸唱蒸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,723評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽神汹。三九已至庆捺,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間慎冤,已是汗流浹背疼燥。 一陣腳步聲響...
    開封第一講書人閱讀 32,858評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留蚁堤,地道東北人醉者。 一個月前我還...
    沈念sama閱讀 47,891評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像披诗,于是被迫代替她去往敵國和親撬即。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,713評論 2 354

推薦閱讀更多精彩內容